Comment la fonction racine carrée est-elle implémentée?
Source ici .
Enoncé du problème: étant donné x> 0, trouvez y tel que y ^ 2 = x => y = x/y (c’est l’étape clé).
double test(double x, double g) {
if closeEnough(x/g, g)
return g;
else
return test(x, betterGuess(x, g));
}
boolean closeEnough(double a, double b) {
return (Math.abs(a - b) < .001);
}
double betterGuess(double x, double g) {
return ((g + x/g) / 2);
}
sqrt(2) | Guess g x / g | New guess, (g + x / g) / 2
----------------|------------------------------|-------------------------------
test(2, 1) | 1 2 / 1 = 2 | (2 + 1) / 2 = 1.5
test(2, 1.5) | 1.5 2 / 1.5 = 1.3333 | (1.3333 + 1.5) / 2 = 1.4167
test(2, 1.4167) | 1.4167 2 / 1.4167 = 1.4118 | (1.4167 + 1.4118) / 2 = 1.4142
test(2, 1.4142) | 1.4142 ... | ...
Mise en oeuvre simple avec Recherche binaire avec C++
double root(double n){
double lo = 0, hi = n, mid;
for(int i = 0 ; i < 1000 ; i++){
mid = (lo+hi)/2;
if(mid*mid == n) return mid;
if(mid*mid > n) hi = mid;
else lo = mid;
}
return mid;
}
Notez que la boucle while
est plus courante avec la recherche binaire mais personnellement, je préfère utiliser for
pour traiter les nombres décimaux, elle enregistre certaines manipulations de cas spéciaux et obtient un résultat assez précis de petites boucles telles que 1000
ou même 500
pour presque tous les chiffres mais juste pour être en sécurité).
Edit: consultez cet article Article Wikipedia pour différentes méthodes à des fins spécifiques, spécialisées dans le calcul de la racine carrée.
Sur le matériel Intel, il est souvent implémenté en plus de l'instruction SQRT du matériel. Certaines bibliothèques n'utilisent que le résultat de cette opération, d'autres peuvent le soumettre à quelques optimisations de Newton pour le rendre plus précis dans les angles.
Le FDLIBM (LIBEL librement distribuable) a une version assez bien documentée de sqrt. e_sqrt.c .
Ils ont une version qui utilise l’arithmétique entière et une formule de récurrence qui modifie un bit à la fois.
Une autre méthode utilise la méthode de Newton. Il commence avec de la magie noire et une table de recherche pour obtenir les 8 premiers bits, puis applique la formule de récurrence
y_{i+1} = 1/2 * ( y_i + x / y_i)
où x est le nombre que nous avons commencé. C'est la méthode babylonienne de la méthode de Heron. Il remonte à Hero of Alexandra au premier centile de notre ère.
Il existe une autre méthode appelée racine carrée inverse rapide ou réciproque. qui utilise un "piratage de niveau en bits à virgule flottante" pour trouver la valeur de 1/sqrt (x) i = 0x5f3759df - ( i >> 1 );
Il exploite la représentation binaire d'un float en utilisant la mantisse et l'exposant. Si notre nombre x est (1 + m) * 2 ^ e, où m est la mantisse et e l'exposant et le résultat y = 1/sqrt (x) = (1 + n) * 2 ^ f. Prendre des journaux
lg(y) = - 1/2 lg(x)
f + lg(1+n) = -1/2 e - 1/2 lg(1+m)
Nous voyons donc que la partie exposant du résultat est -1/2 l'exposant du nombre. La magie noire effectue fondamentalement un décalage au niveau du bit sur l'exposant et utilise une approximation linéaire sur la mantisse.
Une fois que vous avez une bonne première approximation, vous pouvez utiliser les méthodes de Newton pour obtenir un meilleur résultat et, enfin, un peu de travail au niveau du bit pour corriger le dernier chiffre.
Ceci est une implémentation de l'algorithme de Newton, voir https://tour.golang.org/flowcontrol/8 .
func Sqrt(x float64) float64 {
// let initial guess to be 1
z := 1.0
for i := 1; i <= 10; i++ {
z -= (z*z - x) / (2*z) // MAGIC LINE!!
fmt.Println(z)
}
return z
}
Ce qui suit est une explication mathématique de la ligne magique. Supposons que vous souhaitiez trouver la racine du polynôme $ f (x) = x ^ 2 - a $. Selon la méthode de Newton, vous pourriez commencer par une hypothèse initiale $ x_0 = 1 $. La prochaine estimation est $ x_1 = x_0 - f (x_0)/f '(x_0) $, où $ f' (x) = 2x $. Par conséquent, votre nouvelle estimation est
$ x_1 = x_0 - (x_0 ^ 2 - a)/2x_0 $
sqrt (); fonction Dans les coulisses.
Il vérifie toujours les points médians dans un graphique. Exemple: sqrt (16) = 4; sqrt (4) = 2;
Maintenant, si vous donnez une entrée dans 16 ou 4 comme sqrt (10) ==?
Il trouve le point médian de 2 et 4, c'est-à-dire = x, puis il trouve à nouveau le point médian de x et 4 (il exclut la limite inférieure dans cette entrée). Il répète cette étape encore et encore jusqu'à l'obtention de la réponse parfaite, c'est-à-dire sqrt (10) == 3.16227766017.
Implémentation en Python: Le plancher de la valeur racine est la sortie de cette fonction . Exemple: la racine carrée de 8 est 2.82842 ..., cette fonction donnera le résultat '2'.
def mySqrt(x):
# return int(math.sqrt(x))
if x==0 or x==1:
return x
else:
start = 0
end = x
while (start <= end):
mid = int((start + end) / 2)
if (mid*mid == x):
return mid
Elif (mid*mid < x):
start = mid + 1
ans = mid
else:
end = mid - 1
return ans
Donc, juste au cas où il n'y a pas de spécification sur le fait de ne pas utiliser la fonction ceil ou round intégrée, voici une approche récursive en Java pour trouver la racine carrée d'un nombre non signé en utilisant la méthode de Newton-Raphson.
public class FindSquareRoot {
private static double newtonRaphson(double N, double X, double oldX) {
if(N <= 0) return 0;
if (Math.round(X) == Math.ceil(oldX))
return X;
return newtonRaphson(N, X - ((X * X) - N)/(2 * X), X);
}
//Driver method
public static void main (String[] args) {
System.out.println("Square root of 48.8: " + newtonRaphson(48.8, 10, 0));
}
}
il y a quelque chose qui s'appelle la méthode babylonienne.
static float squareRoot(float n)
{
/*We are using n itself as
initial approximation This
can definitely be improved */
float x = n;
float y = 1;
// e decides the accuracy level
double e = 0.000001;
while(x - y > e)
{
x = (x + y)/2;
y = n/x;
}
return x;
}
pour plus d'informations lien: https://www.geeksforgeeks.org/square-root-of-a-perfect-square/
Pour calculer la racine carrée (sans utiliser la fonction math.sqrt intégrée):
SquareRootFunction.Java
public class SquareRootFunction {
public double squareRoot(double value,int decimalPoints)
{
int firstPart=0;
/*calculating the integer part*/
while(square(firstPart)<value)
{
firstPart++;
}
if(square(firstPart)==value)
return firstPart;
firstPart--;
/*calculating the decimal values*/
double precisionVal=0.1;
double[] decimalValues=new double[decimalPoints];
double secondPart=0;
for(int i=0;i<decimalPoints;i++)
{
while(square(firstPart+secondPart+decimalValues[i])<value)
{
decimalValues[i]+=precisionVal;
}
if(square(firstPart+secondPart+decimalValues[i])==value)
{
return (firstPart+secondPart+decimalValues[i]);
}
decimalValues[i]-=precisionVal;
secondPart+=decimalValues[i];
precisionVal*=0.1;
}
return(firstPart+secondPart);
}
public double square(double val)
{
return val*val;
}
}
MainApp.Java
import Java.util.Scanner;
public class MainApp {
public static void main(String[] args) {
double number;
double result;
int decimalPoints;
Scanner in = new Scanner(System.in);
SquareRootFunction sqrt=new SquareRootFunction();
System.out.println("Enter the number\n");
number=in.nextFloat();
System.out.println("Enter the decimal points\n");
decimalPoints=in.nextInt();
result=sqrt.squareRoot(number,decimalPoints);
System.out.println("The square root value is "+ result);
in.close();
}
}
long long int floorSqrt(long long int x)
{
long long r = 0;
while((long)(1<<r)*(long)(1<<r) <= x){
r++;
}
r--;
long long b = r -1;
long long ans = 1 << r;
while(b >= 0){
if(((long)(ans|1<<b)*(long)(ans|1<<b))<=x){
ans |= (1<<b);
}
b--;
}
return ans;
}
Je fais une fonction aussi, 100000000 itérations prend 14 secondes, toujours rien comparé à 1 seconde par sqrt
double mysqrt(double n)
{
double x = n;
int it = 4;
if (n >= 90)
{
it = 6;
}
if (n >= 5000)
{
it = 8;
}
if (n >= 20000)
{
it = 10;
}
if (n >= 90000)
{
it = 11;
}
if (n >= 200000)
{
it = 12;
}
if (n >= 900000)
{
it = 13;
}
if (n >= 3000000)
{
it = 14;
}
if (n >= 10000000)
{
it = 15;
}
if (n >= 30000000)
{
it = 16;
}
if (n >= 100000000)
{
it = 17;
}
if (n >= 300000000)
{
it = 18;
}
if (n >= 1000000000)
{
it = 19;
}
for (int i = 0; i < it; i++)
{
x = 0.5*(x+n/x);
}
return x;
}
Mais la mise en oeuvre la plus rapide est:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
float mysqrt(float n) {return 1/Q_rsqrt(n);}
Suite à ma solution à Golang.
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
z := 1.0 // initial guess to be 1
i := 0
for int(z*z) != int(x) { // until find the first approximation
// Newton root algorithm
z -= (z*z - x) / (2 * z)
i++
}
return z
}
func main() {
fmt.Println(Sqrt(8900009870))
}
Suite à une solution classique/commune.
package main
import (
"fmt"
"math"
)
func Sqrt(num float64) float64 {
const DIFF = 0.0001 // To fix the precision
z := 1.0
for {
z1 := z - (((z * z) - num) / (2 * z))
// Return a result when the diff between the last execution
// and the current one is lass than the precision constant
if (math.Abs(z1 - z) < DIFF) {
break
}
z = z1
}
return z
}
func main() {
fmt.Println(Sqrt(94339))
}
Pour plus d'informations, consultez ici