【问题标题】:square root algorithm C++平方根算法 C++
【发布时间】:2015-01-15 06:53:14
【问题描述】:

如果输入的数字超过 12 位,我无法弄清楚为什么该算法会进入无限循环。谁能明白为什么它永远不会结束?谢谢。我刚刚更新了算法以使用 fabs() 函数并且仍然得到一个无限循环。

double squareroot(double x)

{ /* computes the square root of x */

/* make sure x is not negative .. no math crimes allowed! */
assert( x >= 0 );
if (x==0) return 0;

/* the sqrt must be between xhi and xlo */
double xhi = x;
double xlo = 0;
double guess = x/2;

/* We stop when guess*guess-x is very small */

while (abs(guess*guess-x) > 0.00001 )
{
    if (guess*guess > x){
        xhi = guess;
    }

    else {
        xlo = guess;
    }

    guess = (xhi + xlo)/2;
}
return guess;
}

【问题讨论】:

  • abs 是一个整数函数。你的意思是fabs
  • guess*guess 的 12 位数字?那会很大..
  • 您对终止标准的固定 epsilon 可能也应该相对于输入值表示。否则,您对 16.0e-8 的计算甚至不会进入第一次迭代。

标签: c++ algorithm oop square-root


【解决方案1】:

我相信你应该使用相对误差来终止,而不是绝对误差。

while (abs((guess*guess-x) / guess) > 0.00001)

否则将需要很长时间(这不是无限循环)来计算很长值的平方根。

http://en.wikipedia.org/wiki/Approximation_error

干杯!

编辑:此外,正如下面在 cmets 中指出的那样,值得检查 guess 是否已经被猜到,以避免在某些特定极端情况下出现无限循环。

【讨论】:

  • 当数字很大时,循环很可能会变得无限:当xhixlo 相邻时,中点将始终是其中之一。当xhi - xlo 大于所选的 epsilon 时,您会得到一个无限循环。
  • @MOehm 好点。这可以额外检查,即当之前已经猜到新猜测时应该退出循环(guess == xhi || guess == xlo
  • @MOehm 同意。正如 leemes 在评论中指出的那样,TonyD 的答案解决了这个问题。 (虽然为了实现它而获得一个实际的 x 会很酷:D)
【解决方案2】:

我建议等到你得到一个稳定的答案,而不是摆弄 epsilon 值:

double squareroot(double x)
{
    if (x < 1) return 1.0 / squareroot(x);  // MSalter's general solution

    double xhi = x;
    double xlo = 0;
    double guess = x/2;

    while (guess * guess != x)
    {
        if (guess * guess > x)
            xhi = guess;
        else
            xlo = guess;

        double new_guess = (xhi + xlo) / 2;
        if (new_guess == guess)
            break; // not getting closer
        guess = new_guess;
    }
    return guess;
}

【讨论】:

  • 非常好!由于反复使用相同的猜测而发生的无限循环的补救措施当然是强制执行不同的猜测。
  • 这适用于 x=0.25 吗?因为算法似乎假设sqrt(x) 位于[0,x] 范围内,而sqrt(0.25) &gt; 0.25。第一个猜测是 0.125,第二个猜测是 0.1875 等等。看起来 xlo 收敛到 xhi 并且当 xlo=xhi=guess=0.25 时循环终止,这显然不是 0.5
  • @MSalters:不……值得一提;我不是试图修复算法 - 只是处理问题中的收敛/精度问题。
  • 好吧,无论如何,修复很容易。 sqrt(1/x) = 1/sqrt(x),所以只需添加if (x&lt;1) return 1.0/squareroot(x);
【解决方案3】:

这不是直接回答您的问题,而是另一种解决方案。

你可以使用Newton's method for finding roots:

assert(x >= 0);
if (x == 0)
    return 0;

double guess = x;
for (int i=0; i<NUM_OF_ITERATIONS; i++)
    guess -= (guess*guess-x)/(2*guess);
return guess;

24 次迭代应该可以为您提供足够好的近似值,但您也可以检查绝对差异。

【讨论】:

    【解决方案4】:

    http://floating-point-gui.de/errors/comparison/

    嘿,看起来你不应该那样使用 abs() 。在某些情况下它应该停止,但我不会,因为它的精度有限。

    改为使用 fabs()

    http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

    【讨论】:

    • 谢谢,我刚刚更新了它以使用 fabs() 函数,我仍然得到一个无限循环。
    • 显然有一些 C++ 程序员不知道 std::abs 重载函数。
    • @n.m.: Touché。我是通过 algorithm 标签来到这里的,我没有看到 C++ 标签。刚看了看代码,发现是 C。没有流,没有命名空间,没有 &lt;math&gt;
    • @csciXAV_12 std::abs 超载(并且可能在标头 &lt;cmath&gt; 中出现 abs。请参阅 en.cppreference.com/w/cpp/numeric/math/fabs。发布 MCVE 很重要,如果可能,请使用 @987654329明确地@。
    【解决方案5】:

    我会说,当数字足够大时,您不能使用绝对 epsilon 值,因为它不适合精度。

    尝试使用相对比较。考虑以下函数来检查 2 个双打是否相等:

    bool are_eql(double n1, double n2, double abs_e, double rel_e)
    {
      // also add check that n1 and n2 are not NaN
      double diff = abs(n1 - n2);
      if (diff < abs_e)
        return true;
      double largest = max(abs(n1), abs(n2));
      if (diff < largest * rel_e)
        return true;
      return false;
    }
    

    【讨论】:

    • 第一个lijne显然应该是abs(n1-n2)。我想largest 应该是abs(max(n1,n2)) - 如果两者都是负数怎么办?在您的代码中,largest 可以为负数,在这种情况下,diff&gt;largest(如 +0.01 > -100.0)
    猜你喜欢
    • 1970-01-01
    • 2022-11-14
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多