一阶微分方程是微分方程中最基本也是最常用的类型。从银行复利计算到药物代谢、从化学反应到电路分析,一阶 ODE 无处不在。掌握这些方程的求解技巧,是理解更复杂系统的基础。
可分离方程:最自然的形式
什么是可分离方程?
考虑一阶 ODE 的一般形式:
如果
这就是可分离方程。
核心思想:把所有含
经典例子:指数增长与衰减
例 1:求解
解:这是可分离方程,
分离变量:
两边积分:
令
物理意义: -
1 | import numpy as np |
银行复利问题
实际场景:你在银行存入
建模:设
初始条件:
实例计算:存入 10000 元,年利率 5%
1 | def compound_interest(): |
关键洞察:
| 时间 | 年复利 | 连续复利 | 差额 |
|---|---|---|---|
| 10 年 | $16,288.95 | $16,487.21 | $198.26 |
| 30 年 | $43,219.42 | $44,816.89 | $1,597.47 |
连续复利看似只多一点点利息,但长期来看差异显著。这就是
药物代谢:半衰期概念
背景:你吃了一片药,身体以固定比例代谢药物。多久药效减半?
模型:设
解:
实例:布洛芬的半衰期约为 2 小时,如果初始服用 400mg
1 | def drug_metabolism(): |
临床意义: - 通常 3-5 个半衰期后,药物几乎完全代谢 - 用药间隔通常基于半衰期设计 - 布洛芬:每 4-6 小时服用一次(约 2-3 个半衰期)
Logistic 方程:资源限制下的增长
马尔萨斯模型的问题:指数增长
改进: Pierre Fran ç ois Verhulst( 1838 年)提出
Logistic 模型
直觉: - 当
求解:这是可分离方程
用部分分数分解:
积分得到:
整理得到Logistic 函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47def logistic_growth():
K = 1000 # 环境容纳量
r = 0.3 # 增长率
t = np.linspace(0, 30, 300)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 不同初始值的 Logistic 曲线
P0_values = [10, 50, 100, 500, 1200]
colors = plt.cm.viridis(np.linspace(0, 1, len(P0_values)))
for P0, color in zip(P0_values, colors):
P = K / (1 + (K/P0 - 1) * np.exp(-r * t))
axes[0].plot(t, P, color=color, linewidth=2, label=f'P ₀ = {P0}')
axes[0].axhline(y=K, color='red', linestyle='--', linewidth=2, label=f'K = {K}')
axes[0].set_xlabel('Time', fontsize=12)
axes[0].set_ylabel('Population', fontsize=12)
axes[0].set_title('Logistic Growth: Different Initial Conditions', fontsize=14, fontweight='bold')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# 增长率 vs 人口
P_range = np.linspace(0, 1.5 * K, 200)
dP_dt = r * P_range * (1 - P_range / K)
axes[1].plot(P_range, dP_dt, 'b-', linewidth=2.5)
axes[1].axvline(x=K/2, color='green', linestyle='--', label='Maximum growth at P = K/2')
axes[1].axvline(x=K, color='red', linestyle='--', label='Carrying capacity K')
axes[1].axhline(y=0, color='black', linewidth=0.5)
axes[1].fill_between(P_range[P_range <= K], dP_dt[P_range <= K], alpha=0.3, color='blue')
axes[1].fill_between(P_range[P_range > K], dP_dt[P_range > K], alpha=0.3, color='red')
axes[1].set_xlabel('Population P', fontsize=12)
axes[1].set_ylabel('Growth Rate dP/dt', fontsize=12)
axes[1].set_title('Growth Rate vs Population', fontsize=14, fontweight='bold')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('第 2 章-一阶微分方程/fig4_logistic.png', dpi=150, bbox_inches='tight')
一阶微分方程/fig4_logistic.png)
plt.close()
logistic_growth()
S 形曲线的特征:
- 拐点:在
处,增长最快 - 渐近线:
(永远不会超过太多) - 对称性:绕拐点中心对称
现实应用: - 细菌在培养皿中的生长 - 新技术的采用曲线 - 传染病的传播(早期) - 市场饱和模型
一阶线性方程:积分因子法
标准形式
一阶线性 ODE 的标准形式:
特点:
积分因子法的动机
考虑乘积的导数:
如果能找到一个函数
那么方程变得容易积分!
条件:比较系数,需要
这本身是可分离方程:
这就是积分因子!
通解公式
将
积分:
通解:
例题详解
例 2:求解
乘以积分因子:
左边是
积分:
通解:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30def linear_ode_example():
x = np.linspace(-3, 3, 300)
fig, ax = plt.subplots(figsize=(10, 6))
# 不同 C 值的解曲线
C_values = [-2, -1, -0.5, 0, 0.5, 1, 2]
colors = plt.cm.coolwarm(np.linspace(0, 1, len(C_values)))
for C, color in zip(C_values, colors):
y = 0.5 + C * np.exp(-x**2)
ax.plot(x, y, color=color, linewidth=2, label=f'C = {C}')
# 平衡解
ax.axhline(y=0.5, color='black', linestyle='--', linewidth=2, label='Equilibrium y = 1/2')
ax.set_xlabel('x', fontsize=12)
ax.set_ylabel('y', fontsize=12)
ax.set_title("Solution Family: y' + 2xy = x", fontsize=14, fontweight='bold')
ax.legend(loc='upper right')
ax.grid(True, alpha=0.3)
ax.set_ylim(-2, 3)
plt.tight_layout()
plt.savefig('第 2 章-一阶微分方程/fig5_linear_ode.png', dpi=150, bbox_inches='tight')
一阶微分方程/fig5_linear_ode.png)
plt.close()
linear_ode_example()
观察:所有解曲线都趋向于平衡解
RC 电路:一阶线性 ODE 的经典应用
场景:电阻
电路方程(基尔霍夫电压定律):
由于
整理为标准形式:
定义时间常数
情况 1:恒定电压源
积分因子:
物理意义: - 电容从初始电压
1 | def rc_circuit(): |
恰当方程与积分因子
什么是恰当方程?
考虑形如
或等价地写成微分形式:
如果存在函数
即
恰当性检验:
这是因为
求解恰当方程
如果方程恰当,通解就是
步骤:
- 验证
- 从
对 积分,得 - 用
确定 - 通解为
例 3:求解
求
利用
所以
通解:
使方程变恰当:积分因子
当方程不恰当时(
变成恰当方程。
特殊情况 1:
需要
此时:
特殊情况 2:
需要
此时:
伯努利方程
形式与化简
伯努利方程:
其中
技巧:做变量替换
即:
这是关于
例 4:求解
令
这是线性方程!积分因子
用分部积分:
回代
混合问题:槽中的盐水
问题设定
一个容量为 1000 升的水槽,初始装有纯水。现在以 5 升/分钟的速率注入含盐 2 克/升的盐水,同时以 5 升/分钟的速率排出混合液。求任意时刻槽中盐的含量。
建模
设
盐的流入速率:
盐的流出速率:
(因为流出的浓度 = 当前槽中浓度 =
微分方程:
初始条件:
求解
这是一阶线性方程。整理为标准形式:
积分因子:
用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42def salt_tank():
t = np.linspace(0, 1500, 300)
Q = 2000 * (1 - np.exp(-t / 200))
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 盐含量随时间变化
axes[0].plot(t, Q, 'b-', linewidth=2.5, label='Salt Amount Q(t)')
axes[0].axhline(y=2000, color='red', linestyle='--', linewidth=2, label='Equilibrium = 2000g')
# 标注关键时刻
for t_mark in [200, 400, 600, 1000]:
Q_mark = 2000 * (1 - np.exp(-t_mark / 200))
axes[0].plot(t_mark, Q_mark, 'ko', markersize=6)
axes[0].annotate(f't={t_mark}min\nQ={Q_mark:.0f}g',
xy=(t_mark, Q_mark), xytext=(t_mark+50, Q_mark-200),
fontsize=9)
axes[0].set_xlabel('Time (minutes)', fontsize=12)
axes[0].set_ylabel('Salt Amount (grams)', fontsize=12)
axes[0].set_title('Salt Tank Problem', fontsize=14, fontweight='bold')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# 浓度变化
C = Q / 1000 # 克/升
axes[1].plot(t, C, 'g-', linewidth=2.5, label='Concentration C(t)')
axes[1].axhline(y=2, color='red', linestyle='--', linewidth=2, label='Inlet Concentration = 2 g/L')
axes[1].set_xlabel('Time (minutes)', fontsize=12)
axes[1].set_ylabel('Concentration (g/L)', fontsize=12)
axes[1].set_title('Salt Concentration Over Time', fontsize=14, fontweight='bold')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('第 2 章-一阶微分方程/fig7_salt_tank.png', dpi=150, bbox_inches='tight')
一阶微分方程/fig7_salt_tank.png)
plt.close()
salt_tank()
关键洞察: - 槽中盐量趋向于 2000 克(浓度趋向 2
克/升,等于进入浓度) - 时间常数
变体:水槽容量变化
如果流入速率
例:流入 6 升/分钟( 2 克/升),流出 4 升/分钟,初始 500 升纯水。
水量:
微分方程:
这需要更复杂的积分因子,但原理相同。
存在唯一性定理
Picard-Lindel ö f 定理
定理:考虑初值问题
如果
则在
为什么重要?
正面:告诉我们什么时候可以放心求解
反面:当条件不满足时,可能出现: - 无解 - 无穷多解 - 解只存在于有限区间
例 5:
这个初值问题有无穷多解: -
1 | def non_unique_solution(): |
数值方法入门
欧拉方法( Euler's Method)
思想:用切线近似曲线
其中
几何意义:从
1 | def euler_method_demo(): |
误差分析: - 局部截断误差:
欧拉方法是一阶方法。
改进的欧拉方法( Heun's Method)
思想:用斜率的平均值
这是二阶方法,全局误差
四阶 Runge-Kutta 方法( RK4)
最常用的数值方法:
全局误差:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53def rk4_comparison():
def f(x, y):
return y
def euler(f, x0, y0, h, n_steps):
x, y = [x0], [y0]
for _ in range(n_steps):
y.append(y[-1] + h * f(x[-1], y[-1]))
x.append(x[-1] + h)
return np.array(x), np.array(y)
def rk4(f, x0, y0, h, n_steps):
x, y = [x0], [y0]
for _ in range(n_steps):
k1 = f(x[-1], y[-1])
k2 = f(x[-1] + h/2, y[-1] + h*k1/2)
k3 = f(x[-1] + h/2, y[-1] + h*k2/2)
k4 = f(x[-1] + h, y[-1] + h*k3)
y.append(y[-1] + h/6 * (k1 + 2*k2 + 2*k3 + k4))
x.append(x[-1] + h)
return np.array(x), np.array(y)
h = 0.2
n_steps = 10
x_euler, y_euler = euler(f, 0, 1, h, n_steps)
x_rk4, y_rk4 = rk4(f, 0, 1, h, n_steps)
x_exact = np.linspace(0, 2, 200)
y_exact = np.exp(x_exact)
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x_exact, y_exact, 'b-', linewidth=2.5, label='Exact')
ax.plot(x_euler, y_euler, 'r--o', linewidth=2, markersize=6, label='Euler')
ax.plot(x_rk4, y_rk4, 'g-s', linewidth=2, markersize=6, label='RK4')
ax.set_xlabel('x', fontsize=12)
ax.set_ylabel('y', fontsize=12)
ax.set_title(f'Euler vs RK4 (h = {h})', fontsize=14, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)
# 显示误差
euler_error = np.abs(y_euler[-1] - np.exp(2))
rk4_error = np.abs(y_rk4[-1] - np.exp(2))
ax.text(0.5, 5, f'Euler error: {euler_error:.4f}\nRK4 error: {rk4_error:.6f}',
fontsize=11, bbox=dict(boxstyle='round', facecolor='wheat'))
plt.tight_layout()
plt.savefig('第 2 章-一阶微分方程/fig10_rk4.png', dpi=150, bbox_inches='tight')
plt.close()
rk4_comparison()
Python 实战:综合应用
使用 SciPy 求解 ODE
1 | from scipy.integrate import solve_ivp |
方向场可视化
1 | def plot_direction_field(f, x_range, y_range, ax=None, density=20): |
总结
本章我们系统学习了一阶微分方程的主要类型和求解方法:
| 类型 | 标准形式 | 求解方法 |
|---|---|---|
| 可分离方程 | 分离变量后积分 | |
| 一阶线性方程 | 积分因子 |
|
| 恰当方程 | 找势函数 |
|
| 伯努利方程 | 变量替换 |
应用领域: - 复利与投资(指数增长) - 药物代谢(指数衰减) - 种群动力学( Logistic 方程) - 电路分析( RC 电路) - 化学反应(混合问题)
数值方法: - 欧拉方法:简单但精度低 - RK4 方法:工程中最常用 - SciPy:现代 Python 工具
练习题
基础题
求解可分离方程
, 。求解线性方程
。验证
是否恰当,若不恰当求积分因子。一个水池初始有 1000 升水含 50kg 盐。纯水以 10 升/分钟流入,混合液以 10 升/分钟流出。求盐量
。RC 电路中
, ,初始电容电压为 0,接入 10V 电源。求 。
进阶题
求解伯努利方程
。证明:对于
型方程,令 可化为可分离方程。讨论
, 的解的存在性和唯一性。细菌数量满足 Logistic 方程
, 。求: - 的表达式- 何时
? - 增长最快的时刻
- 何时
求解方程
(提示:用极坐标)
编程题
用 Python 实现改进的欧拉方法( Heun's method),并与欧拉方法比较精度。
对 Logistic 方程绘制方向场,并叠加不同初值的解曲线。
模拟连续服药问题:每 6 小时服药 500mg,药物半衰期 4 小时,绘制血药浓度曲线。
实现自适应步长的 RK45 方法,并与固定步长方法比较效率。
参考资料
- Boyce, W. E., & DiPrima, R. C. (2012). Elementary Differential Equations and Boundary Value Problems. Wiley.
- Zill, D. G. (2017). A First Course in Differential Equations with Modeling Applications. Cengage.
- Edwards, C. H., & Penney, D. E. (2014). Differential Equations and Boundary Value Problems. Pearson.
- SciPy Documentation: scipy.integrate
下一章预告:《高阶线性微分方程》 — 弹簧振动、 RLC 电路、谐振现象
- 本文标题:常微分方程(二)一阶微分方程
- 本文作者:Chen Kai
- 创建时间:2019-04-08 14:30:00
- 本文链接:https://www.chenk.top/%E5%B8%B8%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B%EF%BC%88%E4%BA%8C%EF%BC%89%E4%B8%80%E9%98%B6%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!