方根与巴比伦或苍鹭的方法?

时间:2013-03-13 18:29:23

标签: java algorithm math square-root

我在java中实现了Babylonian / Heron的方法来获取数字的平方根,基于Wikipedia info

目前我有:

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方法适用于浮点数。我不知道为什么会这样。 因此,如果我想使用双打,那么sqrt3方法就像是要走的路。

我对实施哪些方法感到困惑,¿巴比伦方法与苍鹭方法相同吗?。

据我所知,巴比伦方法是基于这样一个事实:正方形的一边是正方形(x)面积的平方根。因此,您可以从具有bh尺寸的矩形开始,获得两边的平均值(b = b + h / 2),然后将此结果视为较小矩形的一侧,当然得到另一边(h = x / b)。矩形将开始进入所需的正方形。这是我在sqrt1,sqrt2和sqrt3方法中所做的:

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

另一方面,维基百科链接说巴比伦/苍鹭的方法是相同的,并将其描述为:

  

“基本思想是,如果x高估了非负实数S的平方根,则S / x将被低估,因此可以合理地预期这两个数字的平均值可以提供更好的结果近似“

您可以在sqrt4方法中看到此实现:

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

从我所看到的,这两种方法不一样,但相似。如果我错了,请纠正我。

有趣的是我无法做到:while (b != h) {当b和h是sqrt2方法中显示的双倍时,因为它将永远循环。相反,我使用了while (Math.abs(b - h) > 0.000000000001) {

但是,我可以这样做:while (t != r) {使用b和h加倍,如sqrt4所示。

我很感激有人解释这种行为。

-----------编辑:------------------------------- -----------------------------

正如所建议的那样,两种算法都是相同的,但实现的不同。但是,以下建议的代码循环就像sqrt2 x = 8:

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

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

那么.. sqrt4与其他实现的区别是什么?为什么它不会像其他人一样永远循环?

3 个答案:

答案 0 :(得分:5)

更新: sqrt4最终退出循环的机会比sqrt5更好,因为它的停止条件会将一次迭代的近似值与之前的迭代次数进行比较。< / p>

计算倾向于减少错误,因此最终为b计算的值非常接近于x/b仅在最后一位与b不同的确切平方根。此时,使用可用有限精度为(x / b + b) / 2计算的值将等于b,并且迭代停止。

例如,如果我们计算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的Newton-Rhapson方法一致。您拥有的各种实现之间的区别在于停止条件。例如:

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

与:

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

当然,b != x/b不是一个非常好的停止条件,因为b可能永远不会成为x的数学上精确的平方根:如果b不是精确的平方根,则x / b不相等湾例如在Python中:

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

您应该几乎总是使用相对差异的界限作为停止条件,例如

eps = 1e-6;
while (abs(b-h)/b > eps) { ...

答案 1 :(得分:3)

由于舍入错误,您不应该依赖双精度或浮点数的相等性。你需要另一种方法来确定你的近似值何时足够好。

一个良好的开端是在每个循环中打印出近似值,以便了解它是如何进行的。可能是两个连续近似之间的差异越来越小,然后突然变得混乱。然后你就知道你确实走得太远了,回到最后一个approcximation,其中与之前的差异小于之前的差异。

一定要测试会产生一个永远不能用double表示的值的情况,比如sqrt(2)。一定要测试应该产生可表示数字的案例。最后,确保在获取正方形的平方根时,函数将产生一个整数值。

对于float和double的差异:由于浮点数的精度较低,你会达到差异太小的点,并且由于舍入误差导致0.但这只是运气,并且可能因不同的输入而不同。但是,尾数中的位数越少,就越有可能。

答案 2 :(得分:2)

正如其他人所写,巴比伦方法和苍鹭的方法是一回事。巴比伦人发明了它,但苍鹭是第一个把它写下来的,几百年后,所以他得到了荣誉。有时生活不公平。

我也同意有关精度和舍入误差的评论。但我提出了不同的解决方案。将正在寻找平方根的 x 数量归一化到范围1&lt; = x &lt; 4通过连续乘以或除以4直到它在范围内。然后“展开执行计算的循环”;由于 x 处于已知范围内,因此可以预先计算循环次数,而无需进行计算以确定何时终止循环。最后,乘以或除以2除以最初的次数或乘以4。这是代码:

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。循环的五次展开足以用于双打。由于每次循环都会使精度数字加倍,因此循环的四次展开足以用于浮点数。

我在my blog讨论了Heron的方法。