1

ウィキペディアの情報に基づいて、数値の平方根を取得するためのバビロニア/ヘロンのメソッドをJavaで実装しています。

現在私は持っています:

public static void main(String[] args) {
    System.out.println(Sqrt.sqrt1(8));
    //System.out.println(Sqrt.sqrt2(8));   //Infinite loop
    System.out.println(Sqrt.sqrt3(8));
    System.out.println(Sqrt.sqrt4(8));
}

static float sqrt1(float x) {
    float b = 0, h = x;

    while (b != h) {
        b = (h + b) / 2;
        h = x / b;
    }
    return b;
}

static double sqrt2(double x) {
    double b = x, h = 0;

    while (b != h) {
        b = (h + b) / 2;
        h = x / b;
    }
    return b;
}

static double sqrt3(double x) {
    double b = x, h = 0;

    while (Math.abs(b - h) > 0.000000000001) {
        b = (h + b) / 2;
        h = x / b;
    }
    return b;
}

static double sqrt4(double x) {
    double r = x, t = 0;

    while (t != r) {
        t = r;
        r = (x / r + r) / 2;
    }
    return r;
}

出力は次のようになります。

2.828427

2.82842712474619

2.82842712474619

sqrt2メソッドは永久ループになりますが、sqrt1メソッドはfloatで正常に機能するため、これはdoubleでのみ発生します。なぜこれなのかわかりません。したがって、sqrt3メソッドは、doubleを使用する場合の方法のように見えます。

どのメソッドを実装するかについて少し混乱しています。¿バビロニア法は開平法と同じですか。

私が理解していることから、バビロニア法は、正方形の辺が正方形の面積(x)の平方根であるという事実に基づいています。したがって、bh寸法の長方形から始めて、2つの辺の平均(b = b + h / 2)を取得し、この結果を小さい長方形の辺と見なして、もちろん反対側(h = x)を取得できます。 / b)。長方形は、目的の正方形に近づき始めます。これは私がsqrt1、sqrt2およびsqrt3メソッドで行ったことです:

while (b != h) {
    b = (h + b) / 2;
    h = x / b;
}

反対側では、ウィキペディアのリンクは、バビロニア/開平法が同じであると述べており、次のように説明しています。

「基本的な考え方は、xが非負実数Sの平方根に対して過大評価されている場合、S / xは過小評価されるため、これら2つの数の平均がより良い近似を提供すると合理的に期待できるということです。」

この実装はsqrt4メソッドで確認できます。

    while (t != r) {  
        t = r;  
        r = (x/r + r) / 2;  
    }  

私が見ることができることから、この2つの方法は同じではありませんが、似ています。私が間違っている場合は私を訂正してください。

