38
$\开始组$

我的原始ODE是$$\frac{d^2y}{dx^2}+\frac{dy}{dx}-6y=0$$,其中$y(0)=3$,$y'(0)=1$。现在我可以手动求解,得到$y(1)=14.82789927$。然而,我希望使用四阶龙格-库塔方法,所以我有这个系统:

$$\左\{\开始{array}{l}\frac{dy}{dx}=z\\\frac{dz}{dx}=6y-z\右端{数组}。$$其中$y(0)=3$和$z(0)=1$。

现在我知道对于两个一般的一阶常微分方程$$\frac{dy}{dx}=f(x,y,z)\\frac{dz}{dx}=g(x,y,z)$$两个常微分方程组的四阶Runge-Kutta公式是:$$y_{I+1}=y_I+\frac{1}{6}(k_0+2k_1+2k_2+k_3)\\z_{I+1}=z_I+frac{1}}{6{(l_0+2l_1+2l_2+l_3)$$其中$$k_0=hf(x_I,y_I,z_I)\\k_1=hv(x_I+\frac{1}{2} 小时,y_i+\压裂{1}{2} 千0,z_i+\压裂{1}{2} l0(零))k_2=hf(x_i+压裂{1}{2} 小时,y_i+\压裂{1}{2} k_1,z_i+\压裂{1}{2} l_1)k_3=hf(x_i+h,y_i+k_2,z_i+l_2)$$和$$l_0=hg(x_i,y_i,z_i){2} 小时,y_i+\压裂{1}{2} 千0,z_i+\压裂{1}{2} l0(零))l_2=hg(x_i+frac{1}{2} 小时,y_i+\压裂{1}{2} k_1,z_i+\压裂{1}{2} l_1级)l_3=hg(x_i+h,y_i+k_2,z_i+l_2)$$

我的问题是,我正在努力将此方法应用于我的ODE系统,以便我可以编程一种方法,使用上述公式可以求解任何由2个一阶ODE组成的系统,我希望有人能运行该方法的一个步骤,以便我能更好地理解它。

$\端组$
1

5个答案5

重置为默认值
48
$\开始组$

我将概述这个过程,你可以填写计算结果。

我们的系统如下:

$$\左\{\开始{array}{l}\压裂{dy}{dx}=z\\\压裂{dz}{dx}=6y-z\右端{数组}。$$其中$y(0)=3$和$z(0)=1$。

我们必须按照一定的顺序进行计算,因为数值计算之间存在相关性。此订单为:

  • $k_0=hf(x_i,y_i,z_i)$
  • $l_0=hg(x_i,y_i,z_i)$

  • $k_1=hf(x_i+压裂{1}{2} 小时,y_i+\压裂{1}{2} 千0,z_i+\压裂{1}{2} l0(零))$

  • $1=hg(x_i+压裂{1}{2} 小时,y_i+\压裂{1}{2} 千0,z_i+\压裂{1}{2} l0(零))$

  • $k_2=hf(x_i+压裂{1}{2} 小时,y_i+\压裂{1}{2} k_1,z_i+\压裂{1}{2} l_1级)$

  • $l_2=hg(x_i+压裂{1}{2} 小时,y_i+\压裂{1}{2} k_1,z_i+\压裂{1}{2} l_1级)$

  • $k_3=hf(x_i+h,y_i+k_2,z_i+l_2)$

  • $l3=hg(x_i+h,y_i+k_2,z_i+l2)$

  • $y_{i+1}=y_i+\压裂{1}{6}(k_0+2k_1+2k_2+k_3)$

  • $z_{i+1}=z_i+\frac{1}{6}(l0+2l1+2l2+l3)$

我们通常需要算法的一些输入:

  • 我们要计算的范围是:$A\le t\le b$,让我们使用$A=0,b=1$。
  • 步骤数$N$,例如$N=10$。
  • 步骤大小$h=\dfrac{b-a}{N}=\dfras{1}{10}$

我们正在解决的系统是:

$$\frac{dy}{dx}=f(x,y,z)=z\\frac{dz}{dx}=g(x,y,z)=6y-z$$

对第一个时间步长$i=0,t_0=0=x_0$使用上述顺序进行计算,得到:

  • $k_0=hf(x_0,y_0,z_0)=\dfrac{1}{10}(z_0$
  • $l_0=hg(x_0,y_0,z_0)=\dfrac{1}{10}(6y_0-z_0$

  • $k_1=hf(x_0+\压裂{1}{2} 小时,y_0+\压裂{1}{2} 千0,z0+\压裂{1}{2} l_0)=\dfrac{1}{10}(1+\dfrac{1}{2}\dfrac}{1}}{10}(17))~~$(请继续计算。)

  • $1=hg(x_0+\压裂{1}{2} 小时,y_i+\压裂{1}{2} 千0,z0+\压裂{1}{2} l0(零))$

  • $k_2=hf(x_0+\压裂{1}{2} 小时,y_0+\压裂{1}{2} k_1,z0+\压裂{1}{2} l_1级)$

  • $l_2=汞(x_0+\压裂{1}{2} 小时,y_0+\压裂{1}{2} k_1,z0+\压裂{1}{2} l_1级)$

  • $k_3=hf(x_0+h,y_0+k_2,z_0+l_2)$

  • $l_3=汞(x_0+h,y_0+k_2,z_0+l_2)$

  • $y_{1}=y_0+\压裂{1}{6}(k_0+2k_1+2k_2+k_3)$

  • $z{1}=z_0+\压裂{1}{6}(l0+2l1+2l2+l3)$

现在您有$x_1$和$z_1$,在所有中间步骤之后的下一个时间步骤中需要它们(再次按顺序)。现在,我们进入下一个时间步骤$i=1,t_1=t_0+h=\dfrac{1}{10}=x_1$,因此我们有:

  • $k_0=hf(x_1,y_1,z_1)=\dfrac{1}{10}(z_1$
  • $l_0=hg(x_1,y_1,z_1)=\dfrac{1}{10}(6y_1-z_1$

  • $k_1=hf(x_1+\压裂{1}{2} 小时,y_1+\压裂{1}{2} k_0,z_1+\压裂{1}{2} l0(零))$

  • $1=hg(x_1+\压裂{1}{2} 小时,y1+\压裂{1}{2} 千0,z_1+\压裂{1}{2} l0(零))$

  • $k_2=hf(x_1+\压裂{1}{2} 小时,y1+\压裂{1}{2} k_1,z_1+\frac{1}{2} l_1级)$

  • $l_2=汞(x_1+\压裂{1}{2} 小时,y1+\压裂{1}{2} k_1,z_1+\压裂{1}{2} l_1级)$

  • $k_3=hf(x_1+h,y_1+k_2,z_1+l_2)$

  • $l3=汞(x_1+h,y_1+k_2,z_1+l_2)$

  • $y_{2}=y_1+\压裂{1}{6}(k_0+2k_1+2k_2+k_3)$

  • $z{2}=z_1+\压裂{1}{6}(l0+2l1+2l2+l3)$

继续进行$10$时间步。您的最终结果应该与精确解紧密匹配(假设此问题的数值算法是稳定的)。您将$z_{10}$与精确结果进行比较。确切的解决方案是:

$$y(x)=e^{-3x}+2e^{2x}$$

如果我们找到$y(1)=\dfrac{1}{e^3}+2e^2=14.8278992662291643974401973…$。

$\端组$
7
  • $\开始组$ 谢谢你的全面回应,看到它的开始,我现在更好地理解了。谢谢! $\端组$
    – 迈克尔
    评论 2014年3月21日15:49
  • 1
    $\开始组$ 回想一下,在您的新系统中,为了使用RK4方法,第一个方程$y'=z$只是一个虚拟变量。当做 $\端组$ 评论 2014年3月21日15:53
  • 1
    $\开始组$ @迈克尔:还有,当你计算$y_i,z_i$时,你会清楚地看到,这是正确的最终结果。 $\端组$ 评论 2014年3月21日16:02
  • 1
    $\开始组$ 延迟回复,但$y_i$或$z_i$是原始ODE的解决方案吗?比较一下这些值,结果似乎是由$y_i$给出的,但我不确定。 $\端组$ 评论 2016年5月4日15:47
  • 1
    $\开始组$ @ErikVesterlund如果我做对了,那么z是y导数的解,y是原始ODE的解 $\端组$
    – 灵芝
    评论 2017年1月22日16:11
21
$\开始组$

虽然这个答案与Amzoti的答案包含相同的内容,但我认为从另一个角度来看是值得的。

一般来说,如果你百万美元$一阶常微分方程(适当分解后)。系统看起来像

\开始{align*}\裂缝{dy1}{dx}&=f1(x,y1,\ldots,ym)\\\裂缝{dy2}{dx}&=f2(x,y1,\ldots,ym)\\&\、\、\、\vdots\\\裂缝{dym}{dx}&=fm(x,y1,\ldots,ym)\\\结束{align*}

定义向量$\vec{Y}=(Y_1,\ldots,Y_m)$$\vec{f}=(f1,\ldots,f_m)$,那么我们可以将系统写为

$$\frac{d}{dx}\vec{Y}=\vec{f}(x,\vec{Y})$$

现在我们可以通过定义\开始{align*}\血管内皮细胞{k} _1个&=h\vec{f}\左(x_n,\vec}(x_n)\右)\\\血管内皮细胞{k} _2&=h\vec{f}\左(x_n+\tfrac{1}{2} 小时,\vec{Y}(x_n)+\tfrac{1}{2}\vec{k} _1个\右侧)\\\向量{k} _3个&=h\vec{f}\左(x_n+\tfrac{1}{2} 小时,\vec{Y}(x_n)+\tfrac{1}{2}\vec{k} _2\右侧)\\\血管内皮细胞{k} _4个&=h\vec{f}\左(x_n+h,\vec}Y}(x_n)+\vec{k} _3个\右侧)\结束{align*}

然后通过以下公式给出解决方案$$\vec{Y}(x_{n+1})=\ vec{Y}(x_n)+\tfrac{1}{6}\左(\vec{k} _1个+2\vec{k} _2+2\vec{k} _3个+\vec{k} _4个\右侧)$$

具有百万美元$由指定的初始条件$\vec{Y}(x_0)$。当编写代码来实现此功能时,可以简单地使用数组,并编写函数来计算$\vec{f}(x,\vec}Y})$

