Existe-t-il des algorithmes sous-quadratiques connus pour calculer le plancher de la racine carrée d'un n
entier binaire?
L'algorithme naïf serait quelque chose comme
def sqrt(x):
r = 0
i = x.bit_length() // 2
while i >= 0:
inc = (r << (i+1)) + (1 << (i*2))
if inc <= x:
x -= inc
r += 1 << i
i -= 1
return r
Cela prend des O(n)
itérations, chacune impliquant des ajouts qui sont du O(n)
temps, il est donc O(n^2)
temps dans l'ensemble. Y a-t-il quelque chose de plus rapide? Je sais que dans le cas de la multiplication, il existe des algorithmes spéciaux qui font mieux que le temps quadratique, mais je ne trouve rien pour les racines carrées.
algorithms
numerical-algorithms
Antimoine
la source
la source
Réponses:
Vous pouvez utiliser la méthode de Newton ou n'importe laquelle d'un certain nombre d'autres méthodes pour trouver des approximations aux racines du polynôme .p ( x ) = x2- c
Le taux de convergence pour la méthode de Newton sera quadratique, ce qui signifie que le nombre de bits corrects double à chaque itération. Cela signifie que itérations de la méthode de Newton suffisent.O ( lgn )
Chaque itération de la méthode de Newton calcule
La complexité en bits de la multiplication est , pour multiplier deux entiers bits (en ignorant les facteurs ). La complexité des bits pour la division (en bits de précision) est la même. Par conséquent, chaque itération peut être calculée dans les opérations . En multipliant par itérations, nous constatons que le temps d'exécution global pour calculer la racine carrée à bits de précision est . C'est sub-quadratique.blglgbb O (nlgn)O(lgn)n O (n(lgn)2)O ( b lgb ) b lglgb b O ( n lgn ) O ( lgn ) n O ( n ( lgn )2)
Je pense qu'une analyse plus minutieuse montre que cela peut être amélioré pour le temps d'exécution (en tenant compte du fait que nous avons seulement besoin de connaître chaque à environ bits de précision, plutôt que bits de précision). Cependant, même l'analyse la plus élémentaire montre déjà une durée de fonctionnement clairement sous-quadratique.xjjnO ( n lgn ) Xj j n
la source
L'un des problèmes de la méthode de Newton est qu'elle nécessite une opération de division à chaque itération, qui est l'opération de base la plus lente.
Cependant, la méthode de Newton pour la racine carrée réciproque ne le fait pas. Si est le nombre pour lequel vous voulez trouver 1x , itérer:1x√
Cela s'exprime souvent comme suit:
d i = 1 - w i x r i + 1 = r i + r i d i
C'est trois opérations de multiplication. La division par deux peut être implémentée comme un décalage vers la droite.
Maintenant, le problème est que n'est pas un entier. Cependant, vous pouvez le manipuler en tant que tel en implémentant manuellement la virgule flottante et en effectuant un tas d'opérations de décalage pour compenser le cas échéant.r
Tout d'abord, redimensionnons :x
où nous aimerions que soit supérieur, mais proche de . Si nous exécutons l'algorithme ci-dessus sur au lieu de , nous trouvons . Ensuite, .x′ 1 x′ x r=1x√′ x−−√=2erx′
Maintenant, divisons en une mantisse et un exposant:r
où est un entier. Intuitivement, représente la précision de la réponse.r′i ei
Nous savons que la méthode de Newton double à peu près le nombre de chiffres significatifs précis. Nous pouvons donc choisir:
Avec un peu de manipulation, on retrouve:
A chaque itération:
À titre d'exemple, essayons de calculer la racine carrée de . Nous savons que la réponse est . La racine carrée réciproque est , nous allons donc définir (c'est l'échelle du problème) et pour notre première estimation, nous choisirons et . (Autrement dit, nous choisissons pour notre estimation initiale à .)x=263 2312–√ 12√2−31 e=31 r′0=3 e0=2 34 12√
Alors:
Nous pouvons déterminer quand arrêter l'itération en comparant à ; si j'ai calculé correctement, devrait être suffisant. Nous nous arrêterons ici, cependant, et trouverons:ei e ei>2e
La racine carrée entière correcte est , nous sommes donc assez proches. Nous pourrions faire une autre itération, ou faire une itération finale optimisée qui ne double pas . Les détails sont laissés en exercice.3037000499 ei
Pour analyser la complexité de cette méthode, notez que la multiplication de deux entiers bits nécessite des opérations . Cependant, nous avons arrangé les choses pour que . Ainsi, la multiplication pour calculer multiplie deux nombres bits pour produire un nombre bits, et les deux autres multiplications multiplient deux nombres bits pour produire un -bit nombre.b O(blogb) r′i<2ei wi ei ei+1 ei+1 2ei+1
Dans chaque cas, le nombre d'opérations par itération est , et il y a itérations requises. La multiplication finale est de l'ordre des opérations . La complexité globale est donc les opérations , qui est sub-quadratique en nombre de bits en . Cela coche toutes les cases.O ( log e ) O ( 2 e log 2 e ) O ( e log 2 e ) xO(eilogei) O(loge) O(2elog2e) O(elog2e) x
Cependant, cette analyse cache un principe important que tous ceux qui travaillent avec de grands entiers doivent garder à l'esprit: parce que la multiplication est superlinéaire en nombre de bits, toutes les opérations de multiplication ne doivent être effectuées que sur des entiers qui ont à peu près l'ampleur de la précision actuelle (et , Je pourrais ajouter, vous devriez essayer de multiplier les nombres qui ont un ordre de grandeur similaire). L'utilisation d'entiers plus grands que cela est une perte d'effort. Les facteurs constants comptent, et pour les grands nombres entiers, ils importent beaucoup.
Enfin, deux des multiplications sont de la forme . De toute évidence, il est inutile de calculer tous les bits de uniquement pour en jeter avec un décalage à droite. La mise en œuvre d'une méthode de multiplication intelligente qui en tient compte est également laissée en exercice. abcab2c ab c
la source