提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
在微分方程的数值求解当中,四阶龙格-库塔法(Runge-Kutta methods)是用于非线性常微分方程的解的重要的一类隐式或显式迭代法。本文以2022年高教杯数学建模A题为例,详细讲解四阶龙格-库塔法的原理及其python实现。
一、原理
假设我们求解以下微分方程
{
d
y
d
t
=
f
(
y
,
t
)
y
(
0
)
=
y
0
\left\{\begin{aligned}&\frac{dy}{dt}=f(y,t) \\&y(0)=y_0\end{aligned}\right.
⎩
⎨
⎧dtdy=f(y,t)y(0)=y0
由中值定理可得
y
n
+
1
=
y
n
+
f
(
y
ε
,
t
ε
)
Δ
t
y_{n+1}=y_{n}+f(y_{\varepsilon },t_{\varepsilon })\Delta t
yn+1=yn+f(yε,tε)Δt
其中,
f
(
y
ε
,
t
ε
)
f(y_{\varepsilon },t_{\varepsilon })
f(yε,tε) 为区间
(
t
n
,
t
n
+
1
)
(t_n,t_{n+1})
(tn,tn+1) 上的平均斜率。四阶龙格-库塔法的基本思想,就是找一个容易计算且与
f
(
y
ε
,
t
ε
)
f(y_{\varepsilon },t_{\varepsilon })
f(yε,tε) 相差极小的值来代替
f
(
y
ε
,
t
ε
)
f(y_{\varepsilon },t_{\varepsilon })
f(yε,tε) 进行运算。
四阶龙格-库塔法的表达式为
y
n
+
1
=
y
n
+
Δ
t
6
(
k
1
+
2
k
2
+
2
k
3
+
k
4
)
y_{n+1}=y_{n}+\frac{\Delta t}{6}(k_{1}+2k_{2}+2k_{3}+k_{4})
yn+1=yn+6Δt(k1+2k2+2k3+k4)
其中
k
1
=
f
(
y
n
,
t
n
)
k
2
=
f
(
y
n
+
k
1
Δ
t
2
,
t
n
+
Δ
t
2
)
k
3
=
f
(
y
n
+
k
2
Δ
t
2
,
t
n
+
Δ
t
2
)
k
4
=
f
(
y
n
+
k
3
Δ
t
,
t
n
+
Δ
t
)
\begin{aligned} &k_1=f(y_n,t_n) \\ &k_2=f\left(y_n+k_1\frac {\Delta t}2,t_n+\frac {\Delta t}2\right) \\ &k_3=f\left(y_n+k_2\frac {\Delta t}2,t_n+\frac {\Delta t}2\right) \\ &k_4=f\left(y_n+k_3\Delta t,t_n+\Delta t\right) \end{aligned}
k1=f(yn,tn)k2=f(yn+k12Δt,tn+2Δt)k3=f(yn+k22Δt,tn+2Δt)k4=f(yn+k3Δt,tn+Δt)
四阶龙格-库塔法的总体截断误差:
∝
Δ
t
4
\propto\Delta t^4
∝Δt4
二、示例
2022年高教杯数学建模A题求解问题1.(1)的微分方程为(推导过程略)
(
m
f
+
m
a
)
d
2
z
f
d
t
2
=
f
cos
ω
t
−
c
a
d
z
f
d
t
−
k
f
z
f
−
c
p
(
d
z
f
d
t
−
d
z
v
d
t
)
−
k
s
(
z
f
−
z
v
)
,
m
v
d
2
z
v
d
t
2
=
−
c
p
(
d
z
v
d
t
−
d
z
f
d
t
)
−
k
s
(
z
v
−
z
f
)
,
\begin{aligned} (m_{f}+m_{a})\frac{\mathrm{d}^{2}z_{f}}{\mathrm{d}t^{2}}& =f\cos\omega t-c_{a}\frac{\mathrm{d}z_{f}}{\mathrm{d}t}-k_{f}z_{f}-c_{p}\left(\frac{\mathrm{d}z_{f}}{\mathrm{d}t}-\frac{\mathrm{d}z_{v}}{\mathrm{d}t}\right)-k_{s}(z_{f}-z_{v}), \\ m_v\frac{\mathrm{d}^2z_v}{\mathrm{d}t^2}& =-c_p\left(\frac{\mathrm{d}z_v}{\mathrm{d}t}-\frac{\mathrm{d}z_f}{\mathrm{d}t}\right)-k_s(z_v-z_f), \end{aligned}
(mf+ma)dt2d2zfmvdt2d2zv=fcosωt−cadtdzf−kfzf−cp(dtdzf−dtdzv)−ks(zf−zv),=−cp(dtdzv−dtdzf)−ks(zv−zf),
初始条件为
t
=
0
:
z
f
(
0
)
=
z
v
(
0
)
=
d
z
f
d
t
(
0
)
=
d
z
v
d
t
(
0
)
=
0.
t=0:\quad z_f(0)=z_v(0)=\frac{\mathrm{d}z_f}{\mathrm{d}t}(0)=\frac{\mathrm{d}z_v}{\mathrm{d}t}(0)=0.
t=0:zf(0)=zv(0)=dtdzf(0)=dtdzv(0)=0.
将其化为一阶常微分方程
d
z
1
d
t
=
v
1
,
d
v
1
d
t
=
1
m
f
+
m
a
[
f
cos
ω
t
−
c
a
v
1
−
k
f
z
1
−
c
p
(
v
1
−
v
2
)
−
k
s
(
z
1
−
z
2
)
]
,
d
z
2
d
t
=
v
2
,
d
v
2
d
t
=
−
1
m
v
[
c
p
(
v
2
−
v
1
)
+
k
s
(
z
2
−
z
1
)
]
.
\begin{aligned} &\frac{\mathrm{d}z_1}{\mathrm{d}t} =v_{1}, \\ &\frac{\mathrm{d}v_1}{\mathrm{d}t} =\frac1{m_f+m_a}[f\cos\omega t-c_av_1-k_fz_1-c_p(v_1-v_2)-k_s(z_1-z_2)], \\ &\frac{\mathrm{d}z_2}{\mathrm{d}t} =v_2, \\ &\frac{\mathrm{d}v_2}{\mathrm{d}t} =-\frac1{m_v}[c_p(v_2-v_1)+k_s(z_2-z_1)]. \end{aligned}
dtdz1=v1,dtdv1=mf+ma1[fcosωt−cav1−kfz1−cp(v1−v2)−ks(z1−z2)],dtdz2=v2,dtdv2=−mv1[cp(v2−v1)+ks(z2−z1)].
根据四阶龙格-库塔法,我们可以列出
z
1
z_1
z1 的递推公式
z
1
n
+
1
=
z
1
n
+
Δ
t
6
(
k
1
+
2
k
2
+
2
k
3
+
k
4
)
z1_{n+1}=z1_{n}+\frac{\Delta t}{6}(k_{1}+2k_{2}+2k_{3}+k_{4})
z1n+1=z1n+6Δt(k1+2k2+2k3+k4)
其中
f
(
v
1
v
)
=
v
1
n
g
(
z
1
n
,
v
1
n
,
z
2
n
,
v
2
n
,
t
n
)
=
1
m
f
+
m
a
[
f
cos
ω
t
−
c
a
v
1
n
−
k
f
z
1
n
−
c
p
(
v
1
n
−
v
2
n
)
−
k
s
(
z
1
n
−
z
2
n
)
]
\begin{aligned} &f(v1_v)=v1_n\\ &g\left(z1_n,v1_n,z2_n,v2_n,t_n\right)=\frac1{m_f+m_a}[f\cos\omega t-c_av1_n-k_fz1_n-c_p(v1_n-v2_n)-k_s(z1_n-z2_n)] \end{aligned}
f(v1v)=v1ng(z1n,v1n,z2n,v2n,tn)=mf+ma1[fcosωt−cav1n−kfz1n−cp(v1n−v2n)−ks(z1n−z2n)]
k
1
=
f
(
v
1
n
)
k
2
=
f
(
v
1
n
+
l
1
Δ
t
2
)
k
3
=
f
(
v
1
n
+
l
2
Δ
t
2
)
k
4
=
f
(
v
1
n
+
l
3
Δ
t
)
\begin{aligned} &k_1=f\left(v1_n\right) \\ &k_2=f\left(v1_n+l_1\frac {\Delta t}2\right) \\ &k_3=f\left(v1_n+l_2\frac {\Delta t}2\right)\\ &k_4=f\left(v1_n+l_3\Delta t\right) \end{aligned}
k1=f(v1n)k2=f(v1n+l12Δt)k3=f(v1n+l22Δt)k4=f(v1n+l3Δt)
l 1 , l 2 , l 3 l_1,l_2,l_3 l1,l2,l3 是 v 1 v1 v1 的增长率,由 g ( z 1 , v 1 , z 2 , v 2 , t ) g\left(z1,v1,z2,v2,t\right) g(z1,v1,z2,v2,t) 得到。同理,我们可以得到其它三个变量的递推公式。
四阶龙格-库塔法写出各变量的递推式后,我们可以开始编辑python代码。以下是一个示例
import numpy as np
import matplotlib.pyplot as pl
pl.rcParams['font.sans-serif'] = ['SimHei']
pl.rcParams['axes.unicode_minus'] = False
# 相关参数
omega = 1.4005
mf = 4866
ma = 1335.535
mv = 2433
f = 6250
ca = 656.3616
kf = 1025 * 9.8 * np.pi * 1 ** 2
cp = 10000
ks = 80000
# 步长和迭代次数
dt = 0.2
n = 600
# 初始条件
z1 = 0
z2 = 0
v1 = 0
v2 = 0
t = 0
z1_list = [0]
z2_list = [0]
v1_list = [0]
v2_list = [0]
t_list = [0]
# v1的增长率
def function1(z1,v1,z2,v2,t):
dv1 = 1/(mf+ma)*(f*np.cos(omega*t)-ca*v1-kf*z1-cp*(v1-v2)-ks*(z1-z2))
return dv1
# v2的增长率
def function2(z1,v1,z2,v2,t):
dv2 = -1/mv*(cp*(v2-v1)+ks*(z2-z1))
return dv2
# 四阶龙格-库塔法
for i in range(n):
dz1_1 = v1
dz2_1 = v2
dv1_1 = function1(z1,v1,z2,v2,t)
dv2_1 = function2(z1,v1,z2,v2,t)
dz1_2 = v1 + dv1_1 * dt / 2
dz2_2 = v2 + dv2_1 * dt / 2
dv1_2 = function1(z1 + dz1_1 * dt / 2, v1 + dv1_1 * dt / 2, z2 + dz2_1 * dt / 2, v2 + dv2_1 * dt / 2, t + dt / 2)
dv2_2 = function2(z1 + dz1_1 * dt / 2, v1 + dv1_1 * dt / 2, z2 + dz2_1 * dt / 2, v2 + dv2_1 * dt / 2, t + dt / 2)
dz1_3 = v1 + dv1_2 * dt / 2
dz2_3 = v2 + dv2_2 * dt / 2
dv1_3 = function1(z1 + dz1_2 * dt / 2, v1 + dv1_2 * dt / 2, z2 + dz2_2 * dt / 2, v2 + dv2_2 * dt / 2, t + dt / 2)
dv2_3 = function2(z1 + dz1_2 * dt / 2, v1 + dv1_2 * dt / 2, z2 + dz2_2 * dt / 2, v2 + dv2_2 * dt / 2, t + dt / 2)
dz1_4 = v1 + dv1_3 * dt
dz2_4 = v2 + dv2_3 * dt
dv1_4 = function1(z1 + dz1_3 * dt, v1 + dv1_3 * dt, z2 + dz2_3 * dt, v2 + dv2_3 * dt, t + dt)
dv2_4 = function2(z1 + dz1_3 * dt, v1 + dv1_3 * dt, z2 + dz2_3 * dt, v2 + dv2_3 * dt, t + dt)
z1 = z1 + dt / 6 * (dz1_1 + 2 * dz1_2 + 2 * dz1_3 + dz1_4)
z2 = z2 + dt / 6 * (dz2_1 + 2 * dz2_2 + 2 * dz2_3 + dz2_4)
v1 = v1 + dt / 6 * (dv1_1 + 2 * dv1_2 + 2 * dv1_3 + dv1_4)
v2 = v2 + dt / 6 * (dv2_1 + 2 * dv2_2 + 2 * dv2_3 + dv2_4)
t = t + dt
z1_list.append(z1)
z2_list.append(z2)
v1_list.append(v1)
v2_list.append(v2)
t_list.append(t)
print(t_list[50],z1_list[50],v1_list[50],z2_list[50],v2_list[50])
print(t_list[100],z1_list[100],v1_list[100],z2_list[100],v2_list[100])
print(t_list[200],z1_list[200],v1_list[200],z2_list[200],v2_list[200])
print(t_list[300],z1_list[300],v1_list[300],z2_list[300],v2_list[300])
print(t_list[500],z1_list[500],v1_list[500],z2_list[500],v2_list[500])
fig = pl.figure(figsize=(10,4))
ax1 =fig.add_subplot(1,2,1)
ax2 =fig.add_subplot(1,2,2)
ax1.plot(t_list, z1_list, 'b-', label='浮子',linewidth=1.0)
ax2.plot(t_list, z2_list, 'r-', label='振子',linewidth=1.0)
pl.subplots_adjust(left=0.15,bottom=0.1,top=0.9,right=0.95, \
hspace=0.35,wspace=0.3)
ax1.set_ylabel('z/m')
ax1.set_xlabel('T/s')
ax1.set_xlim(0,120)
ax1.set_ylim(-1.0,1.0)
ax2.set_ylabel('z/m', )
ax2.set_xlabel('T/s')
ax2.set_xlim(0,120)
ax2.set_ylim(-1.0,1.0)
ax1.legend(loc='upper right')
ax2.legend(loc='upper right')
pl.show()
结果展示:
时间(s) | 浮子位移(m) | 浮子速度(m/s) | 振子位移(m) | 振子速度(m/s) |
---|---|---|---|---|
10 | -0.1905 | -0.6429 | -0.2114 | -0.6959 |
20 | -0.59052 | -0.2432 | -0.6339 | -0.2753 |
40 | 0.2859 | 0.3115 | 0.2970 | 0.3313 |
60 | -0.3143 | -0.4807 | -0.3311 | -0.5171 |
100 | -0.0835 | -0.6048 | -0.0839 | -0.6435 |
(顺带吐槽一下,很多数学建模的优秀范文都没有考虑到“有效位数”,按照题目给出的数据,最终得到的结果根本达不到他们给出的那个精度。)