对于提供的示例,我们有$\vec{Y}=(Y,z)$$\vec{f}=(z,6y-z)$。下面是Fortran90中的一个示例:

程序RK4隐式无整数,参数::dp=kind(0.d0)整数,参数::m=2!ODE的顺序实数(dp)::Y(m)实数(dp)::a,b,x,h整数::N,i! 步骤数N=10! 首字母xa=0x=a! 最终xb=1! 步长h=(b-a)/否! 初始条件Y(1)=3!y(0)Y(2)=1!y’(0)! 迭代N次do i=1,NY=迭代(x,Y)x=x+h结束do打印*,Y包含! 函数f计算向量f函数f(x,Yvec)结果(fvec)实数(dp)::x实数(dp)::Yvec(m),fvec(m)fvec(1)=Yvec(2)!z(z)fvec(2)=6*Yvec(1)-Yvec!6y-z型末端函数! 函数迭代计算Y(t_n+1)函数迭代(x,Y_n)结果(Y_nplus1)实数(dp)::x实数(dp)::Y_n(m),Y_nplus1(m)实(dp)::k1(m),k2(m)、k3(m)和k4(m)k1=h*f(x,Y_n)k2=h*f(x+h/2,Y_n+k1/2)k3=h*f(x+h/2,Y_n+k2/2)k4=h*f(x+h,Y_n+k3)Y_nplus1=Y_n+(k1+2*k2+2*k3+k4)/6end函数结束程序

