Qui si raccolgono alcune osservazioni al fine di giungere ad una soluzione:
- In standard C> = 1999, è garantito che gli interi non netti abbiano una rappresentazione in bit come ci si aspetterebbe per qualsiasi numero di base-2.
----> Quindi, possiamo fidarci della manipolazione di bit di questo tipo di numeri.
- Se
x
è un tipo intero senza segno, immettere x >> 1 == x/2
e x << 1 == x * 2
.
(!) Ma: è molto probabile che le operazioni di bit siano eseguite più velocemente delle loro controparti aritmetiche.
sqrt(x)
equivale matematicamente a exp(log(x)/2.0)
.
- Se consideriamo logaritmi troncati e base-2 esponenziale per i numeri interi, abbiamo potuto ottenere una stima equa:
IntExp2(IntLog2(x)/2) "==" IntSqrtDn(x)
, dove "="
è notazione informale che significa quasi equatl a (nel senso di una buona approssimazione).
- Se scriviamo
IntExp2(IntLog2(x)/2 + 1) "==" IntSqrtUp(x)
, otteniamo un'approssimazione "sopra" per la radice quadrata intera.
- Le approssimazioni ottenute in (4.) e (5.) sono un po 'approssimative (racchiudono il vero valore di sqrt (x) tra due potenze consecutive di 2), ma potrebbero essere un ottimo punto di partenza per qualsiasi algoritmo che cerca il quadrato roor di
x
.
- L'algoritmo Newton per la radice quadrata potrebbe funzionare bene per gli interi, se abbiamo una buona prima approssimazione alla soluzione reale.
http://en.wikipedia.org/wiki/Integer_square_root
L'algoritmo finale ha bisogno di alcuni comprobations matematiche per essere un sacco sicuri che funziona sempre correttamente, ma non voglio farlo adesso ... vi mostrerò il programma definitivo, invece:
#include <stdio.h> /* For printf()... */
#include <stdint.h> /* For uintmax_t... */
#include <math.h> /* For sqrt() .... */
int IntLog2(uintmax_t n) {
if (n == 0) return -1; /* Error */
int L;
for (L = 0; n >>= 1; L++)
;
return L; /* It takes < 64 steps for long long */
}
uintmax_t IntExp2(int n) {
if (n < 0)
return 0; /* Error */
uintmax_t E;
for (E = 1; n-- > 0; E <<= 1)
;
return E; /* It takes < 64 steps for long long */
}
uintmax_t IntSqrtDn(uintmax_t n) { return IntExp2(IntLog2(n)/2); }
uintmax_t IntSqrtUp(uintmax_t n) { return IntExp2(IntLog2(n)/2 + 1); }
int main(void) {
uintmax_t N = 947612934; /* Try here your number! */
uintmax_t sqrtn = IntSqrtDn(N), /* 1st approx. to sqrt(N) by below */
sqrtn0 = IntSqrtUp(N); /* 1st approx. to sqrt(N) by above */
/* The following means while(abs(sqrt-sqrt0) > 1) { stuff... } */
/* However, we take care of subtractions on unsigned arithmetic, just in case... */
while ((sqrtn > sqrtn0 + 1) || (sqrtn0 > sqrtn+1))
sqrtn0 = sqrtn, sqrtn = (sqrtn0 + N/sqrtn0)/2; /* Newton iteration */
printf("N==%llu, sqrt(N)==%g, IntSqrtDn(N)==%llu, IntSqrtUp(N)==%llu, sqrtn==%llu, sqrtn*sqrtn==%llu\n\n",
N, sqrt(N), IntSqrtDn(N), IntSqrtUp(N), sqrtn, sqrtn*sqrtn);
return 0;
}
L'ultimo valore memorizzato in sqrtn
è la radice quadrata intera di N
.
L'ultima riga del programma mostra solo tutti i valori, con scopi di comprobazione.
Quindi, puoi provare diversi valori di N
e vedere cosa succede.
Se aggiungiamo un contatore all'interno del ciclo while, vedremo che non si verificano più di alcune iterazioni.
Nota: È necessario verificare che la condizione abs(sqrtn-sqrtn0)<=1
sia sempre raggiunta quando si lavora con l'impostazione del numero intero. In caso contrario, dovremo risolvere l'algoritmo.
Nota2: Nelle frasi di inizializzazione, osservare che sqrtn0 == sqrtn * 2 == sqrtn << 1
. Questo ci evita alcuni calcoli.
https://en.wikipedia.org/wiki/Methods_of_comquiry_square_roots Calcolare direttamente la radice quadrata a 64 bit interi sarà probabilmente più veloce rispetto alla conversione float. –
Vuoi la radice quadrata troncata ad un intero o arrotondata al numero intero più vicino? –
@EricPostpischil per lo scopo che ho in mente averlo troncato è preferibile. –