Existem algoritmos subquadráticos conhecidos para calcular o piso da raiz quadrada de um n
número inteiro de bit?
O algoritmo ingênuo seria algo como
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
Isso requer O(n)
iterações, cada uma envolvendo adições que são de O(n)
tempo, portanto é a O(n^2)
hora geral. Existe algo mais rápido? Eu sei que, no caso da multiplicação, existem algoritmos especiais que se saem melhor que o tempo quadrático, mas não consigo encontrar nada para raízes quadradas.
algorithms
numerical-algorithms
Antimônio
fonte
fonte
Respostas:
Você pode usar o método de Newton ou qualquer outro método para encontrar aproximações às raízes do polinômio .p ( x ) = x2- c
A taxa de convergência para o método de Newton será quadrática, o que significa que o número de bits corretos dobra em cada iteração. Isso significa que as iterações do método de Newton são suficientes.O ( lgn )
Cada iteração do método de Newton calcula
A complexidade de bits da multiplicação é O ( b lg b ) , para multiplicar dois números inteiros de b bits (ignorando os fatores lg lg b ). A complexidade de bits para divisão (para b bits de precisão) é a mesma. Portanto, cada iteração pode ser calculada em operações O ( n lg n ) . Multiplicando por O ( lg n ) iterações, descobrimos que o tempo de execução geral para calcular a raiz quadrada em n bits de precisão é O ( n ( lg nO (blgb) b lglgb b O (nlgn) O(lgn) n . Isso é sub-quadrático.O (n(lgn)2)
Penso que uma análise mais cuidadosa mostra que isso pode ser aprimorado para O ( n lg n ) tempo de execução (levando em consideração que precisamos apenas conhecer cada x j dentro de j bits de precisão, em vez de n bits de precisão) . No entanto, mesmo a análise mais básica já mostra um tempo de execução claramente subquadrático.O (nlgn) xj j n
fonte
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√
Isso geralmente é expresso como:
d i = 1 - w i x r i + 1 = r i + r i d i
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
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′ 1 x′ x . Então,r=1x√′ x−−√=2erx′ .
Agora vamos dividirr em uma mantissa e expoente:
onde é um número inteiro. Intuitivamente, um e i representam a precisão da resposta.r′i ei
Sabemos que o método de Newton praticamente dobra o número de dígitos significativos precisos. Para que possamos escolher:
Com um pouco de manipulação, encontramos:
w i = r ′ i 2 x ′ i = x
A cada iteração:
As an example, let's try calculating the square root ofx=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:
We can work out when to stop iterating by comparingei to e ; if I've calculated correctly, ei>2e should be good enough. We'll stop here, though, and find:
The correct integer square root is3037000499 , 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 twob -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 isO(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 formab2c . 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.
fonte