这可以应用于任何一组百万美元$一阶常微分方程,只需更改在代码中并更改函数(f)适用于利益体系的任何内容。按-is运行此代码会产生

14.827578509968953美元\qquad 29.406156886687729$

第一个值是美元(1)$,第二个$z(1)$,只需十步就可修正到小数点后三位。

$\端组$
5
  • 1
    $\开始组$ 您应该使用$x{n}$而不是$t{n}$ $\端组$ 评论 2018年11月3日20:39
  • $\开始组$ 接得好,修好了 $\端组$
    – 
    评论 2018年11月4日0:20
  • 1
    $\开始组$ 神奇的答案@Kai+1。如果可能的话,会给+50!许多人与ODE和RK方法体系斗争。不过,关于您的Fortran实现,我有一个问题。如果你想变花样,可以使用for循环正确地编写$k_i$?基本上是将它们放在数组中?那么你会有一个数组$k(i,n)$,其中i是阶段数,n是状态向量的维数?您知道在Fortran中有这样的文档吗?我现在正在写类似的东西,有点困惑!! $\端组$ 评论 2019年2月10日0:03
  • $\开始组$ 我注意到函数没有明确的依赖性(f)关于变量x个这是因为我们用来降低导数阶数的代换吗?这种依赖x个算法通过其他变量自动拾取? $\端组$ 评论 2022年6月10日10:31
  • 1
    $\开始组$ @原始问题中的疯狂物理学家ODE没有明确的$x$依赖性,它只隐含在$y(x)$和$z(x)$。当然,您可以有明确的$x$依赖性 $\端组$
    – 
    评论 2022年6月10日14:49
