用于求非线性方程根的Durand-Kerner方法

时间:2014-12-14 12:39:56

标签: c++ polynomial-math nonlinear-functions newtons-method

我被要求找到f(x)= 5x(e ^ -mod(x))cos(x)+ 1的根。我以前使用Durand-Kerner方法找到函数x ^ 4 -3x ^ 3 + x ^ 2 + x + 1的根,其代码如下所示。我想我可以简单地重用代码来找到f(x)的根,但每当我用f(x)替换x ^ 4 -3x ^ 3 + x ^ 2 + x + 1时,程序输出所有根的nan。我的Durand-Kerner实现有什么问题,如何修改它以适应f(x)?我会非常感谢任何帮助。

#include <iostream>
#include <complex>
#include <math.h>

using namespace std;

typedef complex<double> dcmplx;

dcmplx f(dcmplx x)
{
    // the function we are interested in
    double a4 = 1;
    double a3 = -3;
    double a2 = 1;
    double a1 = 1;
    double a0 = 1;

    return (a4 * pow(x,4) + a3 * pow(x,3) + a2 * pow(x,2) + a1 * x + a0);
}


int main()
{

dcmplx p(.9,2);
dcmplx q(.1, .5);
dcmplx r(.7,1);
dcmplx s(.3, .5);

dcmplx p0, q0, r0, s0;

int max_iterations = 100;
bool done = false;
int i=0;

while (i<max_iterations && done == false)
{
    p0 = p;
    q0 = q;
    r0 = r;
    s0 = s;


p = p0 - f(p0)/((p0-q)*(p0-r)*(p0-s));
q = q0 - f(q0)/((q0-p)*(q0-r)*(q0-s));
r = r0 - f(r0)/((r0-p)*(r0-q)*(r0-s0));
s = s0 - f(s0)/((s0-p)*(s0-q)*(s0-r));

    // if convergence within small epsilon, declare done
    if (abs(p-p0)<1e-5 && abs(q-q0)<1e-5 && abs(r-r0)<1e-5 && abs(s-s0)<1e-5)
        done = true;

    i++;
}

cout<<"roots are :\n";
cout << p << "\n";
cout << q << "\n";
cout << r << "\n";
cout << s << "\n";
cout << "number steps taken: "<< i << endl;

return 0;
}

到目前为止,我唯一改变的是dcmplx f函数。我一直在改变它

dcmplx f(dcmplx x)
{
    // the function we are interested in
    double a4 = 5;

    double a0 = 1;

    return (a4 * x * exp(-x) * cos(x) )+ a0;
}

2 个答案:

答案 0 :(得分:3)

您正在使用的Durand-Kerner方法要求该函数在您工作的时间间隔内是连续的。

这里我们在数学视图和数字应用程序的限制之间存在差异。我建议您绘制您的功能(在谷歌中键入公式将为您提供实际部分的快速概述)。你会注意到:

  • 由于余弦的周期性,有一个无限的根。
  • 由于x * exp(-x),绝对值快速上升超过浮点数可以容纳的最大精度。

要了解对代码的影响,我邀请您跟踪不同的迭代。你会注意到p,r和s在q发散时很快收敛(显然是在一个巨大的峰值的轨道上):

  • 在第二次迭代中,q已经是1e74
  • 第三次迭代已超出双人可以存储的范围。
  • 由于q用于计算p,r和s,误差会传播到其他术语
  • 在第5次迭代时,所有术语均为NAN
  • 然后勇敢地继续进行100次迭代

Perhap你可以通过选择不同的起点来使它发挥作用。如果没有,您将不得不使用其他方法并仔细选择您正在工作的内墙。

答案 1 :(得分:1)

你应该在你的文件中注意到Durand-Kerner方法(由Karl Weierstrass在1850年左右发明),它只适用于多项式。你的第二个功能远非多项式。

实际上,由于mod函数,它必须被声明为数值方法的讨厌函数。它们中的大多数依赖于给定函数的连续性,即,如果该值接近于零,则很可能在附近存在根,并且如果符号在一个间隔上改变则在该间隔中存在根。即使是最基本的无衍生方法,如在该类的复杂末端上的二分法或Brents方法,也可以预设这些属性。