经过几次迭代后,四阶Runge-Kutta方法(RK4)崩溃

时间:2013-05-27 19:24:21

标签: matlab differential-equations integral runge-kutta

我正在努力解决:

x' = 60*x - 0.2*x*y;
y' = 0.01*x*y - 100* y;

使用四阶Runge-Kutta算法。

起点:x(0) = 8000, y(0) = 300范围:[0,15]

这是完整的功能:

function [xx yy time r] = rk4_m(x,y,step)
A = 0;
B = 15;

h = step;
iteration=0;
t = tic;

xh2 = x;
yh2 = y;


rr = zeros(floor(15/step)-1,1);
xx = zeros(floor(15/step)-1,1);
yy = zeros(floor(15/step)-1,1);
AA = zeros(1, floor(15/step)-1);

while( A < B)


    A = A+h;
    iteration = iteration + 1;

    xx(iteration) = x;
    yy(iteration) = y;
    AA(iteration) = A;
    [x y] = rkstep(x,y,h);


    for h2=0:1
        [xh2 yh2] = rkstep(xh2,yh2,h/2);
    end
    r(iteration)=abs(y-yh2);



end
time = toc(t);

xlabel('Range');
ylabel('Value');     
hold on
plot(AA,xx,'b');
plot(AA,yy,'g');
plot(AA,r,'r');
fprintf('Solution:\n');
fprintf('x: %f\n', x);
fprintf('y: %f\n', y);
fprintf('A: %f\n', A);
fprintf('Time: %f\n', time);

end

function [xnext, ynext] = rkstep(xcur, ycur, h)
    kx1 = f_prim_x(xcur,ycur);
    ky1 = f_prim_y(xcur,ycur);

    kx2 = f_prim_x(xcur+0.5*h,ycur+0.5*h*kx1);
    kx3 = f_prim_x(xcur+0.5*h,ycur+0.5*h*kx2);
    kx4 = f_prim_x(xcur+h,ycur+h*kx3);

    ky2 = f_prim_y(xcur+0.5*h*ky1,ycur+0.5*h);
    ky3 = f_prim_y(xcur+0.5*h*ky2,ycur+0.5*h);
    ky4 = f_prim_y(xcur+h*ky2,ycur+h);

    xnext = xcur + (1/6)*h*(kx1 + 2*kx2 + 2*kx3 + kx4);
    ynext = ycur + (1/6)*h*(ky1 + 2*ky2 + 2*ky3 + ky4);
end

function [fx] = f_prim_x(x,y)
fx = 60*x - 0.2*x*y;
end

function [fy] = f_prim_y(x,y)
fy = 0.01*x*y - 100*y;
end

我正在通过执行:[xx yy time] = rk4_m(8000,300,10)

来运行它

问题是,在2-3次迭代后,一切都会崩溃,导致无用的结果。我究竟做错了什么?或者只是这种方法不适合这种方程?

有意省略分号。


看起来我没注意实际的h尺寸。它现在有效!谢谢!

2 个答案:

答案 0 :(得分:2)

看起来像Lotka-Volterra等式的某种形式?

我不确定您的初始条件是[300;8000]还是[8000;300](您在上面指定了两种方式),但无论如何,您都有一个振荡系统,您正试图与之积分大的固定时间步长(大大)大于振荡周期。这就是你的错误爆炸的原因。如果您尝试增加n(例如1e6),您会发现最终会得到一个稳定的解决方案(假设您的Runge-Kutta实现方式正确无误)。

您是否有理由不使用Matlab的内置ODE求解器,例如: ode45ode15s

function ode45demo

[t,y]=odeode45(@f,[0 15],[300;8000]);

figure;
plot(t,y);

function ydot=f(t,y)
ydot(1,1) = 60*y(1) - 0.2*y(1)*y(2);
ydot(2,1) = 0.01*y(1)*y(2) - 100*y(2);

您会发现自适应步长求解器对于这些类型的振荡问题更有效。由于您的系统频率非常高并且看起来相当僵硬,因此我建议您同时查看ode15s给出的内容和/或使用odeset调整'AbsTol''RelTol'选项

答案 1 :(得分:1)

当前的问题是RK4代码没有从标量情况完全演化为两个耦合方程式的情况。请注意,导数功能中没有时间参数。 xy都是因变量,因此可以在每个步骤中获得由导数函数定义的斜率更新。然后xcur获得kx更新,ycur获得ky更新。

function [xnext, ynext] = rkstep(xcur, ycur, h)
    kx1 = f_prim_x(xcur,ycur);
    ky1 = f_prim_y(xcur,ycur);

    kx2 = f_prim_x(xcur+0.5*h*kx1,ycur+0.5*h*ky1);
    ky2 = f_prim_y(xcur+0.5*h*kx1,ycur+0.5*h*ky1);

    kx3 = f_prim_x(xcur+0.5*h*kx2,ycur+0.5*h*ky2);
    ky3 = f_prim_y(xcur+0.5*h*kx2,ycur+0.5*h*ky2);

    kx4 = f_prim_x(xcur+h*kx3,ycur+h*ky3);
    ky4 = f_prim_y(xcur+h*kx3,ycur+h*ky3);

    xnext = xcur + (1/6)*h*(kx1 + 2*kx2 + 2*kx3 + kx4);
    ynext = ycur + (1/6)*h*(ky1 + 2*ky2 + 2*ky3 + ky4);
end