8
$\开始组$

Matlab实现如下所示:

%它使用Runge-Kutta四阶方法计算ODE%作者Ido Schwartz%最初可用的形式:http://www.mathworks.com/matlabcentral/fileexchange/29851-runge-kutta-4th-order-ode/content/runge_kutta_4.m%阿明·A·穆罕默德(Amin A.Mohammed)主编,2部ODE(2016年4月)分类代码;%清除屏幕清除所有;h=0.1;%步长x=0:h:1;%最多计算y(1)y=零(1,长度(x));z=零(1,长度(x));y(1)=3;%初始条件z(1)=1;%初始条件%F_xy=@(t,r)3.*exp(-t)-0.4*r;%根据需要更改功能F_xyz=@(x,y,z)z;%根据需要更改功能G_xyz=@(x,y,z)6*y-z;i=1时:(长度(x)-1)%计算循环k_1=F_xyz(x(i)、y(i)和z(i));L_1=G_xyz(x(i),y(i)和z(i));k_2=F_xyz(x(i)+0.5*h,y(i)=0.5*h*k_1,z(i)+0.5*h*L_1);L_2=G_xyz(x(i)+0.5*h,y(i)=0.5*h*k_1,z(i)+0.5*h*L_1);k_3=F_xyz((x(i)+0.5*h),(y(i)=0.5*h*k_2),(z(i)+0.5*h*L_2));L_3=G_xyz((x(i)+0.5*h),(y(i)=0.5*h*k_2),(z(i)+0.5*h*L_2));k_4=F_xyz((x(i)+h),(y(i)+k_3*h)已更正L_4=G_xyz((x(i)+h),(y(i)+k_3*h);y(i+1)=y(i)+(1/6)*(k_1+2*k_2+2*k_3+k_4)*h;%主方程z(i+1)=z(i)+(1/6)*(L_1+2*L_2+2*L_3+L_4)*h;%主方程结束
$\端组$
0
$\开始组$

A类Fortran代码如下所示:

在此处输入图像描述

生成以下内容结果

输出

