【问题标题】:Does optimization of the initial guess make the Babylonian method of finding square roots fast?初始猜测的优化是否使巴比伦求平方根的方法更快?
【发布时间】:2017-09-25 10:35:53
【问题描述】:

Babylonian aka Heron 的方法似乎是寻找数字 n 平方根的更快算法之一。它收敛的速度取决于您最初猜测的距离。

现在随着数字 n 的增加,它的根 x 随着百分比的减少而增加。
根(10):10 - 31%
10:100 - 10%
根(100) : 100 - 3%
根(1000) : 1000 - 1%
所以基本上对于数字中的每个数字除以 3 左右。然后用它作为你的初始猜测。比如——

 public static double root(double n ) {
      //If number is 0 or negative return the number
            if(n<=0)
                return n ;
     //call to a method to find number of digits
            int num = numDigits(n)  ;
            double guess =  n ;
    //Divide by 0.3 for every digit from second digit onwards
            for (int i = 0 ; i < num-1 ; ++i )
                guess = guess * 0.3;
    //Repeat until it converges to within margin of error
            while (!(n-(guess*guess) <= 0.000001 && n-(guess*guess) >= 0 )) {
                double divide = n/guess ;
                guess = Math.abs(0.5*(divide+guess)) ;

            }

            return Math.abs(guess)  ;
        }

这是否有助于优化算法。这是 O(n) 吗?

【问题讨论】:

  • 求位数的函数是十进制对数函数;实际上,它的 1+ 该数字的十进制对数的整数部分
  • 另外,不要像这样迭代:for (int i = 0 ; i &lt; num-1 ; ++i ) guess = guess * 0.3;,这会增加复杂性,您可以这样做 guess=guess* Math.pow(0.3, num-1)
  • 这是一个非常糟糕的收敛条件,它根本没有考虑数字的大小,所以它只能在特定的输入范围内工作。
  • @Newtonfan01 - 我知道它是 log10,最初使用的是 Math.log10() 方法。但是我将其切换为使用模数来计算位数的简单方法,因为我认为找到确切的日志需要更多时间?

标签: java algorithm square-root


【解决方案1】:

对于 n ≥ 4,sqrt(n) = 2 sqrt(n / 4)。对于 n n) = 1/2 sqrt(n × 4)。因此,您始终可以乘以或除以 4 以在范围 [1, 4) 上归一化 n

一旦你这样做了,将 sqrt(4) = 2 作为 Heron 算法的起点,因为这是几何平均值,每次迭代都会产生最大可能的改进,然后展开循环以执行所需的迭代次数达到所需的精度。

最后,乘以或除以一开始删除的 2 的所有因数。请注意,对于二进制计算机来说,乘以 2 或 4 既简单又快速。

我在my blog 讨论这个算法。

【讨论】:

    【解决方案2】:

    我认为算法的复杂性与所提供的输入无关。 (复杂度是该算法的一般特征,我们不能说算法 x 对于输入 I1 的复杂度为 O1,对于输入 I2 的复杂度为 O2)。因此,无论您提供什么初始值,都不应该提高复杂性。它可能会提高特定情况下的迭代次数,但这是另一回事。将迭代次数减少一半仍然意味着相同的复杂性。请记住,n、2*n、n/3 都适合 O(n) 类。

    现在,关于实际的复杂性,我在 wikipedia (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) 上看到

    This is a quadratically convergent algorithm, which means that the number of correct digits of the approximation roughly doubles with each iteration. 
    

    这意味着您需要的迭代次数与您期望的精度小数位数一样多。这是恒定的。如果您需要 10 个精确的小数,则 10 是一个常数,完全独立于 n。

    但是在维基百科的例子中,他们从一开始就选择了一个与正确答案具有相同数量级的候选者(600 与 354)。但是,如果您最初的猜测过于错误(按数量级),您将需要一些额外的迭代来减少/添加必要的数字。这会增加复杂性。假设正确答案是 10000 ,而您的初始猜测是 10。差异是 4 个数量级,我认为在这种情况下,达到正确数量级所需的复杂性与您猜测的位数和正确答案的位数。由于位数近似为 log(n),在这种情况下,额外的复杂度为log(corect_answer) -log(initial_guess),取绝对值。

    为避免这种情况,请选择一个位数正确的号码,通常是初始号码位数的一半。我最好的选择是选择数字的前半部分作为候选(从 123456 开始,保留 123,从 1234567 开始,123 或 1234)。在java中,您可以使用字节操作来保留数字/字符串的前半部分/任何保存在内存中的内容。因此,您不需要迭代,只需一个具有恒定复杂性的字节操作。

    【讨论】:

      【解决方案3】:

      是的。更好的方法是利用浮点表示,通过将二进制指数大约除以 2,因为对浮点位进行操作非常快。见Optimized low-accuracy approximation to `rootn(x, n)`

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多