【问题标题】:Finding square root without using sqrt function?不使用 sqrt 函数求平方根?
【发布时间】:2013-11-05 19:58:38
【问题描述】:

我在不使用 sqrt 函数的情况下找出求平方根的算法,然后尝试将其用于编程。我最终在 C++ 中得到了这个工作代码

    #include <iostream>
    using namespace std;

    double SqrtNumber(double num)
    {
             double lower_bound=0; 
             double upper_bound=num;
             double temp=0;                    /* ek edited this line */

             int nCount = 50;

        while(nCount != 0)
        {
               temp=(lower_bound+upper_bound)/2;
               if(temp*temp==num) 
               {
                       return temp;
               }
               else if(temp*temp > num)

               {
                       upper_bound = temp;
               }
               else
               {
                       lower_bound = temp;
               }
        nCount--;
     }
        return temp;
     }

     int main()
     {
     double num;
     cout<<"Enter the number\n";
     cin>>num;

     if(num < 0)
     {
     cout<<"Error: Negative number!";
     return 0;
     }

     cout<<"Square roots are: +"<<sqrtnum(num) and <<" and -"<<sqrtnum(num);
     return 0;
     } 

现在的问题是在声明中初始化迭代次数 nCount(这里是 50)。例如求 36 的平方根需要 22 次迭代,所以没有问题,而求 15625 的平方根需要 50 多次迭代,所以它会在 50 次迭代后返回 temp 的值。请给出解决方案。