!Runge-Kutta四阶方法!对于二阶微分方程!首先必须定义函数F(x,y,z)=z!年月日G(x,y,z)=6*y-z!dz/dx=d2y/dx2整数::n,i真实::k1,l1,k2,l2,k3,l3,k4,l4!最重要的写下(*,*)“给定方程'(y2)-6(y1)+(y0)=0'”写入(*,*)“Xo=0,Yo=3,Zo=Y'o=1,Xn=1,n=?”Xo=0!给定条件Yo=3!给定条件Zo=1!给定条件Xn=1!给定条件读取(*,*)n!n=截距数h=(Xn-Xo)/n做i=1,n!您必须进行“n”次计算k1=h*F(Xo,Yo,Zo)l1=h*G(Xo,Yo,Zo)k2=h*F(Xo+h/2,Yo+k1/2,Zo+l1/2)l2=h*G(Xo+h/2,Yo+k1/2,Zo+l1/2)k3=h*F(Xo+h/2,Yo+k2/2,Zo+l2/2)l3=h*G(Xo+h/2,Yo+k2/2,Zo+l2/2)k4=h*F(Xo+h,Yo+k3,Zo+l3)l4=h*G(Xo+h,Yo+k3,Zo+l3)!总结Yn=Yo+(k1+2*k2+2*k3+k4)/6Zn=Zo+(l1+2*l2+2*l3+l4)/6!下一次计算的操作Xo=Xo+h!(+h)比上一学期Yo=Yn!现在Yn变成YoZo=锌!现在Zn变成了Zo结束Do写入(*,*)“Xn,Yn=”,Xo,Yo停止终点
$\端组$
1
  • $\开始组$ 你能澄清一下你的问题吗? $\端组$
    – 丹托帕
    评论 2019年1月31日19:39
0
$\开始组$

Rust实现如下:

常数M:usize=2;//系统的大小fn主(){设mut y:[f64;M]=[0.0;M];设muta:f64=0.0;设mut x:f64=a;设mut b:f64=0.0;设n:usize=10;设h:f64=(b-a)/n为f64;//初始条件y[0]=3.0;//y(0)y[1]=1.0;//y’(0)对于0..n中的_i{y=rk4_步长(x,y,h,&导数);x+=小时;}打印!(“{:?}”,y);}//定义方程组的导数fn导数(x:f64,y:[f64;M])->[f64,M]{设mut dy_dx:[f64;M]=[0.0;M];dy_dx[0]=y[1];//dy/dx=zdy_dx[1]=6.0*y[0]-y[1];//dz/dx=6y-zdy_dx(发送日期)}//执行RK4的一步fn rk4_步长(x:f64,y_n:[f64;M],h:f64{设mut k1:[f64;M]=[0.0;M];设mut k2:[f64;M]=[0.0;M];设mut k3:[f64;M]=[0.0;M];设mut k4:[f64;M]=[0.0;M];设mut y_n_plus_1:[f64;M]=[0.0;M];k1=比例vec(&f(x,y_n),h);k2=比例vec(&f(x+h/2.0,添加vec(y_n,&scalevec(k1,0.5))),h);k3=比例vec(&f(x+h/2.0,添加vec(y_n,&scalevec(k2,0.5))),h);k4=缩放向量(&f(x+h,add_vec(&y_n,&k3)),h);y_n_plus_1=添加vec(&y_n,&缩放vec(&添加vec(&k1,&addvec(&缩放vec(-k2,2.0),&addvesc(&scalevec(k3,2.0)和&k4))),1.0 / 6.0,),);y_n加1}//Helper函数:元素向量加法fn add_vec(a:&[f64;M],b:&[f64;M])->[f64;M]{设mut结果:[f64;M]=[0.0;M];对于0…M中的i{结果[i]=a[i]+b[i];}结果}//Helper函数:向量的元素级标量乘法fn scale_vec(a:&[f64;M],标量:f64)->[f64;M]{让多个结果:[f64;M]=[0.0;M];对于0…M中的i{结果[i]=a[i]*标量;}结果}
$\端组$
1
  • $\开始组$ 您的答案可以通过其他支持信息加以改进。拜托编辑添加更多详细信息,例如引文或文档,以便其他人可以确认您的答案是否正确。你可以找到更多关于如何写出好答案的信息在帮助中心. $\端组$
    – 社区 机器人程序
    评论 2023年12月4日13:25

你必须登录来回答这个问题。

不是你想要的答案吗?浏览标记的其他问题.