本节摘要:微分方程刻画"变化率由当前状态决定"的系统,是物理、生物、金融建模的共同语法。本节从两个可解析求解的类型(可分离变量、一阶线性)入手,转向动力系统的核心问题——平衡点及其稳定性(线性化判据),用数值仿真重现捕食者—猎物的周期振荡,并延伸到混沌:确定性如何生出不可预测。
牛顿第二定律"加速度等于力除以质量"是最早的微分方程:知道现在的位置与速度,加上作用力的规律,未来的轨道就被完全决定。两百年后沃尔泰拉把亚得里亚海渔场数据写成同样的语法:鱼的变化率由当前鱼量与捕食者数量共同决定。语法相同,对象从行星换成了沙丁鱼——微分方程是"变化如何产生"的通用语言,5.1 节的人口模型已用过它最简单的句式。
可解析求解的类型先备两种。可分离变量:把 dy/dx 拆成两边各自积分(逻辑斯蒂方程即此类的成员)。一阶线性方程 y' + p(x)y = q(x):乘以积分因子后左边凑成乘积导数,积分即得。
import sympy as sp x = sp.symbols('x') y = sp.Function('y') # 可分离变量例:y' = x*y,分离后积分得 y = C*e^(x^2/2) sol1 = sp.dsolve(sp.Derivative(y(x), x) - x * y(x), y(x), ics={y(0): 2}) print(sol1) # y(x) = 2*exp(x^2/2) # 一阶线性例:y' + 2y = e^(-x),积分因子 e^(2x) sol2 = sp.dsolve(sp.Derivative(y(x), x) + 2 * y(x) - sp.exp(-x), y(x), ics={y(0): 0}) print(sp.simplify(sol2.rhs)) # 含 exp(-x) 与 exp(-2x) 两项的稳态加暂态结构 # 稳态项 e^(-x)/3 是输入驱动的长期行为,暂态项按 e^(-2x) 衰减——线性系统的标准解剖
多数微分方程没有解析解,动力系统理论绕开解的显式表达,直接研究定性的性格:系统静止在哪里(平衡点),受扰后回不回得来(稳定性)。判据来自线性化——在平衡点处把方程泰勒展开(第 3 章)只留线性项,特征值(第 2 章)的实部符号一锤定音:全负则稳定(扰动衰减)、有正则不稳定(扰动放大)、纯虚则中心(周期振荡)。线性代数与分析在微分方程里会师:
import sympy as sp # 例子:阻尼振子 m*x'' + c*x' + k*x = 0,取 m=1, c=0.6, k=9 lam = sp.symbols('lam') A = sp.Matrix([[0, 1], [-9, -0.6]]) # 状态空间形式的一阶方程组 print(A.eigenvals()) # 输出两个负实部的复特征值:欠阻尼振荡衰减——小球弹几下停住的数学刻画 # 把 c 换成 0:纯虚特征值,永不停振荡;c 换成 -0.6:正实部,振荡放大失稳
洛特卡—沃尔泰拉方程组:猎物 x 的增长被捕食者 y 压制,捕食者的增长以猎物为食。数值仿真(欧拉法,下一节的主角先用起来)重现经典相位关系:
def lotka_volterra(a=1.0, b=0.1, c=1.5, d=0.075, x0=40, y0=9, T=30, dt=0.001): # x' = a*x - b*x*y 猎物:自然增长减被捕食 # y' = -c*y + d*x*y 捕食者:饥饿死亡加捕食获益 xs, ys, t = [x0], [y0], 0 while t < T: x, y = xs[-1], ys[-1] xs.append(x + (a*x - b*x*y) * dt) ys.append(y + (-c*y + d*x*y) * dt) t += dt return xs, ys xs, ys = lotka_volterra() print(f"猎物峰值约 {max(xs):.0f}(初始 40)") print(f"末段猎物 {xs[-1]:.1f} 捕食者 {ys[-1]:.1f}") # 周期可从相邻两次猎物峰值的时间差读出,约 5 个时间单位一循环 # 输出示例:猎物峰值约 65-70,两物种相位错开四分之一周期 # 机制:猎物多 -> 捕食者增长 -> 猎物锐减 -> 捕食者饥饿 -> 循环重启 # 平衡点在 (c/d, a/b) = (20, 10),线性化为纯虚特征值:中心,对应中性周期轨道
渔场传奇的数学注脚:战争期间捕捞减少,沃尔泰拉模型预言捕食者(鲨鱼)占比会上升——因为捕捞相当于同时削弱两物种,但对捕食者的相对伤害更大。数据支持了模型,微分方程第一次在生态学赢下声望。
把捕食模型换成逻辑斯蒂的离散版本 x(n+1) = r x(n) (1 - x(n)),参数 r 越过约 3.57 后,轨道不再收敛也不周期,对初值极端敏感——初始误差千分之一,二十步后轨道完全分道扬镳。蝴蝶效应的数学本体就是这种敏感依赖性:系统完全确定(无随机项),长期预测原则上不可能。这不是数值误差的锅,是系统的结构性质:
def logistic_map(r, x0, steps=40): x = x0 traj = [x] for _ in range(steps): x = r * x * (1 - x) traj.append(x) return traj a = logistic_map(3.9, 0.500) b = logistic_map(3.9, 0.501) # 初值仅差千分之一 for n in [10, 20, 30]: print(n, f"{a[n]:.4f} vs {b[n]:.4f} 差 {abs(a[n]-b[n]):.4f}") # 输出:前几步几乎重合,20 步后差异达到 0.3+ 量级——轨道完全失耦 # 对比 r=3.2:两条轨道都收敛到同一点,敏感性与参数挂钩(倍周期分岔通向混沌)
⚠️ 数值警告:混沌系统上做长期仿真没有意义,误差指数放大(李雅普诺夫指数为正);工程对策是集成预报(多条扰动初值并行跑,看分布而非单轨)与短期滚动预测。天气预报正是这么做的。
方程在手,下一节直面计算的现实:浮点误差、截断与舍入的博弈,以及如何选一个又准又稳的数值方法。