Um dos problemas com o método de Newton é que ele requer uma operação de divisão em cada iteração, que é a operação inteira básica mais lenta.
O método de Newton para a raiz quadrada recíproca , no entanto, não. Se é o número para o qual você deseja encontrar 1x , itere:1x√
ri+1=12ri(3−xr2i)
Isso geralmente é expresso como:
d i = 1 - w i x r i + 1 = r i + r i d i
wi=r2i
di=1−wix
ri+1=ri+ridi2
São três operações de multiplicação. A divisão por dois pode ser implementada como shift-right.
Agora, o problema é que não é um número inteiro. No entanto, você pode manipulá-lo dessa maneira implementando o ponto flutuante manualmente e executando várias operações de turno para compensar quando apropriado.r
Primeiro, vamos redimensionar :x
x′=2−2ex
onde gostaríamos que fosse maior que, mas próximo a 1 . Se executarmos o algoritmo acima em x ′ em vez de x , encontraremos r =x′1x′x. Então,r=1x√′x−−√=2erx′ .
Agora vamos dividir r em uma mantissa e expoente:
ri=2−eir′i
onde é um número inteiro. Intuitivamente, um e i representam a precisão da resposta.r′iei
Sabemos que o método de Newton praticamente dobra o número de dígitos significativos precisos. Para que possamos escolher:
ei+1=2ei
Com um pouco de manipulação, encontramos:
w i = r ′ i 2 x ′ i = x
ei+1=2ei
wi=r′i2
di=2ei+1-w ′ i x ′ ix′i=x22e−ei+1
di=2ei+1−w′ix′i2ei+1
r′i+1=2eir′i−r′idi2ei+1
A cada iteração:
x−−√≈r′ix2e+ei
As an example, let's try calculating the square root of x=263. We happen to know that the answer is 2312–√. The reciprocal square root is 12√2−31, so we'll set e=31 (this is the scale of the problem) and for our initial guess we'll pick r′0=3 and e0=2. (That is, we pick 34 for our initial estimate to 12√.)
Then:
e1=4,r′1=11
e2=8,r′2=180
e3=16,r′3=46338
e4=32,r′4=3037000481
We can work out when to stop iterating by comparing ei to e; if I've calculated correctly, ei>2e should be good enough. We'll stop here, though, and find:
263−−−√≈3037000481×263231+32=3037000481
The correct integer square root is 3037000499, so we're pretty close. We could do another iteration, or do an optimised final iteration which doesn't double ei. The details are left as an exercise.
To analyse the complexity of this method, note that multiplying two b-bit integers takes O(blogb) operations. However, we have arranged things so that r′i<2ei. So the multiplication to calculate wi multiplies two ei-bit numbers to produce a ei+1-bit number, and the other two multiplications multiply two ei+1-bit numbers to produce a 2ei+1-bit number.
In each case, the number of operations per iteration is O(eilogei), and there are O(loge) iterations required. The final multiplication is on the order of O(2elog2e) operations. So the overall complexity is O(elog2e) operations, which is sub-quadratic in the number of bits in x. That ticks all the boxes.
However, this analysis hides an important principle which everyone working with large integers should keep in mind: because multiplication is superlinear in the number of bits, any multiplication operations should only be performed on integers which have the roughly the magnitude of the current precision (and, I might add, you should try to multiply numbers together which have a similar order of magnitude). Using integers larger than that is a waste of effort. Constant factors matter, and for large integers, they matter a lot.
As a final observation, two of the multiplications are of the form ab2c. Clearly it's wasteful to compute the all the bits of ab only to throw c of them away with a right-shift. Implementing a smart multiplication method which takes this into account is also left as an exercise.