【问题标题】:Square root with babylonian or Herons method?使用巴比伦或苍鹭方法的平方根?
【发布时间】:2013-03-13 18:29:23
【问题描述】:

我在 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 方法看起来像是要走的路。

我对我实现了哪些方法有点困惑,¿Babylonian 方法与 Herons 方法相同吗?。

据我了解,巴比伦方法基于以下事实:正方形的边是正方形面积的平方根 (x)。所以你可以从一个 b.h 尺寸的矩形开始,得到两侧的平均值 (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 所示。

我希望有人能解释一下这种行为。

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

如建议的那样,两种算法相同,但实现方式不同。然而,以下建议的代码永远循环,就像 x=8 的 sqrt2:

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

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

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

【问题讨论】:

    标签: java algorithm math square-root


    【解决方案1】:

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

    计算倾向于减少误差,因此最终为b 计算的值非常接近精确的平方根,以至于x/bb 仅在最后一位不同。此时,使用可用的有限精度为(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 永远不会退出。


    Babylonian 和 Heron 的方法是相同的算法,它与求解 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 不等于 b。例如在 Python 中:

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

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

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

    【讨论】:

    • 谢谢,很好的答案。您认为 sqrt4 需要限制相对差异还是可以?
    • 很难说。有界会更安全。如果除法正确四舍五入,则该方法可能会收敛于所有输入,但要保证它需要更深入的分析。
    【解决方案2】:

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

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

    一定要测试会产生永远不能用双精度表示的值的用例,例如 sqrt(2)。一定要测试应该产生可表示数字的案例。最后,确保你的函数在取平方根时会产生一个整数值。

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

    【讨论】:

    • 好吧,在 sqrt3 中,我将while (b != h) 更改为我认为更“合理”的东西:while (Math.abs(b - h) > 0.000000000001),并且似乎工作正常。您对比较浮点数或双精度数是正确的,例如 sqrt1(99) 和 sqrt2(99) 循环。但我想知道为什么 sqrt4 似乎以同样的方式进行比较...
    • @Wyvern666 关于 sqrt4:你确定它永远不会循环吗?你测试了多少个数字?我相信你所拥有的只是“轶事证据”:它没有在你进行的几次测试中循环。
    • 我尝试了 for (int i = 0; i <= 1000 ; i++) 和一些小数。其他海报说这是相同的算法,但实现不同,我发现它们的工作方式不同,我认为最后一个似乎更好。
    【解决方案3】:

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

    我也同意 cmets 关于精度和舍入误差的看法。但我有一个不同的解决方案要提出。通过不断地乘以或除以 4 直到它在范围内,将求平方根的数字 x 标准化到 1 x 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 留给你。对于双打来说,循环的五次展开就足够了。由于每次循环都会使精度位数翻倍,因此循环的四次展开对于浮点数就足够了。

    我在my blog 讨论 Heron 的方法。

    【讨论】:

    • 在您的博客中,您将 Heron 的算法描述为:“如果 xk 是 √x 的良好近似值,则 xk 和 x/xk 的平均值是更好的近似值;”,很像我的描述从维基百科粘贴。但是巴比伦算法不是更多的几何方法吗?我的意思是按顺序将矩形转换为正方形。也许我的数学很弱,我看不出这两个东西是一样的,但我可以说 Heron 是另一个的衍生物或类似的东西。
    • 此外,在维基百科上的“计算平方根的方法”的西班牙语版本中 [link]es.wikipedia.org/wiki/… 并没有提到 Heron,并且解释与 Enlgish 版本关于相同方法的解释不相似.这就是为什么我想出了两个似乎生成相同数量的迭代但具有不同停止条件的实现,尽管我可以在两种方法上使用相同的停止条件。
    • 我不是数学历史学家,但我可以告诉你,巴比伦人(也包括希腊人)以几何方式思考,因为他们描述计算所需的代数要再过两千年才能发明出来.我已经看到在我信任的几个来源中描述的巴比伦和苍鹭方法是相同的,所以我怀疑您的实现中的任何差异都是由于各种来源中算法描述的微小差异。这可能是因为巴比伦人没有留下书面描述,所以我们所知道的关于他们的算法的一切都是推论出来的。
    猜你喜欢
    • 2020-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多