平方根関数はどのように実装されますか?
ソース ここ(バビロン法) 。
問題文:x> 0が与えられた場合、y ^ 2 = x => y = x/y(これが重要なステップです)となるyを見つけます。
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 ... | ...
Intelハードウェアでは、多くの場合、ハードウェアSQRT命令の上に実装されます。一部のライブラリーはその結果をそのまま使用しますが、一部のライブラリーは、コーナーケースでより正確にするために、数回のニュートン最適化を実行する場合があります。
FDLIBM(Freely Distributable LIBM)には、sqrtの文書化されたバージョンがあります。 e_sqrt.c 。
整数演算と、一度に1ビットずつ変更する繰り返し式を使用する1つのバージョンがあります。
別の方法は、ニュートンの方法を使用します。最初の8ビットを取得するために、いくつかのブラックマジックとルックアップテーブルで開始し、繰り返し式を適用します
_ y_{i+1} = 1/2 * ( y_i + x / y_i)
_
ここで、xは開始した番号です。これは、ヘロンの方法の バビロン法 です。最初の100世紀にアレクサンドラの英雄にさかのぼります。
高速逆平方根 またはreciprootと呼ばれる別の方法があります。 「悪浮動小数点ビットレベルハッキング」を使用して、1/sqrt(x)の値を見つけます。 i = 0x5f3759df - ( i >> 1 );
マンティスと指数を使用して、floatのバイナリ表現を活用します。数値xが(1 + m)* 2 ^ eの場合、mは仮数、eは指数、結果y = 1/sqrt(x)=(1 + n)* 2 ^ fです。ログを取る
_lg(y) = - 1/2 lg(x)
f + lg(1+n) = -1/2 e - 1/2 lg(1+m)
_
したがって、結果の指数部は、数値の指数の-1/2です。ブラックマジックは基本的に指数でビット単位のシフトを行い、仮数で線形近似を使用します。
最初の近似値が得られたら、Newtonの方法を使用してより良い結果を得ることができ、最後に最後の桁を修正するためのビットレベルの作業ができます。
これは、ニュートンのアルゴリズムの実装です。 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
}
以下は、魔法のラインの数学的な説明です。多項式$ f(x)= x ^ 2-a $の根を見つけたいとします。ニュートンの方法では、最初の推測$ x_0 = 1 $から始めることができます。次の推測は$ x_1 = x_0-f(x_0)/ f '(x_0)$です。ここで、$ f'(x)= 2x $です。したがって、新しい推測は
$ x_1 = x_0-(x_0 ^ 2-a)/ 2x_0 $
sqrt();舞台裏の機能。
常にグラフの中間点をチェックします。例:sqrt(16)= 4; sqrt(4)= 2;
Sqrt(10)==?
2と4の中間点を見つけます。つまり、x = 4です。次に、xと4の中間点を見つけます(この入力の下限を除外します)。完璧な答え、つまりsqrt(10)== 3.16227766017が得られるまで、このステップを何度も繰り返します。b/ w 2および4にあります。この組み込み関数はすべて、計算、微分、統合を使用して作成されます。
Pythonでの実装:ルート値の下限は、この関数の出力です。例:8の平方根は2.82842 ...です。この関数は出力「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
したがって、組み込みの関数またはラウンド関数を使用するかどうかに関する仕様がない場合に備えて、Javaを使用して符号なし数値の平方根を見つける再帰的アプローチニュートンラプソン法。
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));
}
}
バビロニア法と呼ばれるものがあります。
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;
}
詳細リンク: https://www.geeksforgeeks.org/square-root-of-a-perfect-square/
平方根を計算するには(組み込みのmath.sqrt関数を使用せずに):
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;
}
私もsqrt関数を作成しています、100000000回の反復には14秒かかりますが、sqrtによる1秒に比べてまだ何もありません
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;
}
しかし、最速の実装は次のとおりです。
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);}
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))
}
クラシック/一般的なソリューションに従います。
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))
}
詳細については here を確認してください