Comment écrivez-vous votre propre fonction pour trouver la racine carrée la plus précise d'un entier?
Après l'avoir googlé, j'ai trouvé this (archivé de son lien d'origine ), mais d'abord, je ne l'ai pas tout à fait compris, et ensuite, il est aussi approximatif.
Supposons que la racine carrée est l’entier le plus proche (de la racine réelle) ou un flottant.
Le calcul de l’étage suivant (sqrt (N)) pour N> 0:
x = 2^ceil(numbits(N)/2)
loop:
y = floor((x + floor(N/x))/2)
if y >= x
return x
x = y
Ceci est une version de la méthode de Newton donnée dans Crandall & Pomerance, "Les nombres premiers: une perspective informatique". La raison pour laquelle vous devriez utiliser cette version est que les personnes qui savent ce qu’elles font ont prouvé qu’elle converge exactement vers le sol de la racine carrée. C’est simple, donc la probabilité de faire une erreur d’implémentation est faible. C'est aussi rapide (bien qu'il soit possible de construire un algorithme encore plus rapide - mais le faire correctement est beaucoup plus complexe). Une recherche binaire correctement implémentée peut être plus rapide pour un très petit N, mais vous pouvez également utiliser une table de correspondance.
Pour arrondir au la plus proche entier, calculez simplement t = floor (sqrt (4N)) en utilisant l'algorithme ci-dessus. Si le bit le moins significatif de t est défini, choisissez x = (t + 1)/2; sinon choisissez t/2. Notez que cela se termine sur une cravate; vous pouvez aussi arrondir vers le bas (ou arrondir au pair) en vérifiant si le reste est différent de zéro (c’est-à-dire si t ^ 2 == 4N).
Notez que vous n'avez pas besoin d'utiliser l'arithmétique à virgule flottante. En fait, vous ne devriez pas. Cet algorithme doit être entièrement implémenté en utilisant des entiers (en particulier, les fonctions floor () indiquent simplement que la division entière doit être utilisée).
En fonction de vos besoins, une stratégie simple de division et de conquête peut être utilisée. Il ne convergera pas aussi bien que fast que d’autres méthodes, mais il peut être beaucoup plus facile à comprendre pour un novice. De plus, comme il s’agit d’un algorithme O (log n) (divisant par deux l’espace de recherche à chaque itération), le cas le plus défavorable pour un float 32 bits sera de 32 itérations.
Disons que vous voulez la racine carrée de 62.104. Vous choisissez une valeur à mi-chemin entre 0 et cela, et la place. Si le carré est supérieur à votre nombre, vous devez vous concentrer sur des nombres inférieurs au point médian. Si c'est trop bas, concentrez-vous sur ceux qui sont plus élevés.
En mathématiques réelles, vous pouvez diviser l’espace de recherche en deux pour toujours (s’il n’a pas de racine carrée rationnelle). En réalité, les ordinateurs finiront par manquer de précision et vous aurez votre approximation. Le programme C suivant illustre ce point:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]) {
float val, low, high, mid, oldmid, midsqr;
int step = 0;
// Get argument, force to non-negative.
if (argc < 2) {
printf ("Usage: sqrt <number>\n");
return 1;
}
val = fabs (atof (argv[1]));
// Set initial bounds and print heading.
low = 0;
high = mid = val;
oldmid = -1;
printf ("%4s %10s %10s %10s %10s %10s %s\n",
"Step", "Number", "Low", "High", "Mid", "Square", "Result");
// Keep going until accurate enough.
while (fabs(oldmid - mid) >= 0.00001) {
oldmid = mid;
// Get midpoint and see if we need lower or higher.
mid = (high + low) / 2;
midsqr = mid * mid;
printf ("%4d %10.4f %10.4f %10.4f %10.4f %10.4f ",
++step, val, low, high, mid, midsqr);
if (mid * mid > val) {
high = mid;
printf ("- too high\n");
} else {
low = mid;
printf ("- too low\n");
}
}
// Desired accuracy reached, print it.
printf ("sqrt(%.4f) = %.4f\n", val, mid);
return 0;
}
Voici quelques pistes pour que vous puissiez avoir une idée de son fonctionnement. Pour 77:
pax> sqrt 77
Step Number Low High Mid Square Result
1 77.0000 0.0000 77.0000 38.5000 1482.2500 - too high
2 77.0000 0.0000 38.5000 19.2500 370.5625 - too high
3 77.0000 0.0000 19.2500 9.6250 92.6406 - too high
4 77.0000 0.0000 9.6250 4.8125 23.1602 - too low
5 77.0000 4.8125 9.6250 7.2188 52.1104 - too low
6 77.0000 7.2188 9.6250 8.4219 70.9280 - too low
7 77.0000 8.4219 9.6250 9.0234 81.4224 - too high
8 77.0000 8.4219 9.0234 8.7227 76.0847 - too low
9 77.0000 8.7227 9.0234 8.8730 78.7310 - too high
10 77.0000 8.7227 8.8730 8.7979 77.4022 - too high
11 77.0000 8.7227 8.7979 8.7603 76.7421 - too low
12 77.0000 8.7603 8.7979 8.7791 77.0718 - too high
13 77.0000 8.7603 8.7791 8.7697 76.9068 - too low
14 77.0000 8.7697 8.7791 8.7744 76.9893 - too low
15 77.0000 8.7744 8.7791 8.7767 77.0305 - too high
16 77.0000 8.7744 8.7767 8.7755 77.0099 - too high
17 77.0000 8.7744 8.7755 8.7749 76.9996 - too low
18 77.0000 8.7749 8.7755 8.7752 77.0047 - too high
19 77.0000 8.7749 8.7752 8.7751 77.0022 - too high
20 77.0000 8.7749 8.7751 8.7750 77.0009 - too high
21 77.0000 8.7749 8.7750 8.7750 77.0002 - too high
22 77.0000 8.7749 8.7750 8.7750 76.9999 - too low
23 77.0000 8.7750 8.7750 8.7750 77.0000 - too low
sqrt(77.0000) = 8.7750
Pour 62.104:
pax> sqrt 62.104
Step Number Low High Mid Square Result
1 62.1040 0.0000 62.1040 31.0520 964.2267 - too high
2 62.1040 0.0000 31.0520 15.5260 241.0567 - too high
3 62.1040 0.0000 15.5260 7.7630 60.2642 - too low
4 62.1040 7.7630 15.5260 11.6445 135.5944 - too high
5 62.1040 7.7630 11.6445 9.7037 94.1628 - too high
6 62.1040 7.7630 9.7037 8.7334 76.2718 - too high
7 62.1040 7.7630 8.7334 8.2482 68.0326 - too high
8 62.1040 7.7630 8.2482 8.0056 64.0895 - too high
9 62.1040 7.7630 8.0056 7.8843 62.1621 - too high
10 62.1040 7.7630 7.8843 7.8236 61.2095 - too low
11 62.1040 7.8236 7.8843 7.8540 61.6849 - too low
12 62.1040 7.8540 7.8843 7.8691 61.9233 - too low
13 62.1040 7.8691 7.8843 7.8767 62.0426 - too low
14 62.1040 7.8767 7.8843 7.8805 62.1024 - too low
15 62.1040 7.8805 7.8843 7.8824 62.1323 - too high
16 62.1040 7.8805 7.8824 7.8815 62.1173 - too high
17 62.1040 7.8805 7.8815 7.8810 62.1098 - too high
18 62.1040 7.8805 7.8810 7.8807 62.1061 - too high
19 62.1040 7.8805 7.8807 7.8806 62.1042 - too high
20 62.1040 7.8805 7.8806 7.8806 62.1033 - too low
21 62.1040 7.8806 7.8806 7.8806 62.1038 - too low
22 62.1040 7.8806 7.8806 7.8806 62.1040 - too high
23 62.1040 7.8806 7.8806 7.8806 62.1039 - too high
sqrt(62.1040) = 7.8806
Pour 49 personnes:
pax> sqrt 49
Step Number Low High Mid Square Result
1 49.0000 0.0000 49.0000 24.5000 600.2500 - too high
2 49.0000 0.0000 24.5000 12.2500 150.0625 - too high
3 49.0000 0.0000 12.2500 6.1250 37.5156 - too low
4 49.0000 6.1250 12.2500 9.1875 84.4102 - too high
5 49.0000 6.1250 9.1875 7.6562 58.6182 - too high
6 49.0000 6.1250 7.6562 6.8906 47.4807 - too low
7 49.0000 6.8906 7.6562 7.2734 52.9029 - too high
8 49.0000 6.8906 7.2734 7.0820 50.1552 - too high
9 49.0000 6.8906 7.0820 6.9863 48.8088 - too low
10 49.0000 6.9863 7.0820 7.0342 49.4797 - too high
11 49.0000 6.9863 7.0342 7.0103 49.1437 - too high
12 49.0000 6.9863 7.0103 6.9983 48.9761 - too low
13 49.0000 6.9983 7.0103 7.0043 49.0598 - too high
14 49.0000 6.9983 7.0043 7.0013 49.0179 - too high
15 49.0000 6.9983 7.0013 6.9998 48.9970 - too low
16 49.0000 6.9998 7.0013 7.0005 49.0075 - too high
17 49.0000 6.9998 7.0005 7.0002 49.0022 - too high
18 49.0000 6.9998 7.0002 7.0000 48.9996 - too low
19 49.0000 7.0000 7.0002 7.0001 49.0009 - too high
20 49.0000 7.0000 7.0001 7.0000 49.0003 - too high
21 49.0000 7.0000 7.0000 7.0000 49.0000 - too low
22 49.0000 7.0000 7.0000 7.0000 49.0001 - too high
23 49.0000 7.0000 7.0000 7.0000 49.0000 - too high
sqrt(49.0000) = 7.0000
Une méthode simple (mais pas très rapide) pour calculer la racine carrée de X:
squareroot(x)
if x<0 then Error
a = 1
b = x
while (abs(a-b)>ErrorMargin)
a = (a+b)/2
b = x/a
endwhile
return a;
Exemple: racine carrée (70000)
a b
1 70000
35001 2
17502 4
8753 8
4381 16
2199 32
1116 63
590 119
355 197
276 254
265 264
Comme vous pouvez le constater, il définit une limite supérieure et une limite inférieure pour la racine carrée et réduit la limite jusqu'à ce que sa taille soit acceptable.
Il existe des méthodes plus efficaces, mais celle-ci illustre le processus et est facile à comprendre.
Méfiez-vous simplement de définir la valeur 1 sur Errormargin si vous utilisez des entiers, sinon vous avez une boucle infinie.
Permettez-moi de souligner une méthode extrêmement intéressante pour calculer une racine carrée inverse 1/sqrt (x), qui est une légende du monde du game design, car elle est incroyablement rapide. Ou attendez, lisez le post suivant:
http://betterexplained.com/articles/understanding-quakes-fast-inverse-square-root/
PS: Je sais que vous voulez juste la racine carrée mais l'élégance de tremblement de terre a vaincu toute résistance de ma part :)
En passant, l'article mentionné ci-dessus parle également de l'approximation ennuyeuse de Newton-Raphson quelque part.
Bien sûr, c'est approximatif. C’est ainsi que fonctionnent les mathématiques avec des nombres à virgule flottante.
Quoi qu'il en soit, la méthode standard est avec la méthode de Newton . C'est à peu près la même chose que d'utiliser la série de Taylor, l'inverse qui me vient immédiatement à l'esprit.
#!/usr/bin/env python
import decimal
def sqrt(n):
assert n > 0
with decimal.localcontext() as ctx:
ctx.prec += 2 # increase precision to minimize round off error
x, prior = decimal.Decimal(n), None
while x != prior:
prior = x
x = (x + n/x) / 2 # quadratic convergence
return +x # round in a global context
decimal.getcontext().prec = 80 # desirable precision
r = sqrt(12345)
print r
print r == decimal.Decimal(12345).sqrt()
Sortie:
111.10805551354051124500443874307524148991137745969772997648567316178259031751676
True
J'ai trouvé un excellent article sur Integer Square Roots .
Ceci est une version légèrement améliorée qu’elle présente ici:
unsigned long sqrt(unsigned long a){
int i;
unsigned long rem = 0;
unsigned long root = 0;
for (i = 0; i < 16; i++){
root <<= 1;
rem = (rem << 2) | (a >> 30);
a <<= 2;
if(root < rem){
root++;
rem -= root;
root++;
}
}
return root >> 1;
}
C'est une question d'entretien fréquemment posée par Facebook, etc. Je ne pense pas que ce soit une bonne idée d'utiliser la méthode de Newton dans une interview. Et si l'intervieweur vous demandait le mécanisme de la méthode de Newton sans vraiment le comprendre?
J'ai fourni une solution de recherche binaire en Java que tout le monde peut comprendre.
public int sqrt(int x) {
if(x < 0) return -1;
if(x == 0 || x == 1) return x;
int lowerbound = 1;
int upperbound = x;
int root = lowerbound + (upperbound - lowerbound)/2;
while(root > x/root || root+1 <= x/(root+1)){
if(root > x/root){
upperbound = root;
} else {
lowerbound = root;
}
root = lowerbound + (upperbound - lowerbound)/2;
}
return root;
}
Vous pouvez tester mon code ici: leetcode: sqrt (x)
Voici un moyen d'obtenir une racine carrée en utilisant la trigonométrie. Ce n'est pas l'algorithme le plus rapide par un long shot, mais c'est précis. Le code est en javascript:
var n = 5; //number to get the square root of
var icr = ((n+1)/2); //intersecting circle radius
var sqrt = Math.cos(Math.asin((icr-1)/icr))*icr; //square root of n
alert(sqrt);
Il existe un algorithme que j'ai étudié à l'école et que vous pouvez utiliser pour calculer des racines carrées exactes (ou une précision arbitrairement grande si la racine est un nombre irrationnel). Il est nettement plus lent que les algorithmes de Newton mais il est exact… .. Disons que vous voulez calculer la racine carrée de 531.3025
La première chose à faire est de diviser votre nombre à partir du point décimal en groupes de 2 chiffres:
{5} {31}. {30} {25}
Ensuite:
1) Trouvez la racine carrée la plus proche du premier groupe qui soit plus petite ou égale à la racine carrée réelle du premier groupe: sqrt ({5})> = 2. Cette racine carrée est le premier chiffre de votre réponse finale. Notons les chiffres que nous avons déjà trouvés de notre dernière racine carrée sous la forme B. Donc, pour le moment, B = 2.
2) Calculez ensuite la différence entre {5} et B ^ 2: 5 - 4 = 1.
3) Pour tous les groupes de 2 chiffres suivants, procédez comme suit:
Multipliez le reste par 100, puis ajoutez-le au second groupe: 100 + 31 = 131.
Trouver X - chiffre suivant de votre racine, tel que 131> = ((B * 20) + X) * X. X = 3. 43 * 3 = 129 <131. Maintenant, B = 23. En outre, comme vous n'avez plus de groupes à 2 chiffres à gauche des points décimaux, vous avez trouvé tous les chiffres entiers de votre racine finale.
4) Répétez la même chose pour {30} et {25}. Donc vous avez:
{30}: 131 - 129 = 2. 2 * 100 + 30 = 230> = (23 * 2 * 10 + X) * X -> X = 0 -> B = 23,0
{25}: 230 - 0 = 230. 230 * 100 + 25 = 23025. 23025> = (230 * 2 * 10 + X) * X -> X = 5 -> B = 23,05
Résultat final = 23.05.
L'algorithme semble compliqué de cette façon, mais il est beaucoup plus simple de le faire sur papier en utilisant la même notation que celle utilisée pour la "division longue" que vous avez étudiée à l'école, sauf que vous ne faites pas la division mais que vous calculez le carré. racine.
La première chose qui me vient à l’esprit est la suivante: c’est un bon endroit pour utiliser la recherche binaire (inspiré par cet excellent tutoriels .)
Pour trouver la racine carrée de vaule
, nous recherchons la number
dans (1..value)
où le prédicteur.__ est vrai pour la première fois. Le prédicteur que nous choisissons est number * number - value > 0.00001
.
double square_root_of(double value)
{
assert(value >= 1);
double lo = 1.0;
double hi = value;
while( hi - lo > 0.00001)
{
double mid = lo + (hi - lo) / 2 ;
std::cout << lo << "," << hi << "," << mid << std::endl;
if( mid * mid - value > 0.00001) //this is the predictors we are using
{
hi = mid;
} else {
lo = mid;
}
}
return lo;
}
// Fastest way I found, an (extreme) C# unrolled version of:
// http://www.hackersdelight.org/hdcodetxt/isqrt.c.txt (isqrt4)
// It's quite a lot of code, basically a binary search (the "if" statements)
// followed by an unrolled loop (the labels).
// Most important: it's fast, twice as fast as "Math.Sqrt".
// On my pc: Math.Sqrt ~35 ns, sqrt <16 ns (mean <14 ns)
private static uint sqrt(uint x)
{
uint y, z;
if (x < 1u << 16)
{
if (x < 1u << 08)
{
if (x < 1u << 04) return x < 1u << 02 ? x + 3u >> 2 : x + 15u >> 3;
else
{
if (x < 1u << 06)
{ y = 1u << 03; x -= 1u << 04; if (x >= 5u << 02) { x -= 5u << 02; y |= 1u << 02; } goto L0; }
else
{ y = 1u << 05; x -= 1u << 06; if (x >= 5u << 04) { x -= 5u << 04; y |= 1u << 04; } goto L1; }
}
}
else // slower (on my pc): .... y = 3u << 04; } goto L1; }
{
if (x < 1u << 12)
{
if (x < 1u << 10)
{ y = 1u << 07; x -= 1u << 08; if (x >= 5u << 06) { x -= 5u << 06; y |= 1u << 06; } goto L2; }
else
{ y = 1u << 09; x -= 1u << 10; if (x >= 5u << 08) { x -= 5u << 08; y |= 1u << 08; } goto L3; }
}
else
{
if (x < 1u << 14)
{ y = 1u << 11; x -= 1u << 12; if (x >= 5u << 10) { x -= 5u << 10; y |= 1u << 10; } goto L4; }
else
{ y = 1u << 13; x -= 1u << 14; if (x >= 5u << 12) { x -= 5u << 12; y |= 1u << 12; } goto L5; }
}
}
}
else
{
if (x < 1u << 24)
{
if (x < 1u << 20)
{
if (x < 1u << 18)
{ y = 1u << 15; x -= 1u << 16; if (x >= 5u << 14) { x -= 5u << 14; y |= 1u << 14; } goto L6; }
else
{ y = 1u << 17; x -= 1u << 18; if (x >= 5u << 16) { x -= 5u << 16; y |= 1u << 16; } goto L7; }
}
else
{
if (x < 1u << 22)
{ y = 1u << 19; x -= 1u << 20; if (x >= 5u << 18) { x -= 5u << 18; y |= 1u << 18; } goto L8; }
else
{ y = 1u << 21; x -= 1u << 22; if (x >= 5u << 20) { x -= 5u << 20; y |= 1u << 20; } goto L9; }
}
}
else
{
if (x < 1u << 28)
{
if (x < 1u << 26)
{ y = 1u << 23; x -= 1u << 24; if (x >= 5u << 22) { x -= 5u << 22; y |= 1u << 22; } goto La; }
else
{ y = 1u << 25; x -= 1u << 26; if (x >= 5u << 24) { x -= 5u << 24; y |= 1u << 24; } goto Lb; }
}
else
{
if (x < 1u << 30)
{ y = 1u << 27; x -= 1u << 28; if (x >= 5u << 26) { x -= 5u << 26; y |= 1u << 26; } goto Lc; }
else
{ y = 1u << 29; x -= 1u << 30; if (x >= 5u << 28) { x -= 5u << 28; y |= 1u << 28; } }
}
}
}
z = y | 1u << 26; y /= 2; if (x >= z) { x -= z; y |= 1u << 26; }
Lc: z = y | 1u << 24; y /= 2; if (x >= z) { x -= z; y |= 1u << 24; }
Lb: z = y | 1u << 22; y /= 2; if (x >= z) { x -= z; y |= 1u << 22; }
La: z = y | 1u << 20; y /= 2; if (x >= z) { x -= z; y |= 1u << 20; }
L9: z = y | 1u << 18; y /= 2; if (x >= z) { x -= z; y |= 1u << 18; }
L8: z = y | 1u << 16; y /= 2; if (x >= z) { x -= z; y |= 1u << 16; }
L7: z = y | 1u << 14; y /= 2; if (x >= z) { x -= z; y |= 1u << 14; }
L6: z = y | 1u << 12; y /= 2; if (x >= z) { x -= z; y |= 1u << 12; }
L5: z = y | 1u << 10; y /= 2; if (x >= z) { x -= z; y |= 1u << 10; }
L4: z = y | 1u << 08; y /= 2; if (x >= z) { x -= z; y |= 1u << 08; }
L3: z = y | 1u << 06; y /= 2; if (x >= z) { x -= z; y |= 1u << 06; }
L2: z = y | 1u << 04; y /= 2; if (x >= z) { x -= z; y |= 1u << 04; }
L1: z = y | 1u << 02; y /= 2; if (x >= z) { x -= z; y |= 1u << 02; }
L0: return x > y ? y / 2 | 1u : y / 2;
}
utiliser la recherche binaire
public class FindSqrt {
public static void main(String[] strings) {
int num = 10000;
System.out.println(sqrt(num, 0, num));
}
private static int sqrt(int num, int min, int max) {
int middle = (min + max) / 2;
int x = middle * middle;
if (x == num) {
return middle;
} else if (x < num) {
return sqrt(num, middle, max);
} else {
return sqrt(num, min, middle);
}
}
}
L'inverse, comme son nom l'indique, mais parfois "assez proche" est "assez proche"; une lecture intéressante quand même.
En général, la racine carrée d'un entier (comme 2, par exemple) ne peut être approchée que par seulement (non à cause de problèmes d'arithmétique en virgule flottante, mais parce que ce sont des nombres irrationnels qui ne peuvent pas être calculés exactement).
Bien sûr, certaines approximations sont meilleures que d'autres. Je veux dire, bien sûr, que la valeur 1.732 est une meilleure approximation de la racine carrée de 3 que 1.7
La méthode utilisée par le code sur ce lien que vous avez donné fonctionne en prenant une première approximation et en l'utilisant pour calculer une approximation mieux.
Cela s'appelle la méthode de Newton et vous pouvez répéter le calcul avec chaque nouvelle approximation jusqu'à ce que soit suffisamment précis pour vous.
En fait, il y a un {doit} _ moyen de décider quand arrêter la répétition ou elle durera indéfiniment.
Généralement, vous vous arrêtez lorsque la différence entre les approximations est égale à inférieure à, une valeur que vous décidez.
EDIT: Je ne pense pas qu'il puisse y avoir une implémentation plus simple que les deux que vous avez déjà trouvées.
Une solution simple pouvant traiter la racine carrée flottante et la précision arbitraire à l'aide de la recherche binaire
codé en Ruby
include Math
def sqroot_precision num, precision
upper = num
lower = 0
middle = (upper + lower)/2.0
while true do
diff = middle**2 - num
return middle if diff.abs <= precision
if diff > 0
upper = middle
else diff < 0
lower = middle
end
middle = (upper + lower)/2.0
end
end
puts sqroot_precision 232.3, 0.0000000001
Eh bien, il y a déjà pas mal de réponses, mais voici la mienne C'est le morceau de code le plus simple (pour moi), voici le algorithm pour cela.
Et code en python 2.7:
from __future__ import division
val = 81
x = 10
def sqr(data,x):
temp = x - ( (x**2 - data)/(2*x))
if temp == x:
print temp
return
else:
x = temp
return sqr(data,x)
#x =temp
#sqr(data,x)
sqr(val,x)
Supposons que nous essayons de trouver la racine carrée de 2 et que vous avez une estimation de 1,5. Nous dirons a = 2 et x = 1,5. Pour calculer une meilleure estimation, nous allons diviser un par x. Cela donne une nouvelle valeur y = 1,333333. Cependant, nous ne pouvons pas prendre cela comme notre prochaine estimation (pourquoi pas?). Nous devons faire la moyenne avec l'estimation précédente. Donc, notre prochaine estimation, xx sera (x + y)/2, ou 1.416666.
Double squareRoot(Double a, Double epsilon) {
Double x = 0d;
Double y = a;
Double xx = 0d;
// Make sure both x and y != 0.
while ((x != 0d || y != 0d) && y - x > epsilon) {
xx = (x + y) / 2;
if (xx * xx >= a) {
y = xx;
} else {
x = xx;
}
}
return xx;
}
Epsilon détermine le degré de précision de l'approximation. La fonction doit renvoyer la première approximation x obtenue qui vérifie abs (x * x - a) <epsilon, où abs (x) est la valeur absolue de x.
square_root(2, 1e-6)
Output: 1.4142141342163086