【问题讨论】:

  • :) root = pow(x,0.5);
  • 通常您会检查每次迭代所产生的差异,当它下降到某个水平以下时,您会将结果视为足够准确。另请注意,牛顿法通常比您使用的二分法快很多。
  • 如何检查 temp*temp 和数字之间的差异并在足够接近时停止循环?
  • @jrok 喜欢它!严肃地说,这是专业的方式:en.wikipedia.org/wiki/Newton's_method
  • @MartinBeckett 是的,如果(temp*temp-num > 0.001){upper_bound = temp;}else {lower_bound = temp; 0.001 是否足够接近?

标签: c++ algorithm math sqrt


【解决方案1】:

有一种更好的算法,最多需要 6 次迭代才能收敛到双精度数的最大精度:

#include <math.h>

double sqrt(double x) {
    if (x <= 0)
        return 0;       // if negative number throw an exception?
    int exp = 0;
    x = frexp(x, &exp); // extract binary exponent from x
    if (exp & 1) {      // we want exponent to be even
        exp--;
        x *= 2;
    }
    double y = (1+x)/2; // first approximation
    double z = 0;
    while (y != z) {    // yes, we CAN compare doubles here!
        z = y;
        y = (y + x/y) / 2;
    }
    return ldexp(y, exp/2); // multiply answer by 2^(exp/2)
}

算法从 1 开始,作为平方根值的第一近似值。 然后,在每一步,它通过取当前值yx/y 之间的平均值来改进下一个近似值。如果y = sqrt(x),则相同。如果y > sqrt(x),那么x/y sqrt(x) 的数量差不多。换句话说,它会很快收敛。

更新:为了加快在非常大或非常小的数字上的收敛速度,更改了 sqrt() 函数以从 [1, 4) 范围内的数字中提取二进制指数并计算平方根。它现在需要 frexp() from &lt;math.h&gt; 来获得二进制指数,但可以通过从 IEEE-754 数字格式中提取位而不使用 frexp() 来获得这个指数。

【讨论】:

  • 我猜停止规则的条件应该使用小于(或等于):while (fabs(y-z)
  • @EmmadKareem:不,规则很好。不过,我会将其更改为使用相对精度-对于非常大的数字可能会遇到麻烦。另一个不错的改进是将轮数限制为 20
  • 我做了一些测试,对于数字 1 到 4,只需要 6 轮就可以收敛到最大双精度。但是,对于像 1000000 这样的大数字,需要更长的时间。解决方案是分别取指数,并仅根据既不大也不小的数字计算 sqrt - 大约 1。这样轮数甚至可以固定为 6。
  • 另外,我认为这是非常罕见的情况之一,而不是检查fabs(y-z) &lt; delta,您实际上可以对双精度进行精确相等检查:y == z。这是因为算法收敛速度非常快,一旦收敛,从这一点开始就是完全相同的数字
  • 这称为Babylonian method。这个公式也可以从牛顿法推导出来。
【解决方案2】:

为什么不尝试使用Babylonian method 来求平方根。

这是我的代码:

double sqrt(double number)
{
    double error = 0.00001; //define the precision of your result
    double s = number;

    while ((s - number / s) > error) //loop until precision satisfied 
    {
        s = (s + number / s) / 2;
    }
    return s;
}

祝你好运!

【讨论】:

  • 正确使用 () 会使阅读代码变得困难.. 我有点困惑,为什么你在 s = number 时写 s-number
  • 我不会使用固定小数点。我会用double error = number * 10e-8; 替换double error = 0.00001; 行——这会使错误相对于输入值的大小。但是 +1 是一个不错且简单的示例。
  • 很好的答案!足够简单和高效。有趣的是,如果需要找到最接近的整数解,可以将循环变为无限循环,并在s 的最后两个值相等时退出。
【解决方案3】:

完全删除您的nCount(因为有一些根,该算法需要多次迭代)。

double SqrtNumber(double num)
{
    double lower_bound=0; 
    double upper_bound=num;
    double temp=0;

    while(fabs(num - (temp * temp)) > SOME_SMALL_VALUE)
    {
           temp = (lower_bound+upper_bound)/2;
           if (temp*temp >= num)
           {
                   upper_bound = temp;
           }
           else
           {
                   lower_bound = temp;
           }
    }
    return temp;
 }

【讨论】:

  • 不工作即使是完美的正方形,它也只显示最接近的值
  • 哎呀,是的,你是对的(抱歉,打字太快了)。正如@mvp 指出的那样,这是一种用于查找平方根近似值的非常慢的算法。还有其他的快得多
【解决方案4】:

因为我发现这个问题很老并且有很多答案,但我有一个简单且效果很好的答案..

#define EPSILON 0.0000001 // least minimum value for comparison
double SquareRoot(double _val) {
    double low = 0; 
    double high = _val;
    double mid = 0; 

    while (high - low > EPSILON) {
            mid = low + (high - low) / 2; // finding mid value
            if (mid*mid > _val) {
                high = mid;
            } else {
                low = mid;
            }    
    }   
    return mid;
}

希望对以后的用户有所帮助。

【讨论】:

  • 尝试调用这个函数,值为 10,000,000,000,看看需要多长时间才能得到答案。或者,拨打0.000000001,看看答案是否正确。
【解决方案5】:

如果您需要在不使用sqrt() 的情况下求平方根,请使用root=pow(x,0.5)

其中 x 是您需要找到其平方根的值。

【讨论】:

    【解决方案6】:
    //long division method.
    #include<iostream>
    using namespace std;
    int main() {
    int n, i = 1, divisor, dividend, j = 1, digit;
    cin >> n;
    while (i * i < n) {
        i = i + 1;
    }
    i = i - 1;
    cout << i << '.';
    
    divisor  = 2 * i;
    dividend = n - (i * i );
    while( j <= 5) {
        dividend = dividend * 100;
        digit = 0;
        while ((divisor * 10 + digit) * digit < dividend) {
            digit = digit + 1;
        }
        digit = digit  - 1;
        cout << digit;
        dividend = dividend - ((divisor * 10 + digit) * digit);
        divisor = divisor * 10 + 2*digit;
        j = j + 1;
    }
    cout << endl;
    return 0;
    }
    

    【讨论】:

      【解决方案7】:

      这是一种非常简单但不安全的方法来求数字的平方根。 不安全,因为它仅适用于自然数,您知道底数和指数是自然数。我不得不将它用于一项既不允许我使用 #include -library,也不允许我使用指针的任务。

      效力 = 底 ^ 指数

      // FUNCTION: square-root
      int sqrt(int x)
      {
          int quotient = 0;
          int i = 0;
      
          bool resultfound = false;
          while (resultfound == false) {
              if (i*i == x) {
                quotient = i;
                resultfound = true;
              }
              i++;
          }
          return quotient;
      }
      

      【讨论】:

        【解决方案8】:

        这是一个非常简单的递归方法。

        double mySqrt(double v, double test) {
            if (abs(test * test - v) < 0.0001) {
                return test;
            }
        
            double highOrLow = v / test;
            return mySqrt(v, (test + highOrLow) / 2.0);
        }
        double mySqrt(double v) {
            return mySqrt(v, v/2.0);
        }
        

        【讨论】:

          【解决方案9】:

          这是一个非常棒的代码,可以找到 sqrt,甚至比原来的 sqrt 函数更快。

          float InvSqrt (float x) 
          {
              float xhalf = 0.5f*x;
              int i = *(int*)&x;
              i = 0x5f375a86 - (i>>1);
              x = *(float*)&i;
              x = x*(1.5f - xhalf*x*x);
              x = x*(1.5f - xhalf*x*x);
              x = x*(1.5f - xhalf*x*x);
              x=1/x;
              return x;
          }
          

          【讨论】:

          • 1) 不解释,一文不值。 2) 以除法结尾,说明思想不被理解 3) 不注明出处,是抄袭。
          • @PascalCuoq:作者未知。 hackersdelight.org/hdcodetxt/rsqrt.c.txt
          • 其实归功于 Greg Walsh:en.wikipedia.org/wiki/…。如果您阅读了许多关于此函数主题的文章,您可以使用相同的技巧直接计算 sqrt,而不是计算平方根的倒数并取逆,这完全破坏了原始的任何优雅算法:en.wikipedia.org/wiki/…
          【解决方案10】:

          在查看了之前的回复后,我希望这将有助于解决任何歧义。如果以前的解决方案和我的解决方案的相似之处是虚幻的,或者这种求解根的方法不清楚,我还做了一个图表,可以找到here

          这是一个有效的根函数,能够求解任何 nth-root

          为了这个问题,默认是平方根

          #include <cmath> 
          // for "pow" function
          
          double sqrt(double A, double root = 2) {
              const double e = 2.71828182846;
              return pow(e,(pow(10.0,9.0)/root)*(1.0-(pow(A,-pow(10.0,-9.0)))));
          }
          

          解释

          click here for graph

          这可以通过泰勒级数、对数属性和一些代数来实现。

          举个例子:

          log A = N
             x
          

          *注意:对于平方根,N = 2;对于任何其他根,您只需要更改一个变量 N。

          1) 更改基数,将基数'x'对数函数转换为自然对数,

          log A   =>   ln(A)/ln(x) = N
             x
          

          2) 重新排列以隔离 ln(x),最终只是 'x',

          ln(A)/N = ln(x)
          

          3) 设置两边为'e'的指数,

          e^(ln(A)/N) = e^(ln(x))  >~{ e^ln(x) == x }~>  e^(ln(A)/N) = x
          

          4) 泰勒级数将“ln”表示为无穷级数,

          ln(x) = (k=1)Sigma: (1/k)(-1^(k+1))(k-1)^n
          
                     <~~~ expanded ~~~>
          
          [(x-1)] - [(1/2)(x-1)^2] + [(1/3)(x-1)^3] - [(1/4)(x-1)^4] + . . .
          

          *注意:继续该系列以提高准确性。为简洁起见,我的函数中使用了 10^9,它表示大约 7 位或百万分之一位的自然对数的级数收敛,以实现精度,

          ln(x) = 10^9(1-x^(-10^(-9)))
          

          5) 现在,只需将这个自然对数方程代入步骤 3 中获得的简化方程。

          e^[((10^9)/N)(1-A^(-10^-9)] = nth-root of (A)
          

          6) 这个实现可能看起来有点矫枉过正;但是,它的目的是演示如何在无需猜测和检查的情况下求解根。此外,它还可以让您用自己的 pow 函数替换 cmath 库中的 pow 函数:

          double power(double base, double exponent) {
              if (exponent == 0) return 1;
              int wholeInt = (int)exponent;
              double decimal = exponent - (double)wholeInt;
              if (decimal) {
                  int powerInv = 1/decimal;
                  if (!wholeInt) return root(base,powerInv);
                  else return power(root(base,powerInv),wholeInt,true);
              }
              return power(base, exponent, true);
          }
          
          double power(double base, int exponent, bool flag) {
              if (exponent < 0) return 1/power(base,-exponent,true);
              if (exponent > 0) return base * power(base,exponent-1,true);
              else return 1;
          }
          
          int root(int A, int root) {
              return power(E,(1000000000000/root)*(1-(power(A,-0.000000000001))));
          }
          

          【讨论】:

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