简体   繁体   English

获取近似平方根

[英]Get approximate square root

I'm implementing Babylonian method to approximate the square root of number n using following formula : 我正在使用以下公式实现巴比伦方法来近似数字n平方根:

nextGuess = (lastGuess + n / lastGuess) / 2;

So when nextGuess and lasGuess are almost identical, nextGuess is the approximated square root. 因此,当nextGuesslasGuess几乎相同时, nextGuess是近似平方根。

What am doing is checking if nextGuess and lastGuess is less than very small number such as 0.0001 then i can claim that nextGuess is the approximated square root of n . 在做的是检查nextGuesslastGuess是否小于非常小的数字(例如0.0001那么我可以断言nextGuessn的近似平方根。 if not nextGuess become lastGuess . 如果不是,则nextGuess成为lastGuess

So how i can implement that in the right way? 那么我如何以正确的方式实现呢?

My current code: 我当前的代码:

public static void getApproximatedSquare(long n){
    DecimalFormat decimalFormat = new DecimalFormat("#.####");
    decimalFormat.setRoundingMode(RoundingMode.CEILING);

    double lastGuess = 1, nextGuess;

    nextGuess = (lastGuess + n / lastGuess) / 2;
    Double init =  0.0001;
    System.out.println(decimalFormat.format(init));
    if (Double.valueOf(decimalFormat.format(nextGuess)) <= init)
        //todo

}

Current draft of implementation has a few flaws: 当前的实施草案有一些缺陷:

  • I doubt you really need Double.valueOf(decimalFormat.format(...)) , it just removes some precision from the result 我怀疑您是否真的需要Double.valueOf(decimalFormat.format(...)) ,它只是从结果中删除了一些精度
  • Your convergence condition is not nextGuess < init but difference_between_nextGuess_and_lastGuess < init 您的收敛条件不是nextGuess < init而是difference_between_nextGuess_and_lastGuess < init
  • You have to repeat the approximation until convergence, so you can't use just a if . 您必须重复逼近直到收敛,所以不能只使用if You need a for or while or (as in my solution) do... while 您需要forwhile或(按照我的解决方案) do... while
  • This should work (at each step, it prints last and next guesses) 这应该可以工作(在每个步骤中,它会打印出最后和下一个猜测)

     public static double getApproximatedSquare(long n) { DecimalFormat decimalFormat = new DecimalFormat("#.####"); decimalFormat.setRoundingMode(RoundingMode.CEILING); double lastGuess, nextGuess = 1; double init = 0.0001; do { lastGuess = nextGuess; nextGuess = (lastGuess + (double) n / lastGuess) / 2; System.out.println(decimalFormat.format(lastGuess)+" ---> "+decimalFormat.format(nextGuess)); } while (Math.abs(lastGuess - nextGuess) >= init); return nextGuess; } 

Using an absolute tolerance is always a bad idea because it doesn't take into account the order of magnitude of the argument. 使用绝对公差总是一个坏主意,因为它没有考虑参数的数量级。 A relative error is better. 相对误差更好。

But in the case of the square root, I recommend a much better approach: make sure that your initial approximation is within a factor √2 of the exact root. 但是,对于平方根,我建议采用更好的方法:确保您的初始近似值在精确根的√2范围内。 This is obtained by halving the exponent of 2 in the floating-point representation of the argument. 这是通过将自变量的浮点表示形式中的2的指数减半而获得的。 (If you can't access this exponent, you can obtain it by successive divisions or multiplications until to reach the interval [1, 2).) (如果无法访问此指数,则可以通过连续的除法或乘法来获得它,直到达到间隔[1、2]。)

Example: for 27, you have 16 ≤ 27 < 32. Then 1 ≤ √27 / 4 < √2, and you can start the iterations from 4. 例如:对于27,您有16≤27 <32。然后1≤√27/ 4 <√2,则可以从4开始迭代。

Then perform four iterations of the Babylonian formula. 然后执行巴比伦公式的四个迭代 No less, no more. 不多不多。

In the example, after four iterations, you obtain 5.19615242271, which is exact. 在此示例中,经过四次迭代,您获得了5.19615242271,这是准确的。


If you have the feeling that the successive halving or doubling process is slow and believe that Newton is faster, consider that (x + n / x) / 2 > x / 2, so that Newton actually converges slower than halvings and involves more arithmetic ! 如果您感到连续的减半或加倍过程很慢并且认为牛顿更快,请考虑(x + n / x)/ 2> x / 2,这样牛顿实际上会比减半收敛慢,并且涉及更多的算术!

If nextGuess 's value is 100% sure to go down and reach a good enough value, can't you just do this? 如果nextGuess的值是100%,请确保下降并达到足够好的值,您不能这样做吗?

public static void getApproximatedSquare(long n){
    DecimalFormat decimalFormat = new DecimalFormat("#.####");
    decimalFormat.setRoundingMode(RoundingMode.CEILING);

    double lastGuess = n + 1;
    double nextGuess = n;
    double init      = 0.0001;

    while (lastGuess - nextGuess > init)
    {
        lastGuess = nextGuess;
        nextGuess = (lastGuess + n / lastGuess) / 2;
    }

    System.out.println(decimalFormat.format(init));
}

As nextGuess approaches sqrt(n) from above, you can use this: nextGuess从上面接近sqrt(n)时,您可以使用以下命令:

double lastGuess = n;
double nextGuess = n;
double epsilon = 1e-4;
while (lastGuess - nextGuess > epsilon) {
    lastGuess = nextGuess;
    nextGuess = (lastGuess + n / lastGuess) / 2;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM