【问题标题】:Runge-Kutta algorithm C++龙格-库塔算法 C++
【发布时间】:2017-08-08 22:31:12
【问题描述】:

下面是我求解一阶 ODE 的四阶 Runge-Kutta 算法。我正在对照找到here 的维基百科示例进行检查以解决:

\frac{dx}{dt} = tan(x) + 1

不幸的是,它有点过时了。我玩了很长时间,但我找不到错误。答案应该是 t = 1.1 和 x = 1.33786352224364362。下面的代码给出 t = 1.1 和 x = 1.42223。

/*

This code is a 1D classical Runge-Kutta method. Compare to the Wikipedia page.

*/

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

double x,t,K,K1,K2,K3,K4;

const double sixth = 1.0 / 6.0;

static double dx_dt(double t, double x){
    return tan(x) + 1;
}

int main(int argc, const char * argv[]) {

/*======================================================================*/
/*===================== Runge-Kutta Method for ODE =====================*/
/*======================================================================*/

double t_initial = 1.0;// initial time
double x_initial = 1.0;// initial x position

double t_final = 1.1;// value of t wish to know x 
double dt = 0.025;// time interval for updates
double halfdt = 0.5*dt;

/*======================================================================*/

while(t_initial < t_final){

    /*============================ Runge-Kutta increments =================================*/ 

    double K1 = dt*dx_dt( t_initial, x_initial );
    double K2 = dt*dx_dt( t_initial + halfdt, x_initial + halfdt*K1 );
    double K3 = dt*dx_dt( t_initial + halfdt, x_initial + halfdt*K2 );
    double K4 = dt*dx_dt( t_initial + dt, x_initial + dt*K3 );

    x_initial += sixth*(K1 + 2*(K2 + K3) + K4);

    /*============================ prints =================================*/

    std::cout << t_initial << std::setw(16) << x_initial << "\n";

    /*============================ re-setting update conditions =================================*/

    t_initial += dt;

    /*======================================================================*/
}

std::cout<<"----------------------------------------------\n";
std::cout << "t =  "<< t_initial << ",  x =  "<< x_initial << std::endl; 


}/* main */

【问题讨论】:

  • dt变小会更好吗?
  • double dx_dt(double t, double x) 看起来很有趣,因为您没有在函数内部使用参数 t
  • @TheQuantumPhysicist - 我的 SO 测试项目的所有警告都设置为最大值,因此它会标记未使用的参数等。抱歉,如果我误导了。
  • @PeterSM 在这一点上,我建议您开始学习如何使用调试器,并确保每个值都是您所期望的,从第一个 k1 开始。
  • 如果使用 C++,则不应涉及 C 头文件。将 #include&lt;cmath&gt; 用于具有 std 命名空间的 C++ 标头。

标签: c++ math numerical-methods runge-kutta


【解决方案1】:

问题是用于您的代码的画面与您在维基百科中引用的代码的画面不同。你正在使用的是这个:

0   |
1/2 |   1/2
1/2 |   0       1/2
1   |   0       0       1   
-------------------------------------
    |   1/6     1/3     1/3     1/6

维基百科中使用的是

0   |
2/3 |   2/3     
---------------------
    |   1/4     3/4

不同的画面会根据步长产生不同的结果,这是make sure that the step-size is good enough for a certain accuracy所使用的方式。但是,当dt -&gt; 0 时,所有的画面都是一样的。

除此之外,即使对于 RK4,您的代码也是错误的。函数的第二部分应该有一半,而不是0.5*dt

double K1 = dt*dx_dt( t_initial, x_initial );
double K2 = dt*dx_dt( t_initial + halfdt, x_initial + 0.5*K1 );
double K3 = dt*dx_dt( t_initial + halfdt, x_initial + 0.5*K2 );
double K4 = dt*dx_dt( t_initial + dt, x_initial + K3 );

【讨论】:

  • 你的第二个画面是针对二阶 Ralston 方法的,任务显然要求第一个画面的四阶经典龙格-库塔方法。
  • @PeterSM:您还在循环中根据上述变量重新定义了K1,K2,K3,K4,而K 仍未使用。
【解决方案2】:

你犯了一个相当常见的错误,试图过于正确并同时实现算法的两个变体。

应该是

k2 = dt*f(t+0.5*dt, x+0.5*k1)

k2 = f(t+0.5*dt, x+0.5*dt*k1)

相应的其他ks。

请注意,在这两种情况下,斜率 f 只会与 dt 相乘一次。

【讨论】:

    【解决方案3】:

    我认为你包含了太多的增量,并且通过重新排列数学引入了问题。试试这个:

    #include <math.h> 
    #include <iostream>
    #include <iomanip>
    
    static double dx_dt(double t, double x)
    {
        return tan(x) + 1;
    }
    
    int main(int argc, const char * argv[])
    {
        double t = 1.0;
        double t_end = 1.1;
    
        double y = 1.0;
        double h = 0.025;
    
        std::cout << std::setprecision(16);
    
        int n = static_cast<int>((t_end - t) / h);
    
        for (int i = 0; i < n; i++)
        {
            double k1 = dx_dt(t, y);
            double k2 = dx_dt(t + h / 2.0, y + h*k1 / 2.0);
            double k3 = dx_dt(t + h / 2.0, y + h*k2 / 2.0);
            double k4 = dx_dt(t + h, y + h*k3);
    
            y += (k1 + 2 * k2 + 2 * k3 + k4) * h / 6.0;
    
            std::cout << t << ": " << y << std::endl;
    
            t += h;
        }
    
        std::cout << "----------------------------------------------\n";
        std::cout << "t =  " << t << ",  x =  " << y << std::endl;
    
        std::getchar();
    }
    

    我预先计算了迭代的次数,这样可以避免一些不同的问题。同样正如其他人所提到的,维基百科上的工作示例是算法的两阶段变体。

    我冒昧地更改了变量名称以匹配维基百科。一个好的提示是始终匹配参考文本的命名,直到事情起作用。

    【讨论】:

    • 也可以保留原始代码并在最后一步添加截止点if(t_initial+dt &gt; t_final) dt = t_final-t_initial;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-29
    • 1970-01-01
    • 1970-01-01
    • 2017-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多