興味深いのは、私ができないことですwhile (b != h) {。bとhが、sqrt2メソッドで示されているようにdoubleである場合、無限ループになるためです。代わりに私は使用しましたwhile (Math.abs(b - h) > 0.000000000001) {

しかし、私はできます:while (t != r) {sqrt4に示されているようにbとhが2倍になります。

誰かがこの振る舞いを説明してくれれば幸いです。

- - - - - -編集: - - - - - - - - - - - - - - - - - - - -----------------------

提案されているように、両方のアルゴリズムは同じですが、実装が異なります。ただし、次の推奨コードは、x =8のsqrt2と同じようにループします。

static double sqrt5(double x) {
    double b = x;

    while (b != x/b) {
        b = (x / b + b) / 2;
    }
    return b;
}

それで..他の実装とのsqrt4の違いは何ですか?なぜそれは他の人のように永遠にループしないのですか?

4

3 に答える 3

5

更新: 停止条件が1つの反復からの近似をその前の反復からの近似と比較するためsqrt4よりも、最終的にループを終了する可能性が高くなります。sqrt5

計算によってエラーが減少する傾向があるため、最終的に計算される値は、最後のビットのみとは異なるb正確な平方根に非常に近くなります。この時点で、使用可能な有限精度を使用するために計算された値はに等しくなり、反復は停止します。x/bb(x / b + b) / 2b

たとえば、sqrt(2)を計算していて、近似値b = 1.414213562373095に達した場合、次のようになります。

>>> b
1.414213562373095
>>> 2/b                     # Close but not quite equal to b, 
1.4142135623730951          # iteration in sqrt5 continues
>>> (2/b + b)/2        
1.414213562373095           # Exactly equal to b, sqrt4 stops

ご覧のとおり、bが1.414213562373095に達すると、ループ内の計算によってその値が変更されることはありません。bと2/bはまだ最後の桁sqrt5が異なるため、終了することはありません。


バビロニア法とヘロン法は同じアルゴリズムであり、x²=aを解くためのニュートンラプソン法と一致します。さまざまな実装の違いは、停止条件です。例えば:

while (b != h) {
    b = (h + b) / 2;
    h = x / b;
}

と同じです:

while (b != x/b) {
    b = (x / b + b) / 2;
}

もちろん、 xの数学的に正確な平方根になることは決してないb != x/bため、これはあまり良い停止条件でbはありません。bが正確な平方根でない場合、x/bはbと等しくありません。たとえば、Pythonの場合:

>>> sqrt(2)
1.4142135623730951
>>> 2/sqrt(2)
1.4142135623730950

たとえば、停止条件として、ほとんどの場合、相対差の限界を使用する必要があります。

eps = 1e-6;
while (abs(b-h)/b > eps) { ...
于 2013-03-13T18:57:07.260 に答える
3

丸め誤差があるため、doubleまたはfloatの同等性に依存しないでください。近似が十分になる時期を決定するための別の方法が必要です。

良いスタートは、すべてのサイクルで近似値を印刷して、それがどのように進行するかを確認することです。2つの連続する近似の差がどんどん小さくなり、突然、無秩序になる可能性があります。次に、行き過ぎたことがわかり、前の差よりも前の差が小さかった最後のapprocximationに戻ります。

sqrt(2)のように、doubleで表すことができない値を生成するケースを必ずテストしてください。表現可能な数が得られるはずのケースを必ずテストしてください。最後に、正方形の平方根をとったときに、関数が整数値を生成することを確認してください。

floatとdoubleの違いについては、floatの精度が低いため、違いが小さすぎて丸め誤差が原因で0になるポイントに到達します。ただし、これは単なる運であり、入力が異なると異なる可能性があります。ただし、仮数に含まれるビットが少ないほど、その可能性が高くなります。

于 2013-03-13T18:47:58.253 に答える
2

他の人が書いているように、バビロニア法と開平法は同じものです。バビロニア人がそれを発明しましたが、数百年後、ヘロンが最初にそれを書き留めたので、彼は信用を得ます。時々人生は公平ではありません。

精度と丸め誤差に関するコメントにも同意します。しかし、私には別の解決策を提案する必要があります。平方根が求められている数xを、範囲内になるまで4で継続的に乗算または除算することにより、範囲1 <= x <4に正規化します。次に、計算を実行する「ループを展開」します。xは既知の範囲内にあるため、ループの数を事前に計算できます。ループを終了するタイミングを決定するための計算を行う必要はありません。最後に、最初に4で除算または乗算した回数の2倍で乗算または除算します。コードは次のとおりです。

function sqrt(n)
    if n < 1
        return sqrt(n * 4) / 2
    if 4 <= n
        return sqrt(n / 4) * 2
    x := (n + 1) / 2
    x := (x + n / x) / 2
    x := (x + n / x) / 2
    x := (x + n / x) / 2
    x := (x + n / x) / 2
    x := (x + n / x) / 2
    return x

上記の擬似コードをJavaに変換するのはあなたに任せます。ダブルスには、ループを5回展開するだけで十分です。ループを通過するたびに精度の桁数が2倍になるため、フロートにはループを4回展開するだけで十分です。

ヘロンの方法についてはブログで説明しています。

于 2013-03-13T20:52:13.893 に答える