使用Scipy和Sympy解微分方程

127 阅读1分钟

1 核心概念

  • 多数微分方程难以求出解析解,需借助数值解。数值解针对自变量数组每个取值给出相对精确因变量值,可通过列表法或作图法表示。
  • 求微分方程数值解的底层原理主要是 Euler 法和 Runge - Kutta 法。

1.1 Euler 法

1.1.1 Euler 法的原理

欧拉法是最基础、最直观的常微分方程(ODE)数值解法之一,由瑞士数学家莱昂哈德·欧拉(Leonhard Euler)在 18 世纪提出。它的核心思想非常简单:

用切线近似代替曲线,一步步递推求解。 具体来说,假设我们有一阶常微分方程初值问题:

dydt=f(t,y),y(t0)=y0\frac{dy}{dt} = f(t, y), \quad y(t_0) = y_0

欧拉法将时间区间离散化,用小步长 h 进行迭代:

yn+1=yn+hf(tn,yn)y_{n+1} = y_n + h \cdot f(t_n, y_n)

其中:

  • tn+1=tn+h t_{n+1} = t_n + h
  • yny_n 是在 tnt_n 时刻的近似解
  • f(tn,yn)f(t_n, y_n) 是在 (tn,yn)(t_n, y_n) 处的导数值(即斜率)

1.1.2 欧拉法的推导

欧拉法的推导基于泰勒展开。对 y(tn+1y(t_{n+1}) 在 tnt_n 处做一阶泰勒展开:

y(tn+1)=y(tn)+hy(tn)+O(h2)y(t_{n+1}) = y(t_n) + h \cdot y'(t_n) + O(h^2)

忽略高阶小项 O(h^2) ,并用数值近似代替导数:

yn+1yn+hf(tn,yn)y_{n+1} \approx y_n + h \cdot f(t_n, y_n)

示例:

假设有微分方程:

dydt=y,y(0)=1\frac{dy}{dt} = -y, \quad y(0) = 1

取步长 h = 0.1 ,用欧拉法计算几步:

ntnt_nyny_nf(tn,yn)=ynf(t_n, y_n) = -y_nyn+1=yn+hf(tn,yn)y_{n+1} = y_n + h \cdot f(t_n, y_n)
00.01.0-1.01.0 + 0.1 × (-1.0) = 0.9
10.10.9-0.90.9 + 0.1 × (-0.9) = 0.81
20.20.81-0.810.81 + 0.1 × (-0.81) = 0.729

可以看到,数值解逐步逼近真实解 y(t) = e^{-t} 。

2 Sympy 求解微分方程解析解

2.1 求解单个微分方程

  • 步骤

    • symbols声明符号变量,若变量为函数需指定cls = Function
    • 通过Eq对象创建方程,分别存储方程左右两边,用diff方法表示求导。
    • 调用dsolve(eq, 待求函数)求解。
  • 示例:求解y′′+2y′+y=x2,代码如下:

from sympy import * 
y = symbols('y', cls = Function) 
x = symbols('x') 
eq = Eq(y(x).diff(x, 2)+2*y(x).diff(x, 1)+y(x), x*x) print(dsolve(eq, y(x)))
  • 结果Eq(y(x), x**2 - 4*x + (C1 + C2*x)*exp(-x) + 6),因未指定初值,保留参数C1和C2。

2.2 求解常微分方程组

  • 方法一

    • symbols批量创建变量,将方程用列表表示,移项使右侧为 0。
    • 用字典保存函数初始值,通过ics参数传入dsolve求解。
  • 方法二

    • 对于线性微分方程组,可利用Matrix对象构造函数向量和系数矩阵,对方程组矩阵化求解。

示例:求解

{dx1dt=2x13x2+3x3,x1(0)=1dx2dt=4x15x2+3x3,x2(0)=2dx3dt=4x14x2+2x3,x3(0)=3 \begin{cases} \frac{\mathrm{d}x_1}{\mathrm{d}t} = 2x_1 - 3x_2 + 3x_3, & x_1(0) = 1 \\ \frac{\mathrm{d}x_2}{\mathrm{d}t} = 4x_1 - 5x_2 + 3x_3, & x_2(0) = 2 \\ \frac{\mathrm{d}x_3}{\mathrm{d}t} = 4x_1 - 4x_2 + 2x_3, & x_3(0) = 3 \end{cases}

参考资料

www.datawhale.cn/learn/conte…