【问题标题】:Runge-Kutta 4th order method cumulative errors when iteratingRunge-Kutta 4 阶方法迭代时的累积误差
【发布时间】:2016-11-17 14:32:20
【问题描述】:

我试图获得一个简单追逐问题的数值解

(移动目标+火箭等速模块)

每次迭代我的速度模块都会减少一点,从而增加错误;在几百次迭代错误之后,速度急剧下降。

但是,欧拉法(大块下方的代码)不是这种情况,它只会在使用RK4方法时弹出。

我不确定错误出在哪里以及为什么会发生,因此感谢任何输入

#include <fstream>
#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>
#define vx(t,x,y) n*V*((t)*(V)-(x))/pow(((t)*(V)-(x))*((t)*(V)-(x))+((h)-(y))*((h)-(y)),0.5)
#define vy(t,y,x) n*V*((h)-(y))/pow(((t)*(V)-(x))*((t)*(V)-(x))+((h)-(y))*((h)-(y)),0.5)
using namespace std;
class Vector {
public:
    double x,y;
    Vector(double xx, double yy):x(xx), y(yy){};
    virtual ~Vector(){}
    Vector operator-() {return Vector(-x,-y);};
    friend Vector operator-(const Vector &, const Vector &); 
    friend Vector operator+(const Vector &, const Vector &); 
    Vector operator*(double l){return Vector(x*l,y*l);};
    friend Vector operator*(double, const Vector &);
    Vector operator/(double l){return Vector(x/l,y/l);};
    void operator+=(const Vector & v ){ x+=v.x; y+=v.y;};
    void operator-=(const Vector & v ){ x-=v.x; y-=v.y;};
    void operator/=(const Vector & v ){ x/=v.x; y/=v.y;};
    friend ostream & operator<<(ostream & os,const Vector & v){os<<"("<<v.x<<", "<<v.y<<")";return os;};
    double norm() {return sqrt(x*x+y*y);};
};

Vector operator-(const Vector & v1, const Vector & v2){
    return Vector(v1.x-v2.x,v1.y-v2.y);
}
Vector operator+(const Vector & v1, const Vector & v2){
    return Vector(v1.x+v2.x,v1.y+v2.y);
}
Vector operator*(double l, const Vector & v){
    return Vector(v.x*l,v.y*l);
}

int main() {
    Vector posP(0,0);
    double V=100.,t = 0,dt = pow(10.,-2),vx,vy,h=1000.,x,y,n=2.,v;
    double kx1,kx2,kx3,kx4,ky1,ky2,ky3,ky4;
    Vector posE(0,h);
    FILE *fp;
    fp = fopen("/Users/Philipp/Desktop/a.dat","w");
    while(posP.y<(h)){
        posE.x=posE.x+V*dt;
        x=posP.x;y=posP.y;
        kx1 = vx(t,x,y);
        ky1 = vy(t,y,x);
        kx2 = vx(t+dt/2.0,x+kx1/2.0,y+ky1/2.0);
        ky2 = vy(t+dt/2.0,y+ky1/2.0,x+kx1/2.0);
        kx3 = vx(t+dt/2.0,x+kx2/2.0,y+ky2/2.0);
        ky3 = vy(t+dt/2.0,y+ky2/2.0,x+kx2/2.0);
        kx4 = vx(t+dt,x+kx3,y+ky3);
        ky4 = vy(t+dt,y+ky3,x+kx3);
        posP.x = posP.x + dt*((kx1 + 2.0*(kx2+kx3) + kx4)/6.0);
        posP.y = posP.y + dt*((ky1 + 2.0*(ky2+ky3) + ky4)/6.0);
        v=sqrt(((kx1 + 2.0*(kx2+kx3) + kx4)/(6.0))*((kx1 + 2.0*(kx2+kx3) + kx4)/(6.0))+((ky1 + 2.0*(ky2+ky3) + ky4)/(6.0))*((ky1 + 2.0*(ky2+ky3) + ky4)/(6.0)));
        t+=dt;
        if ((posE-posP).norm()<1) break;
        fprintf(fp,"%lf %lf %lf %lf \n",posP.x, posP.y, v, t);
    }
    fclose(fp);
    return 0;
}

欧拉方法

//Euler cycle
    while(posP.y<(h)) {
        posE.x=posE.x+V*dt;
        x=posP.x;y=posP.y;
        vx=vx(t,x,y);
        vy=vy(t,y,x);
        posP.x=posP.x+vx*dt;
        posP.y=posP.y+vy*dt;
        t+=dt;
        if ((posE-posP).norm()<0.1) break;
        fprintf(fp,"%lf %lf %lf \n",posP.x, posP.y,vx*vx+vy*vy, t);

//速度模块在第三列,你可以看到一切都是 200,而不是 RK4 的情况,即使是第一次迭代它也会下降到 ~199.99985

    }

【问题讨论】:

  • 计算机上的浮点值往往存在舍入错误。这些舍入误差复合。见Is floating point math broken?
  • @Someprogrammerdude 那么为什么欧拉方法即使在小步长下也能工作?我还是觉得我的 RK4 方法有问题
  • 请为 vx 和 vy 使用内联函数。所有这些括号使它完全不可读。
  • 另外,你说“速度模块在第三列”——第三列是什么?
  • @MartinBonner 在输出文件中;关于vx&vy:puu.sh/slliu/808a54c15f.png

标签: c++ differential-equations runge-kutta


【解决方案1】:

你使用

kx2 = vx(t+dt/2.0,x+kx1/2.0,y+ky1/2.0);

但应该是:

kx2 = vx(t+dt/2.0,x+kx1/2.0*dt,y+ky1/2.0*dt);

以后也一样。 或者,您可以通过 dt 对所有 k 值进行多个:

kx2 = dt*vx(t+dt/2.0,x+kx1/2.0,y+ky1/2.0);

这两种变体对于隐式 Runge-Kutta 方法更为重要

【讨论】:

  • 我在那里使用它而不是在posP.x = posP.x + dt*((kx1 + 2.0*(kx2+kx3) + kx4)/6.0);中使用它真的很重要吗?
  • 是的。当您将速度添加到 x 时,您总是必须将速度乘以 dt
  • 好吧,在我刚刚发给你的那段代码中,它稍后会成倍增加
  • 但是您必须始终乘以 dt。请参阅标准参考资料,例如 Hairer、Noersett、Wanner “Solving Ordinary Differential Equations I”,或 en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-10
  • 1970-01-01
  • 2017-09-21
  • 2023-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多