在c++中使用递归的Newton-Raphson

Newton-Raphson using recursion in C++

本文关键字:递归 Newton-Raphson c++      更新时间:2023-10-16

我编写了下面的代码来使用牛顿方法计算平方根,但是每次运行它都会溢出。我自己试着检查过,但没有发现任何错误。你们能帮帮我吗?

double root(double n,double init){
    if(fabs(init*init-n)<=0.00001){
        return init;
    }else{
        init=(init*init-n)/2*init;
        return root(n,init);
   }
}
int main()
{
    double a;
    cout<<"Enter any number to get its square root: ";
    cin>>a;
    cout<<"Square Root of "<<a<<" is: "<<root(a,2);
    return 0;
}

所以你的问题是双重的。首先,你的牛顿方法有点偏离(这是一个更大的问题)。其次,你实现它的方式会导致溢出。

第一期(更大的一期):

其他答案似乎忽略了这一点,尽管这是一个更大的问题。计算机可以处理小数字的平方溢出,比如计算9的平方根,但你的方法甚至对它们都不起作用。这是因为,正如我在评论中提到的,你的牛顿方法有点偏离。

你应该在这一行使用加号而不是减号(试着重新推导看看为什么):

init=(init*init+n)/(2*init);

推导:

x_{k+1} = x_k - f(x_k)/f'(x_k)
        = x_k - ((x_k)^2 - n)/(2x_k)
        = x_k - 1/2*x_k + n/(2x_k)  // note the + here
        = 0.5*x_k + 0.5*n/x_k
        = 0.5*(x_k + n/x_k)

其中x_kinit变量。

<标题>问题2:

init*init会导致数字增长过快和溢出(更大的数字有更多的错误)。你可以用代数方法重写为:

init=0.5*(init + n/init);

把这些放在一起:

#include <iostream>
#include <cmath>
using namespace std;
double root(double n,double init){
    if(fabs(init*init-n)<=0.00001){
        return init;
    }else{
        init=0.5*(init + n/init);
        return root(n,init);
   }
}
int main()
{
    double a;
    cout<<"Enter any number to get its square root: ";
    cin>>a;
    cout<<"Square Root of "<<a<<" is: "<<root(a,2);
    return 0;
}

您正在使用递归,这可能会溢出堆栈。例如,我试图输入9999,而你的程序永远挂起(因为n没有减少)。

另一个问题是double*double可能溢出。

正确的(高效、简洁和迭代的)版本是:

double root(double n){ // you dont need the second parameter
    double val = n; // initial guess
    for(;;) {
        double last = val;
        val = (val + n / val) * 0.5; // iterative
        if (abs(val - last) < 1e-9) break;  // error small enough
    }
    return val;
}

,如果你坚持递归,这里有一个更简洁的递归实现:

double root(double n, double last){
    if (abs(last * last - n) < 1e-9) { // good guess
            return last;
    }
    return root(n, 0.5 * (last + n / last)); // recursive call
}

更多信息请参见"牛顿迭代Sqrt法"