python微分求解
这里用简单函数
假设我们现在知道原函数 \(f(x)=x^2\)
那么二阶微分方程为: \[f''(x)+f'(x)+(-2-2\times x) = 0\] 初始条件 \[f(0)=0 \quad f'(0)=0\]
先看代码
1 | import matplotlib.pyplot as plt |
解析式求解
使用 sympy
库 dsolve
函数
微分方程在 eq_test
函数里,其中 3 个注释掉了,分别是
\[f''(x)+sin(x)=0 \quad f''(x)+x^2=0 \quad f''(x)+f(x)=0\]
1 | import sympy as sy |
使用函数 sympy
,eq_test
函数即为微分方程,sy.pprint
可以美化输出,print(sy.printing.latex(s))
可以输出为 latex 代码
解析解:
\[\displaystyle f{\left(x \right)} = C_{1} + C_{2} e^{- x} + x^{2}\]
数值求解
scipy
里内置函数 odeint, solve_ivp
两个都可以,区别如下
函数 | 作用 | 速度 | 区别 |
---|---|---|---|
odeint | 微分方程数值求解 | 快 | 参数简洁,推荐 |
solve_ivp | 如果使用 method='LSODA' 与 odeint 一样 | 相对慢一些 | 通用,含多个计算方式 |
1 | import matplotlib.pyplot as plt |
odeint 函数
odeint
函数一般只需要传递 3 个参数
微分方程矩阵函数、自变量、初始值
1 | odeint(fvdp, y0, t, tfirst=True) |
微分方程矩阵函数
对于高阶,需要构造微分方程函数 fvdp
,即最开始提到的方程为
\[ f''(x)+f'(x)+(-2-2x) = 0 \quad f(0)=0 \quad f'(0)=0 \]
要把这个二阶方程全部转化为矩阵
\[[y', y'']\]
即 fvdp
函数的返回值,这个函数的参数为 t
自变量,y
因变量的导数矩阵 \([y, y']\)
odeint
函数的参数顺序(y, t)
,如果要与solve_ivp
一致参数顺序变成(t, y)
,odeint
函数多传递一个参数tfirst=True
所以,返回值 \(y'=y[1]\),y''
用微分方程 (\(f''(x)+f'(x)+(-2-2x) = 0\)) 表示
\(y''=-y[1]+2+2t\)
最终 fvdp
函数返回值
\[[y', y'']=[y[1], -y[1]+2+2t]\]
自变量
用 numpy 数组表示即可 t = np.linspace(0, 5, 1000)
初始值
注意与自变量对应,二阶微分方程初始值为
\[[f(t_0), f'(t_0)]\]
这里的\(t_0\)必须是自变量的第一个值,不然会有很大的误差
返回值
odeint
函数返回值只有 ys=\([[y], [y']]\) ,但是注意转置一下,行列不太方便,如果不转置
\(y=ys[:, 0] \qquad y'=ys[:, 1]\)
solve_ivp 函数
1 | y_ = solve_ivp(fvdp, t_span=tspan, method="LSODA", y0=y0, t_eval=t) |
微分方程矩阵函数
solve_ivp
不一样的仅仅只有参数顺序,这个函数参数应该是 (t, y)
与 matlab 是一致的,
- 自变量
这个也有区别,自变量数组在 t_eval=t
,但是还需要一个 t_span=(min, max)
,其中 max min
为自变量数组的最大值最小值,如果只有t_span
,取得值太少,不行
- 初始值
与 odeint
一致
- 返回值
ys = y_.y
\(y=ys[0] \qquad y'=ys[1]\)
总结
还是用 odeint
吧,参数少一些,方便
其他
solve_ivp 积分:
详细看官网文档,包含各种方法的具体参考文献
'RK45'(默认):显式 Runge-Kutta 5(4)阶。假设四阶方法的精度控制误差,但使用五阶精确公式(完成局部外推)采取步骤。四次插值多项式用于密集输出[2]。可应用于复杂领域。
'RK23':显式 Runge-Kutta (2)3 阶。假设二阶方法的精度,控制误差,但使用三阶精确公式(完成局部外推)采取步骤。三次 Hermite 多项式用于密集输出。可应用于复杂领域。
'DOP853':8 阶显式 Runge-Kutta。最初用 Fortran 编写的“DOP853”算法的 Python 实现。精确到 7 阶的 7 阶插值多项式用于密集输出。可应用于复杂领域。
'Radau':5 阶 Radau IIA 的隐式 Runge-Kutta。误差由三阶精确嵌入公式控制。满足搭配条件的三次多项式用于密集输出。
'BDF':基于导数逼近的后向微分公式的隐式多步变阶(1 到 5)方法。使用准恒定步长方案,并使用 NDF 修改提高精度。可应用于复杂领域。
'LSODA':具有自动刚度检测和切换功能的 Adams/BDF 方法。这是来自 ODEPACK 的 Fortran 求解器的封装。
显式 Runge-Kutta 方法('RK23'、'RK45'、'DOP853')应用于非刚性问题,隐式方法('Radau'、'BDF')应用于刚性问题。在 Runge-Kutta 方法中,建议使用“DOP853”进行高精度求解(rtol 和 atol 的值较低)。
如果不确定,请先尝试运行“RK45”。如果它进行了异常多次的迭代、发散或失败,则您的问题可能很僵硬,您应该使用“Radau”或“BDF”。'LSODA' 也可以是一个很好的通用选择,但它可能不太方便,因为它包装了旧的 Fortran 代码。
本文作者:yuhldr
本文地址: https://yuhldr.github.io/posts/13877.html
版权声明:转载请注明出处!