📑 目录
- 微分方程数值解概览
- Euler 法与 Taylor 展开法
- Runge-Kutta 方法族
- Predictor-Corrector 多步法
- 微分方程组与 Lorenz 混沌
- 高阶微分方程的降阶处理
- 刚性微分方程(Stiff ODE)
- 边值问题与打靶法(Shooting)
- Numerov 方法
- Schrödinger 方程数值求解
- Green 函数与 Wronskian
- 有限差分法(Finite Difference)
- 应用实例:TOV 方程与中子星
- 总结对比表
1. 微分方程数值解概览
两大问题类型
| 类型 | 给定条件 | 物理例子 | 主要方法 |
|---|
| 初值问题 (IVP) | 已知 y(t0), 求后续 | 行星轨道、电路暂态 | Euler, RK, Adams |
| 边值问题 (BVP) | 已知两边界条件 | 量子束缚态、热传导稳态 | 打靶法, 有限差分 |
💡 物理直觉:初值问题是"知道现在,推演未来"(动力学),边值问题是"知道两端,反推中间"(稳态分布)。
2. Euler 法与 Taylor 展开法
2.1 Euler 法 — 最朴素的一阶方法
核心思想:用切线近似曲线。
对于一阶 ODE:
等间距离散化:取步长 h,定义 ti=t0+i⋅h,$i = 0,1,2,...,N$
Euler 递推公式:
说人话:下一步 = 当前值 + 步长 × 当前斜率。就像盲人走路,每步朝着当前方向走固定距离,完全不看前面弯不弯。
📐 数值例子: y′=−2y+1, y(0)=1
精确解: y(t)=0.5+0.5e−2t
观察:Euler 法在 t=1.0 时误差约 12%。步长太大!
2.2 高阶 Taylor 展开法
递推公式:
局部截断误差 (Local Truncation Error):
- Euler 法(1阶 Taylor):$O(h^2)$ 每步
- k 阶 Taylor:$O(h^{k+1})$ 每步
对 y′=−2y+1 用二阶 Taylor
y′′=−2y′=−2(−2y+1)=4y−2
| t | 精确解 | Euler (h=0.2) | 二阶 Taylor (h=0.2) | Euler (h=0.1) |
|---|
| 0.4 | 0.7247 | 0.7200 | 0.7244 | 0.7568 |
| 0.8 | 0.6009 | 0.5968 | 0.6003 | 0.6334 |
| 1.0 | 0.5677 | 0.4982 | 0.5596 | 0.5838 |
💡 结论:提高阶数比缩小步长更有效!二阶 Taylor 用 h=0.2 的精度已经超过 Euler 用 h=0.1。
3. Runge-Kutta 方法族
在科学计算、航空航天轨道计算、乃至游戏物理引擎中,90% 以上的常微分方程初值问题,其底层默认的算法都是龙格-库塔法。
3.1 核心思想
Taylor 展开法需要手动求各阶导数
(麻烦! + 阶数越高 手动求偏导的项数呈现指数级爆炸 —— 工程上不可以承受的 + 导数无具体解析解 需要数值手段)
Runge-Kutta 的巧思:
能否不求导,仅仅是通过在当前步长的不同位置 多算几次斜率,然后将其“加权平均”,来拼凑出来和Taylor一样的高阶精度效果
用多点函数值的组合来模拟高阶导数效果,无需显式求导。 我们无需去求那些复杂的偏导数,我们只需要在区间 [ti,ti+1] 之间 ,多选取
一般形式:
其中 k就是我们在这一步里,通过不同位置的“试探”得到的斜率。每一个k,都依赖于前面已经算出来的斜率
这些系数 ajl、 bj 和 cj 是通过与泰勒级数展开进行复杂的代数比对(Taylor matching)强行解出来
3.2 二阶 Runge-Kutta:中点法与改进 Euler 法
🔹 中点法 (Midpoint Method) 先探路 再迈步
说人话:先用半步走到的中点斜率,再拿这个斜率走一整步。比直接走精度高了一阶。
k1就是 起点的斜率 , k2就是中点处预测的斜率
你蒙着眼睛在一座山上往下走。如果直接用起点斜率 k1 走一整步(欧拉法),由于山坡在弯曲,你很容易偏离轨道。 中点法的策略是:我先用起点的斜率 k1 试探着走半步( 2h),到达“中点”。我测出中点处的斜率 k2,然后我回到起点,手持这个中点斜率 k2 跨出完整的一整步。由于中点斜率代表了这一小段路程的“平均趋势”,这样跨出的一步,精度直接从一阶跃升到了二阶(全局误差 O(h2))!
🔹 改进 Euler 法 (Modified Euler / Heun's Method)
说人话:取"起点斜率"和"用 Euler 走到终点的斜率"的平均。相当于预测-校正。
中点法 是去中间探路 而 改进的欧拉法 则是两头兼顾
可以先用 起点的斜率 k1 算一个“预期的终点”,接着 我们测量这个预期终点处斜率k2,最后将起点与终点的斜率进行算术平均 得出最终迈步的依据 —— 预测-校正
Predictor- Corrector 思想!!!
3.3 四阶 Runge-Kutta (RK4) — 工业标准
公式(经典 RK4):
k1 k2 k3 k4 yi+1=f(ti,yi)=f(ti+2h, yi+2hk1)=f(ti+2h, yi+2hk2)=f(ti+h, yi+hk3)=yi+6h(k1+2k2+2k3+k4)
本质上其代数逻辑 与 Simpson 积分 惊人的相似!!!
与数值积分里的 Simpson 公式权重( 61,64,61)极其相似!
k1(起点斜率)权重为 1/6;
k2,k3(两个在中点不同试探方式得到的斜率)权重合起来是 4/6;
k4(终点斜率)权重为 1/6。
本质
是常微分方程数值解和数值积分 在深层代数逻辑上 合流的必然结果
一、 终极源头:将 ODE 降维成“纯积分”
要理解为什么它们会发生碰撞,我们先做一个数学实验: 假设我们有一个一阶常微分方程: dtdy=f(t,y),y(ti)=yi
如果我们对两边在区间 [ti,ti+h] 上进行精确求导积分,由微积分基本定理可以得到: y(ti+h)−y(ti)=∫titi+hf(t,y(t))dt
所以,下一步的精确值一定是: yi+1=yi+∫titi+hf(t,y(t))dt
这是一个无法直接计算的积分,因为被积函数里的 y(t) 本身就是我们正在求解的未知数!
降维假设:如果斜率不依赖于 y
现在,我们假设一种最简单、最特殊的物理场景:斜率 f(t,y) 仅仅与时间 t 有关,与 y 无关。 比如,一个物体的加速度只随时间变化: dtdy=f(t)。
此时,上面的方程直接变成了一个纯积分问题: yi+1=yi+∫titi+hf(t)dt
二、 辛普森公式(Simpson's Rule)是如何处理这个积分的?
如果要计算 ∫titi+hf(t)dt,辛普森公式的思路是:在区间 [ti,ti+h] 里,取三个等距点来做二次抛物线插值:
- 左端点: ti,对应的函数值为 f(ti);
- 中点: ti+2h,对应的函数值为 f(ti+2h);
- 右端点: ti+h,对应的函数值为 f(ti+h)。
根据辛普森公式的求积权重($1:4:1$ 比例),这个积分的近似值为: ∫titi+hf(t)dt≈6h[f(ti)+4f(ti+2h)+f(ti+h)]
因此,我们得到下一步的预测值为:
yi+1≈yi+6h[f(ti)+4f(ti+2h)+f(ti+h)]
三、 经典 RK4 此时在干什么?
现在,我们把相同的“降维假设”($f$ 不依赖于 y)代入到经典 RK4 的公式里,看看它会退化成什么样3:
- 计算 k1(起点斜率): k1=f(ti,yi)⟹k1=f(ti)
- 计算 k2(中点试探斜率 1): k2=f(ti+2h, yi+2hk1)⟹k2=f(ti+2h)
- 计算 k3(中点试探斜率 2): k3=f(ti+2h, yi+2hk2)⟹k3=f(ti+2h)
- 计算 k4(终点试探斜率): k4=f(ti+h, yi+hk3)⟹k4=f(ti+h)
因为 f 与 y 无关,我们惊奇地发现:在中点算出来的两个试探斜率 k2 和 k3 居然完全相等了2! k2=k3=f(ti+2h)
现在,我们把它们代入 RK4 的最后一步加权平均式中: yi+1=yi+6h(k1+2k2+2k3+k4)=yi+6h[f(ti)+2f(ti+2h)+2f(ti+2h)+f(ti+h)]=yi+6h[f(ti)+4f(ti+2h)+f(ti+h)]
两套公式完全合二为一,分毫不差!
四、 这个“巧合”背后的深刻含义
1. RK4 是 辛普森公式在 ODE 领域的“非线性推广”
- 辛普森公式:只能处理函数只与时间 t 相关的静态积分。
RK4:当函数不仅与 t 有关,还与当前状态 y 动态耦合时,RK4 通过巧妙的多步迭代预测(用 k1 预测出 yi+2hk1,进而算 k2;再用 k2 修正出 yi+2hk2,进而算 k3),在非线性空间里重建了辛普森公式所需要的“中点斜率”(在泰勒展开的代数精度上,RK4 巧妙地通过这两步试探,将非线性耦合带来的高阶误差一项一项全部抵消掉,从而在代数精度上达到了与辛普森公式完全匹配的四阶精度 O(h4))
因此,RK4 本质上就是“带有动态状态修正的辛普森积分”。
补充
一、 核心痛点:辛普森公式需要“真正的中点斜率”
如果我们把 ODE 的精确积分形式写出来: yi+1=yi+∫titi+hf(t,y(t))dt 如果我们想用辛普森公式来算这个积分,我们需要知道三个值:
- 起点斜率: f(ti,yi) —— 已知,就是 k1。
- 终点斜率: f(ti+h,y(ti+h)) —— 未知,因为我们不知道终点的精确状态 y(ti+h)。
- 中点真正的斜率: f(ti+2h,y(ti+2h)) —— 未知,因为我们不知道中点精确状态 y(ti+2h)。
因为我们不知道中点精确状态 y(ti+2h),所以我们无法直接算出真正的中点斜率。
二、 RK4 是如何用 k1,k2,k3,k4 逐步逼近这个“真中点斜率”的?
RK4 的精妙之处在于,它通过三次接力(预测-校正-再预测),把这个“真正的中点斜率”给强行“套”了出来。
根据泰勒展开,函数 y(t) 在 ti 处的半步($\frac{h}{2}$)展开为: y(ti+2h)=y(ti)+(2h)y′(ti)+2!1(2h)2y′′(ti)+O(h3) 整理一下系数: y(ti+2h)=yi+2hy′(ti)+8h2y′′(ti)+O(h3)— (式 A)
因为我们的常微分方程是 y′=f(t,y),所以利用全微分(链式法则)求二阶导数 y′′: y′′(t)=dtd[f(t,y(t))]=∂t∂f+∂y∂f⋅dtdy=ft+fyf (为了书写简洁,我们用偏导数简写 ft=∂t∂f,$fy = \frac{\partial f}{\partial y}$,且它们都在 $(ti, y_i)$ 处取值)。
把 y′(ti)=f 和 y′′(ti)=ft+fyf 代回 (式 A),得到真正的中点状态的精确展开式: y(ti+2h)=yi+2hf+8h2(ft+fyf)+O(h3)— (式 A-精确版)
在 RK4 中,我们第一步用 k1(即 f)预测的中点状态为: ymid,1=yi+2hk1=yi+2hf
我们用它去计算 k2: k2=f(ti+2h, yi+2hf)
这是一个二元函数 f(t,y)。我们将它在点 (ti,yi) 处进行二元泰勒展开。 二元泰勒展开公式为: f(ti+Δt, yi+Δy)=f(ti,yi)+Δt⋅ft+Δy⋅fy+O(Δ2)
在 k2 的计算中:
- 时间增量 Δt=2h
- 状态增量 Δy=2hf
代入二元泰勒展开式: k2=f+(2h)ft+(2hf)fy+O(h2) 提取公因子 2h k2=f+2h(ft+fyf)+O(h2)— (式 B)
现在,我们用这个含有更高阶误差信息的 k2 重新代入到第二次中点预测值 ymid,2 中: ymid,2=yi+2hk2
把 (式 B) 的展开式直接代入到 k2 的位置: ymid,2=yi+2h[f+2h(ft+fyf)+O(h2)]
把括号外面的 2h 乘进去: ymid,2=yi+2hf+4h2(ft+fyf)+O(h3)— (式 C)
在 RK4 公式中,第三步是在第二次中点预测状态 ymid,2 处计算斜率: k3=f(ti+2h, ymid,2)
我们将 (式 C) 的结果: ymid,2=yi+2hf+4h2(ft+fyf)+O(h3) 代入到 k3 中: k3=f(ti+2h, yi+2hf+4h2(ft+fyf)+O(h3))
此时,我们需要对二元函数 f(ti+Δt, yi+Δy) 进行泰勒展开,其中:
- 时间增量 Δt=2h
- 状态增量 Δy=2hf+4h2(ft+fyf)+O(h3)
为了计算到 O(h2) 项,我们的二元泰勒展开需要保留到二阶偏导数项: f(ti+Δt, yi+Δy)=f+Δtft+Δyfy+21[Δt2ftt+2ΔtΔyfty+Δy2fyy]+O(h3) (注:这里所有的偏导数 ft,fy,ftt,fty,fyy 都在 (ti,yi) 处取值)。
我们分项把它们代入展开:
- 一阶时间项: Δtft=2hft
- 一阶状态项: Δyfy=[2hf+4h2(ft+fyf)+O(h3)]fy=2hffy+4h2fy(ft+fyf)+O(h3)
二阶偏导数项(由于我们只需要保留到 O(h2),所以 Δy 里的 O(h2) 项在平方后会变成 O(h3) 以上,可以省去):
21Δt2ftt=21(2h)2ftt=8h2ftt 21⋅2ΔtΔyfty=(2h)(2hf+O(h2))fty=4h2ffty 21Δy2fyy=21(2hf+O(h2))2fyy=8h2f2fyy
在 RK4 中,我们用最新的精细中点斜率 k3 跨出完整的一整步,预测终点的状态 yend: yend=yi+hk3
将 (式 D) 代入 yend 的计算中: yend=yi+h(f+2h(ft+fyf)+O(h2)) 把 h 乘进去,得到预测的终点状态: yend=yi+hf+2h2(ft+fyf)+O(h3)— (式 E)
现在,我们在终点计算最后一个斜率 k4: k4=f(ti+h, yend)=f(ti+h, yi+hf+2h2(ft+fyf)+O(h3))
我们再次对二元函数进行展开,此时:
- 时间增量 Δt=h
- 状态增量 Δy=hf+2h2(ft+fyf)+O(h3)
代入二元泰勒展开公式: f(ti+Δt, yi+Δy)=f+Δtft+Δyfy+21[Δt2ftt+2ΔtΔyfty+Δy2fyy]+O(h3)
- 一阶时间项: Δtft=hft
- 一阶状态项: Δyfy=[hf+2h2(ft+fyf)+O(h3)]fy=hffy+2h2fy(ft+fyf)+O(h3)
- 二阶偏导数项(省去 O(h3) 阶): 21Δt2ftt=21h2ftt 21⋅2ΔtΔyfty=h(hf+O(h2))fty=h2ffty 21Δy2fyy=21(hf+O(h2))2fyy=21h2f2fyy
- h 的一阶项: hft+hffy=h(ft+fyf)
- h 的二阶项: 2h2fy(ft+fyf)+2h2ftt+h2ffty+2h2f2fyy 我们通分并提取公因子 2h2: 2h2[ftt+2ffty+f2fyy+fy(ft+fyf)]
因此,我们得到了 k4 的完整展开式: k4=f+h(ft+fyf)+2h2[ftt+2ffty+f2fyy+fy(ft+fyf)]+O(h3)— (式 F)
现在,四个斜率 k1、$k2$、$k3$、$k_4$ 已经全部列阵完毕。我们把它们毫无保留地写在一起:
- k1=f
- k2=f+2h(ft+fyf)+8h2[ftt+2ffty+f2fyy]+O(h3)
- k3=f+2h(ft+fyf)+8h2[ftt+2ffty+f2fyy+2fy(ft+fyf)]+O(h3)
- k4=f+h(ft+fyf)+2h2[ftt+2ffty+f2fyy+fy(ft+fyf)]+O(h3)
现在,我们将它们代入 RK4 最终的加权表达式: yi+1=yi+6h(k1+2k2+2k3+k4)
为了让你看清楚每一部分是如何抵消和合并的,我们把求和部分 k1+2k2+2k3+k4 按 h 的阶数 拆成三部分算:
- 零阶项(常数 f 的加权)
零阶项和=f+2(f)+2(f)+f=6f 代回最终公式中,再乘以外面的 6h: 6h(6f)=hf
- 一阶项(含 ft+fyf 的加权)
一阶项和=0+2(2h(ft+fyf))+2(2h(ft+fyf))+h(ft+fyf)=h(ft+fyf)+h(ft+fyf)+h(ft+fyf)=3h(ft+fyf) 代回最终公式中,再乘以外面的 6h: 6h[3h(ft+fyf)]=2h2(ft+fyf)
- 二阶项(含 ftt,fty,fyy 和 fy(ft+fyf) 的加权)
这里是整个代数魔术最核心的高潮。我们把 2k2、$2k3$ 和 $k4$ 的二阶项展开相加:
- 对于不含 fy 的偏导数组合 [ftt+2ffty+f2fyy](我们简记为 P): P 的系数和=2(8h2P)+2(8h2P)+2h2P=4h2P+4h2P+2h2P=h2P
- 对于带有交叉项 fy(ft+fyf) 的部分(我们简记为 Q):
- 2k2 里面没有这一项(系数为 0);
- 2k3 里面含有这一项,系数为 2×8h2×2=2h2;
- k4 里面含有这一项,系数为 2h2。 Q 的系数和=0+2h2Q+2h2Q=h2Q
把 P 和 Q 合并: 二阶项和=h2(P+Q)=h2[ftt+2ffty+f2fyy+fy(ft+fyf)]
代回最终公式中,乘以外面的 6h: 6h(h2[ftt+2ffty+f2fyy+fy(ft+fyf)])=6h3[ftt+2ffty+f2fyy+fy(ft+fyf)]
现在,我们将上面三个部分的加权计算结果全部拼起来,得到 RK4 计算得到的数值解展开: yi+1=yi+hf+2h2(ft+fyf)+6h3[ftt+2ffty+f2fyy+fy(ft+fyf)]+O(h4)— (RK4 的展开)
接着,我们写出真实的微分方程精确解 y(ti+h) 展开到 h3 的真实泰勒展开: y(ti+h)=yi+hy′(ti)+2h2y′′(ti)+6h3y′′′(ti)+O(h4)
我们用全微分(链式法则)求出精确解的各阶导数:
- 一阶导数: y′(ti)=f
- 二阶导数: y′′(ti)=ft+fyf
- 三阶导数:对 y′′=ft+fyf 再次关于时间 t 求导: y′′′(ti)=dtd[ft(t,y)+fy(t,y)⋅f(t,y)]=(ftt+ftyy′)+(fyt+fyyy′)f+fy(ft+fyf)=ftt+ftyf+(fty+fyyf)f+fy(ft+fyf)(因 y′=f,fyt=fty)=ftt+2ffty+f2fyy+fy(ft+fyf)
把这些精确导数代入精确解的泰勒展开式: y(ti+h)=yi+hf+2h2(ft+fyf)+6h3[ftt+2ffty+f2fyy+fy(ft+fyf)]+O(h4)— (精确泰勒展开)
🏁 惊人的结论:
现在,请将 (RK4 的展开) 和 (精确泰勒展开) 放在一起进行对比。
它们在 h 的零阶项、一阶项、二阶项、三阶项(甚至包含复杂的交叉偏导数 fty 和 fy(ft+fyf))上,所有的代数项和系数全部一模一样,分毫不差!
这也就意味着,通过这四个斜率的巧妙试探和最终 1:2:2:1 的加权平均:
- 局部截断误差中的 h1,h2,h3,h4 阶误差全部被完全消去(抵消)。
- 残余的第一项误差出现在 h5 阶,即局部截断误差为 O(h5) 。
- 经过区间累积,全局累积误差为 O(h4)。
精度奇迹的代数根源
为什么 RK4 只需要 4 个点就能达到全局四阶精度 ?
- 因为它退化后的辛普森公式本身就是一个具有“代数奇迹”的方法:辛普森公式虽然只用了 3 个点(相当于二次多项式插值),但由于对称性抵消,它对任意三次多项式的积分都是绝对精确的!
- 这种“免费赠送一阶精度”的对称性优势,通过 k2 和 k3 在中点的双重采样,被完美遗传给了 RK4。这使得 RK4 拥有极高的代数稳定性,成为了不需要高阶导数就能运行的性价比之王。
局部截断误差: O(h5),全局误差 O(h4)
🧮 完整数值示例:用 RK4 解 y′=−2y+1, y(0)=1
输出(部分):
| t | RK4 | 精确解 | 误差 |
|---|
| 0.40 | 0.724665 | 0.724664 | 4.5e-07 |
| 0.80 | 0.600896 | 0.600895 | 4.8e-07 |
| 1.00 | 0.567669 | 0.567668 | 5.0e-07 |
| 1.60 | 0.520423 | 0.520423 | 5.7e-07 |
| 2.00 | 0.509158 | 0.509158 | 5.9e-07 |
💡 惊人的精度:RK4 用 h=0.2,误差约 5×10−7。Euler 法要达到同样精度需要步长约 10−7 级别!
3.4 自适应步长:Runge-Kutta-Fehlberg (RKF45)
核心想法:每步同时计算 4 阶和 5 阶结果,利用差值估计误差,自动调整步长。
- RKF: 局部截断误差 O(h6)(5阶),可用作误差控制
- Runge-Kutta-Verner: 达到 6 阶
伪代码:
while t < t_end:
计算 RK4 和 RK5 结果 y4, y5
误差 err = |y5 - y4|
if err > tolerance:
h = h/2; 重算
else if err < tolerance/10:
h = 2*h (放大步长)
接受结果,前进
Copy
💡 物理直觉:解变化慢的地方(平坦区)自动放大步长,变化快的地方(陡峭区)自动缩小步长。就像开车——高速路上踩油门,拐弯处踩刹车。
4. Predictor-Corrector 多步法
4.1 与 Runge-Kutta 的对比
RK4 就是在单步内多点探路 然后代数消去高阶项 达到精度
P-C 预测校正多步法 则是 通过 “不抛弃历史 用插值和积分 重构过去与未来” 来实现 长距离、高效率的稳定计算!!!
RK4 的思路:为了近似这个积分,在当前区间 [ti,ti+h] 里临时计算 4 个测试点的斜率 k1,k2,k3,k4,把旧的信息全部扔掉。这需要每前进一步就重复计算 4 次 f(t,y)。
多步法的思路:既然我们在前面已经算过了 ti,ti−1,ti−2 等处的斜率,为什么还要在每一步里重复去算新测试点?我们直接利用这些现成的历史斜率,用多项式把 f(t,y(t)) 在过去及当下的曲线“描绘(插值)”出来,然后直接对这个多项式进行精确求积分!
| 特性 | Runge-Kutta | 多步法 (Adams 族) |
|---|
| 步数依赖 | 单步法(只用上一步) | 多步法(用前面多步) |
| 计算量 | 每步多次函数求值 | 每步 1-2 次函数求值 |
| 起步 | 自动起步 | 需 RK4 起步(算前几步) |
| 典型应用 | 通用 | 长时间演化(如 TDDFT) |
4.2 Adams-Bashforth (显式预测)
三步显式公式:
具体推导
我们要推导三步显式公式: yi+1=yi+12h[23f(ti,yi)−16f(ti−1,yi−1)+5f(ti−2,yi−2)]
1. 待定系数法的泰勒展开设定
假设时间步长 h 是恒定的(即 ti−ti−1=h, ti−ti−2=2h)。为了简洁,我们将已知的历史斜率记为:
- fi=f(ti,yi)
- fi−1=f(ti−1,yi−1)
- fi−2=f(ti−2,yi−2)
我们希望寻找三个系数 a,b,c,使得以下线性组合在代数上具有最高精度: y(ti+1)≈y(ti)+h[afi+bfi−1+cfi−2]— (式 1)
由于 y′(t)=f(t,y(t)),我们把这三项写成导数形式:
- fi=y′(ti)
- fi−1=y′(ti−1)
- fi−2=y′(ti−2)
2. 将所有历史点在当下的 ti 处进行泰勒展开
我们将 y(ti+1),以及历史导数 y′(ti−1)、$y'(t{i-2})$ 全部在 $ti$ 处展开到 h3 阶:
- 左端点 y(ti+1) 在 ti 处的展开(向前走一步 h): y(ti+1)=y(ti)+hy′(ti)+2h2y′′(ti)+6h3y′′′(ti)+O(h4)— (式 2)
- 历史一阶导数 y′(ti−1) 在 ti 处的展开(向后退一步 −h): y′(ti−1)=y′(ti)−hy′′(ti)+2h2y′′′(ti)+O(h3)— (式 3)
- 历史一阶导数 y′(ti−2) 在 ti 处的展开(向后退两步 −2h): y′(ti−2)=y′(ti)−(2h)y′′(ti)+2(2h)2y′′′(ti)+O(h3)=y′(ti)−2hy′′(ti)+2h2y′′′(ti)+O(h3)— (式 4)
3. 代入待定系数式,合并同类项
现在,我们将 (式 3) 和 (式 4) 代回我们的待定系数公式 (式 1) 的右端项: 右端项=y(ti)+h[ay′(ti)+by′(ti−1)+cy′(ti−2)] 将展开式代入: 右端项=y(ti)+h[ay′(ti)+b(y′(ti)−hy′′(ti)+2h2y′′′(ti))+c(y′(ti)−2hy′′(ti)+2h2y′′′(ti))]+O(h4)
我们按 y′(ti),y′′(ti),y′′′(ti) 的阶数进行提取与合并: 右端项=y(ti)+h(a+b+c)y′(ti)+h2(−b−2c)y′′(ti)+h3(21b+2c)y′′′(ti)+O(h4)— (式 5)
4. 对比系数,建立线性方程组
为了让数值公式 (式 5) 与真实的泰勒展开 (式 2) 完美契合,我们强制令它们的前三项系数完全相等1:
- hy′(ti) 的系数: a+b+c=1— (方程 1)
- h2y′′(ti) 的系数: −b−2c=21⟹b+2c=−21— (方程 2)
- h3y′′′(ti) 的系数: 21b+2c=61— (方程 3)
5. 求解方程组,见证系数的诞生
我们用 (方程 2) 减去 (方程 3): (b+2c)−(21b+2c)=−21−61 21b=−32⟹b=−34=−1216
将 b=−34 代回 (方程 2): −34+2c=−21⟹2c=34−21=65⟹c=125
将 b=−1216 和 c=125 代入 (方程 1): a−1216+125=1⟹a−1211=1⟹a=1223
6. 最终合成
把求得的 a=1223,b=−1216,c=125 代回 (式 1),提取公因子 12h1: yi+1=yi+12h[23fi−16fi−1+5fi−2]
三步显式 Adams-Bashforth 公式,完美推导完成!
(同理,若将泰勒展开推进到 O(h4) 阶并引入四个历史点 fi,fi−1,fi−2,fi−3,用完全一样的方法即可推导得出四步显式公式的系数: 2455,−2459,2437,−249。
四步显式公式:
4.3 Adams-Moulton (隐式校正)
隐式:在公式中,包含了 f(ti+1,yi+1) 这意味着:为了算出下一时刻的状态 yi+1 我们必须在等式右边提前知道下一时刻的状态 yi+1 !!! 这个在非线性方程中是无法直接进行求解的!
三步隐式公式:
代数推导
为了推导它的系数,我们假设:
我们同样把它们写成导数形式,并将所有项在当前点 ti 处展开到 h4 阶(因为多引入了一个点 ti+1,精度会再提升一阶):
- 左端点 y(ti+1) 在 ti 处的展开(向前走 h): y(ti+1)=y(ti)+hy′(ti)+2h2y′′(ti)+6h3y′′′(ti)+24h4y(4)(ti)+O(h5)— (式 7)
- 未来一阶导数 y′(ti+1) 在 ti 处的展开(向前走 h): y′(ti+1)=y′(ti)+hy′′(ti)+2h2y′′′(ti)+6h3y(4)(ti)+O(h4)— (式 8)
- 当前一阶导数 y′(ti): y′(ti)=y′(ti)
- 历史一阶导数 y′(ti−1) 在 ti 处的展开(向后退 −h): y′(ti−1)=y′(ti)−hy′′(ti)+2h2y′′′(ti)−6h3y(4)(ti)+O(h4)— (式 9)
- 历史一阶导数 y′(ti−2) 在 ti 处的展开(向后退 −2h): y′(ti−2)=y′(ti)−2hy′′(ti)+2h2y′′′(ti)−34h3y(4)(ti)+O(h4)— (式 10)
代入待定系数式,合并同类项
将 (式 8)、(式 9)、(式 10) 代入 (式 6) 的右侧括号中: 右端项=y(ti)+h[+++α(y′(ti)+hy′′(ti)+2h2y′′′(ti)+6h3y(4)(ti))βy′(ti)γ(y′(ti)−hy′′(ti)+2h2y′′′(ti)−6h3y(4)(ti))δ(y′(ti)−2hy′′(ti)+2h2y′′′(ti)−34h3y(4)(ti))]
按各阶导数项归类合并: 右端项=y(ti)+h(α+β+γ+δ)y′(ti)+h2(α−γ−2δ)y′′(ti)+h3(21α+21γ+2δ)y′′′(ti)+h4(61α−61γ−34δ)y(4)(ti)+O(h5)— (式 11)
对比精确展开式 (式 7),建立方程组
对比 (式 11) 与 (式 7) 的对应系数:
- hy′(ti) 对应项: α+β+γ+δ=1— (方程 I)
- h2y′′(ti) 对应项: α−γ−2δ=21— (方程 II)
- h3y′′′(ti) 对应项: 21α+21γ+2δ=61⟹α+γ+4δ=31— (方程 III)
- h4y(4)(ti) 对应项: 61α−61γ−34δ=241⟹α−γ−8δ=41— (方程 IV)
求解隐式方程组
用 (方程 II) 减去 (方程 IV): (α−γ−2δ)−(α−γ−8δ)=21−41 6δ=41⟹δ=241
将 δ=241 代回 (方程 II): α−γ−242=21⟹α−γ=2414— (方程 A)
将 δ=241 代入 (方程 III): α+γ+244=31⟹α+γ=244— (方程 B)
联立 (方程 A) 和 (方程 B) 相加: 2α=2418⟹α=249
联立 (方程 A) 和 (方程 B) 相减: 2γ=−2410⟹γ=−245
将已求得的 α,γ,δ 代入 (方程 I) 求 β: 249+β−245+241=1⟹β+245=1⟹β=2419
我们得到了极其完美的系数: α=249,β=2419,γ=−245,δ=241
代回 (式 6) 中:
yi+1=yi+24h[9f(ti+1,yi+1)+19f(ti,yi)−5f(ti−1,yi−1)+f(ti−2,yi−2)]
三步隐式 Adams-Moulton 公式,完美推导完成!
4.4 PECE 模式(预测-求值-校正-求值)
流程:
1. Predict: 用 Adams-Bashforth (显式) 预测 y_{i+1}^(0)
2. Evaluate: 计算 f(t_{i+1}, y_{i+1}^(0))
3. Correct: 用 Adams-Moulton (隐式) 校正得到 y_{i+1}
4. Evaluate: 重新计算 f(t_{i+1}, y_{i+1})
→ 反复迭代 predictor-corrector 直至收敛
Copy
💡 用于 Time-Dependent DFT(含时密度泛函理论)等需要长期稳定积分的高精度计算。
极低的计算开销:RK4 走一步需要计算 4 次极其复杂的 f(t,y)。而在 PECE 多步法中,历史斜率都是直接从内存中读取的,走一步通常只需要进行 1∼2 次 f(t,y) 求值(Evaluate 步骤),计算效率比单步法高出 2 到 4 倍。
极强的数值稳定性:隐式方法(Moulton 族)天然具有比显式方法更宽广的绝对稳定域。PECE 通过显式预测和隐式校正的交替,牢牢锁住了非线性长期演化中的能量守恒性,有效防止了长时间积分中的误差发散。
总结
数值分析微分方程中主要的就是两种方法 :
1、高阶单步法 RK 纵向延伸 不断地k去迭代 只要可以一直算下去 不断精确 消去高阶项
缺点如下
著名的“Butcher障壁”(Butcher's Barrier):数学家 Butcher 证明了,4 阶 RK 只需要 4步迭代($k1 \dots k4$);但如果要达到 5 阶精度,最少需要 6 步迭代;要达到 6 阶精度,需要 7 步或 8 步。阶数越高,性价比(精度提升 / 计算量增加)急剧下降。
公式推导极其恐怖:高阶 RK 的系数推导(即代数消去的过程)极其困难,后续往往需要借助计算机代数系统(如 Maple 或 Mathematica)来推导系数。
经典
RK4:性价比之王(4 步达成 4 阶)。
Dormand-Prince (DP54 / solve_ivp 中的 RK45):通过 7 步迭代(算到 k7),同时拼凑出一个 4 阶和一个 5 阶的公式,用来自动控制步长。
2、P-C多步法 横向时空协作 的 “历史借力”
隐式公式(如 Adams-Moulton):它的精度极高、稳定性极强(能抗住非线性发散),但它需要解非线性方程(未来点 yi+1 在等式两边都有),非常难算(往往需要牛顿迭代,求导数雅可比矩阵)。
显式公式(如 Adams-Bashforth):它不含未来点,非常好算,但精度和稳定性稍逊。
关键
显式和隐式一结合,显式负责快速给个粗糙的预估,隐式负责精细校正。
最绝的是计算速度:因为在 PECE 模式中,历史的 fi−1,fi−2 早已在之前的步骤中算过并存在内存里了,这步计算它们不需要任何耗时的函数求值($O(1)$ 读取)。
在长期演化(如你提到的 TDDFT 含时密度泛函、天体轨道计算、分子动力学)中,算一次 f(t,y) 常常要解一次庞大的薛定谔方程或泊松方程。此时,PECE 相比于 RK4,能直接砍掉一半以上的计算时间,同时保持极高的精度!
5. 微分方程组与 Lorenz 混沌
5.1 向量形式
多个变量: y=(y1,y2,…,ym)
方程:
Extended RK4(m 个变量的 RK4):
在实际写代码时,最容易犯的一个毁灭性错误,就是“一边计算新值,一边覆盖旧值”。
❌ 错误示范:
正确示范:利用临时的“同步状态向量”
在计算 k2,k3,k4 之前,先用上一个阶段完整的斜率向量,生成一个当前阶段专用的、临时的“探路状态”向量 y_temp,然后所有变量统一在这个 y_temp 下进行函数求值:
💡 注意 k2 依赖所有变量的 k1——必须算完所有 k1 再算 k2。
处理多元微分方程组的黄金法则是:“同生共死,步调一致”。 通过引入一个像 y_temp 这样的中间过渡容器,我们在时空上强行将所有互相纠缠的变量锁定在了同一个时刻(起点、中点、终点)。泰勒展开的代数消去魔术也因此得以在多元空间中完美复现,护送整个混沌系统以高精度、高稳定性安全前行。
5.2 Lorenz 方程 — 混沌的经典
参数: σ=10, b=8/3,当 r=28 时出现混沌。
PYTHON
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import time
# =============================================
# 1. 定义洛伦兹系统(混沌系统)
# =============================================
def lorenz(t, state, sigma=10, r=28, b=8/3):
"""
洛伦兹方程 (大气对流简化模型)
dx/dt = sigma*(y - x)
dy/dt = x*(r - z) - y
dz/dt = x*y - b*z
"""
x, y, z = state
dx = sigma * (y - x)
dy = x * (r - z) - y
dz = x * y - b * z
return np.array([dx, dy, dz])
# =============================================
# 2. 核心:向量版 RK4 步进函数(通用,支持任意维度的系统)
# =============================================
def rk4_step_vector(f, t, y, h):
"""
四阶龙格-库塔法 (向量形式)
参数:
f: 微分方程函数,返回 np.array
t: 当前时间 (标量)
y: 当前状态向量 (np.array)
h: 步长
返回:
下一个时间步的状态向量 (np.array)
"""
# 四个斜率计算(全为向量运算)
k1 = f(t, y)
k2 = f(t + h/2, y + (h/2) * k1)
k3 = f(t + h/2, y + (h/2) * k2)
k4 = f(t + h, y + h * k3)
# 加权平均更新状态
y_next = y + (h/6) * (k1 + 2*k2 + 2*k3 + k4)
return y_next
# =============================================
# 3. 主程序:求解并保存轨迹
# =============================================
if __name__ == "__main__":
# 参数设置
state = np.array([1.0, 1.0, 1.0]) # 初始条件 (x0, y0, z0)
h = 0.01 # 步长 (越小精度越高,但计算量越大)
N = 5000 # 迭代步数 (总积分时间 T = h*N = 50秒)
# 记录轨迹(为了节省内存,可以预先分配数组,但用列表 append 更直观)
trajectory = [state.copy()]
t_current = 0.0
print(f"开始求解洛伦兹系统,步长 h={h},总步数 N={N}...")
start_time = time.time()
# 主循环
for i in range(N):
state = rk4_step_vector(lorenz, t_current, state, h)
t_current += h
trajectory.append(state.copy()) # 注意要 copy,防止后续修改影响历史数据
# 可选:每 500 步打印一次进度
if (i + 1) % 500 == 0:
print(f" 已完成 {i+1}/{N} 步,当前时间 t = {t_current:.2f}")
end_time = time.time()
print(f"计算完成!耗时 {end_time - start_time:.4f} 秒。")
# 将轨迹列表转换为 numpy 数组,方便切片绘图
traj = np.array(trajectory)
print(f"轨迹数组形状: {traj.shape}") # 应为 (N+1, 3)
# =============================================
# 4. 可视化:绘制三维相图和二维投影
# =============================================
fig = plt.figure(figsize=(15, 5))
# 子图 1: 三维相空间 (经典蝴蝶效应)
ax1 = fig.add_subplot(131, projection='3d')
ax1.plot(traj[:, 0], traj[:, 1], traj[:, 2], linewidth=0.8, color='blue')
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.set_zlabel('Z')
ax1.set_title('洛伦兹吸引子 (3D)')
# 子图 2: X-Z 投影 (最常见的蝴蝶图)
ax2 = fig.add_subplot(132)
ax2.plot(traj[:, 0], traj[:, 2], linewidth=0.6, color='red')
ax2.set_xlabel('X')
ax2.set_ylabel('Z')
ax2.set_title('X-Z 投影')
ax2.grid(True, linestyle='--', alpha=0.5)
# 子图 3: X-Y 投影
ax3 = fig.add_subplot(133)
ax3.plot(traj[:, 0], traj[:, 1], linewidth=0.6, color='green')
ax3.set_xlabel('X')
ax3.set_ylabel('Y')
ax3.set_title('X-Y 投影')
ax3.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()
# =============================================
# 5. 额外:打印最终状态和简单的统计学检查
# =============================================
final_state = traj[-1]
print(f"\n最终状态 (t={t_current:.2f}): X={final_state[0]:.6f}, Y={final_state[1]:.6f}, Z={final_state[2]:.6f}")
# 检查是否有数值溢出 (洛伦兹系统在参数 28 时是混沌有界的,一般不会溢出)
if np.any(np.isnan(traj)):
print("⚠️ 警告:检测到 NaN,请尝试减小步长 h 或检查初始条件!")
else:
print("✅ 数值稳定,未出现溢出。")
💡 混沌标志:初始条件的微小差异(如 x0=1.000 vs 1.001)在 t≈15 后演化出完全不同的轨迹——蝴蝶效应!
6. 高阶微分方程的降阶处理
6.1 核心技巧:高阶 → 一阶方程组
二阶 ODE: y′′+p(t)y′+q(t)y=r(t)
定义新变量降阶:
这就是 m 个一阶方程 → 用 Extended RK4 直接求解。
6.2 📐 完整例子:阻尼谐振子
方程:
y′′+2γy′+ω02y=0,设 γ=0.3, ω0=2,$y(0)=1,\ y'(0)=0$
降阶为:
💡 通用策略:任意 n 阶 ODE 都可以转化为 n 个一阶方程。这个技巧在计算物理中无处不在。
7. 刚性微分方程(Stiff ODE)
7.1 什么是刚性?
系统中同时存在"快变量"和"慢变量"时,显式方法必须用极小的步长来追踪最快分量,否则发散。
快变量:变化速度极快 在瞬间 衰减到0
慢变量: 真正关心的 主导了系统长期演变的变量
刚性的本质:微分方程组的 Jacobi 矩阵特征值 λi 中,$\max|\lambdai| / \min|\lambdai|$ 很大。
7.2 稳定性对比
致命的痛点:为什么显式方法(如欧拉、RK4)会在这里死掉?
如果你想计算这个系统在 t=5 秒时的状态。此时弹簧 A 早就静止了,照理说我们应该可以跨大步子(比如步长 h=0.1 秒)来快速计算弹簧 B 的悠闲运动。
然而,如果你使用显式欧拉法(或者 RK4): 只要你的步长 h 稍稍大于 0.0001 秒(比如你用了 h=0.01),那个早就已经静止的“快变量”就会在数值计算中像疯了一样成倍放大、瞬间发散到无穷大!
显式方法被逼着必须使用 h=0.00001 秒的超级小步长,去算那毫无意义、早就静止了的快变量,从而在极小的步长上把自己硬生生“耗死”。
绝对稳定性与特征值:数值世界里的“紧箍咒”
为了看清为什么显式方法会发散,而隐式方法不会,我们必须做一次严密的代数推导。
1. 显式欧拉的自我毁灭推导
假设我们面对最简单的一个一阶线性微分方程(代表我们系统里的“快变量”衰减过程): dtdy=λy,(其中 λ<0,是一个极大的负数,比如 λ=−1000) 它的精确解析解是 y(t)=Ceλt=Ce−1000t。随着时间推移,它会以极快的速度衰减到 0。
现在,我们用显式欧拉法来计算它: yi+1=yi+hf(ti,yi)=yi+h(λyi) 提取公因子 yi: yi+1=(1+hλ)yi— (式 1)
致命的死亡条件:
要想让计算出来的 y 像真实物理世界一样不断衰减(不发散),我们必须保证每前进一步,新值都比旧值小。也就是: ∣1+hλ∣<1 由于 λ=−1000,我们解这个绝对值不等式: −1<1−1000h<1⟹0<1000h<2⟹h<0.002
瞧见了吗?不管你有多想跨大步子,你的步长 h 都被死死地限制在了 0.002 秒以内! 一旦你的 h 哪怕大了一丁点(比如 h=0.003),系数就会变成 ∣1+hλ∣=∣1−3∣=2。 下一步的值就会变成上一步的 2 倍,再下一步是 4 倍、8 倍……瞬间发散崩溃!
这就是表里的: Explicit: ∣1+hλ∣<1。
隐式梯形法的“降维打击”
现在,我们把相同的方程,用隐式梯形法来算。 隐式梯形法的公式为: yi+1=yi+2h[f(ti,yi)+f(ti+1,yi+1)]
代入 f(t,y)=λy: yi+1=yi+2h[λyi+λyi+1]
我们将含有未来项 yi+1 的项移到等式的左边: yi+1−2hλyi+1=yi+2hλyi 提取并整理: (1−2hλ)yi+1=(1+2hλ)yi
yi+1=(1−2hλ1+2hλ)yi— (式 2)
🎉 奇迹发生了:
我们要让数值解不发散,同样需要放大系数的绝对值小于 1: 1−2hλ1+2hλ<1
由于物理衰减中 λ<0(即负实部),所以:
- 分母 1−2hλ 一定是一个大于 1 的正数。
- 分子 1+2hλ 的绝对值,在 λ<0 时,永远小于分母!
这意味着:无论你的步长 h 取得有多大(哪怕 h=10000 甚至无穷大),这个放大系数的绝对值都永远小于 1! 隐式梯形法是无条件稳定的。你可以放心大胆地用巨无霸步长跨过去,那个快变量只会温顺地、迅速地衰减到 0,绝对不会发散。
| 方法 | 类型 | 稳定性 | 对 Stiff 方程 |
|---|
| Euler (显式) | Explicit | $ | 1+h\lambda | < 1$ | ❌ 步长受最快分量限制 |
| 隐式梯形法 | Implicit | 无条件稳定(对负实部特征值) | ✅ |
| Gear 方法 | Implicit | 刚性好 | ✅ 推荐 |
7.3 隐式梯形法 (Implicit Trapezoidal)
隐式方法虽然好,但是它有一个致命难点:
未来项 yi+1 藏在复杂的非线性函数 f(ti+1,yi+1) 里面,我们该怎么把它解出来?
以隐式梯形法为例,我们定义一个残差函数 F(yi+1),我们的目标是找到一个 yi+1 使得这个函数等于 0: F(yi+1)=yi+1−yi−2h[f(ti,yi)+f(ti+1,yi+1)]=0
因为 f 是非线性的,我们必须在每一步里,都用牛顿迭代法(Newton-Raphson)去把它活生生“猜”出来。
牛顿迭代手算推导:
牛顿迭代法是求解非线性方程 F(x)=0 最犀利的武器。它的几何直觉极其美妙:
- 第一步(盲猜):我们先闭着眼睛对 yi+1 猜一个初始值,记为 y(0)。
- 注:怎么猜?最常用的办法是用最简单的显式欧拉法跨一步: y(0)=yi+hf(ti,yi),这个粗糙的值作为我们的起点。
- 第二步(作切线):我们画出函数 F(x) 的曲线,找到曲线在 x=y(0) 处的点 (y(0),F(y(0)))。我们在这个点上作一条切线。
- 第三步(切线求交点):这条切线的斜率就是导数 F′(y(0))。切线与 x 轴(即 F(x)=0 处)相交的点,就会比我们盲猜的 y(0) 更接近真正的根。我们将这个交点记为下一次的猜测值 y(1)。
我们用一次函数(切线方程)来写出这个过程: 在点 x=y(k) 处的切线方程为: y−F(y(k))=F′(y(k))(x−y(k)) 我们要找它与 x 轴的交点,即令 y=0,求解此时的 x(即下一次逼近值 y(k+1):
(0−F(y(k))=F′(y(k))(y(k+1)−y(k))
移项整理: y(k+1)−y(k)=−F′(y(k))F(y(k))
y(k+1)=y(k)−F′(y(k))F(y(k))— (牛顿迭代核心公式)
我们要对 F(yi+1) 关于 yi+1 求导: F′(yi+1)=∂yi+1∂(yi+1−yi−2h[f(ti,yi)+f(ti+1,yi+1)])=1−2h∂y∂f 这里的 ∂y∂f 就是一维时的导数,在多元微分方程组里,它就是赫赫有名的雅可比矩阵(Jacobian Matrix) J。
因此,牛顿迭代的每一次修正公式为: y(k+1)=y(k)−1−2hJy(k)−yi−2h[f(ti,yi)+f(ti+1,y(k))]
yi+1 出现在方程右侧,需用 Newton 迭代求解:
7.4 Gear 方法 (2阶 / 3阶)
普通多步法(Adams 族):通过对斜率 f 进行插值,去近似积分。
Gear 方法(BDF 族):通过对状态 y(即当前点和历史点)进行高阶多项式插值,强行对这个多项式求导,以此来逼近未来的斜率。
2 阶 Gear:
它的代数原理极其简单: 假设我们在 ti+1,ti,ti−1 这三个点上,穿过状态值 yi+1,yi,yi−1 作一条二次抛物线 P(t)。 通过拉格朗日插值法,我们可以写出这条代表位移的抛物线 P(t) 的表达式。 然后,我们强制要求抛物线在未来时刻 ti+1 处的导数 P′(ti+1),必须完美等于物理方程给出的斜率 f(ti+1,yi+1)! 即: P′(ti+1)=f(ti+1,yi+1) 对插值多项式求导并整理后,这一步就会硬生生、分毫不差地凑出: yi+1=34yi−31yi−1+32hf(ti+1,yi+1)
同理,若用 4 个点作三次抛物线,求导后就能推导出 3 阶 Gear:
yi+1=1118yi−119yi−1+112yi−2+116hf(ti+1,yi+1)
3 阶 Gear:
都是隐式方法,每步需 Newton 迭代求解 yi+1。
💡 稳定性准则(微分方程组):
为什么 Gear 方法(BDF)最适合 Stiff 方程?(深入绝对稳定性)
为了彻底看清为什么这套方法能成为刚性(Stiff)微分方程的“降维打击”王牌,我们需要从绝对稳定域的数学本源去剖析它。
1. 测试方程与线性化
为了分析任何一个数值方法的稳定性,数学家都会使用标准线性测试方程:
这个方程代表了物理系统中的衰减分量(快变量)。我们定义无量纲参数 z=hλ。 在真实物理世界里,因为实部为负,所以随着 t→∞,解析解 y(t)→0。 我们要求:数值方法计算出来的序列 yn 在步数 n→∞ 时,也必须衰减到 0。
2. BDF2 的特征方程与绝对稳定条件
我们将测试方程 f(t,y)=λy 代入刚刚推导出的 BDF2 公式中:
我们将所有项移到左侧并整理:
为了去分母,两边乘以 3:
这是一个关于序列 y 的二阶常系数线性差分方程。其通解的形式为 yn=Crn,其中 r 是其特征方程的根。 我们将 yn=rn 代入上式,得出其特征方程:
根据线性差分方程的稳定性理论,要让数值解 yn→0 稳定不发散,特征方程的两个根 r1,r2 的模长都必须严格小于 1(即它们必须落在复平面的单位圆内):
我们来解出特征根 r 看看它与 z=hλ 的关系:
🌟 为什么它极其稳定?
假设我们的方程存在极度刚性的快分量,这意味着 λ 是一个极大的负实数(例如 λ=−10000)。如果我们用大步长 h=1 去跨,那么 z=hλ=−10000 就会变成一个极其恐怖的负数。 我们来看一下当 z→−∞ 时,特征根 r 会发生什么:
由于分母上有 z 的一次项,而分子上只有根号下 z(即半次方项)。根据极限法则,分母的增长速度远快于分子:
这意味着什么?
当面对极强刚性( z→−∞)时,BDF2 的特征根 r 直接趋近于 0! 特征根趋近于 0,意味着每走一步,误差不仅不会放大,反而会被以近乎 100% 的效率瞬间抹除、强行掐灭! 这就是为什么说它是“黑洞一样的结构,能够吞噬快分量误差”的数学解释。
3. 绝对稳定域的对比(显式 vs 隐式)
为了让你更具象地感受,我们画出复平面上的“绝对稳定域”(也就是能让特征根模长小于 1 的所有 z 的集合):
- 显式欧拉法:稳定域是复平面上以 (−1,0) 为圆心、半径为 1 的小圆圈。 z=hλ 必须在这个小圆圈内。当 λ=−1000 时, h 必须小于 0.002 才能把 z 关进这个圆圈。
- 隐式梯形法:稳定域是整个复平面的左半平面(A-Stable)。只要 Re(λ)<0,无论 h 有多大, z 都永远在稳定域里。但是当 z→−∞ 时,其放大系数趋于 −1(边界上),误差虽然不发散,但会产生持久的数值振荡。
- Gear 方法(BDF2):不仅是 A-stable 的(整个左半平面几乎全包),而且它拥有极强的 L-稳定性(L-Stable)。也就是说,当 z→−∞ 时,它的放大系数直接收敛到 0。这相当于对快分量的数值误差实施了“降维打击”——只要你刚性发作得越厉害,它掐死误差的速度就越快。
核心结论:Implicit 的稳定性远优于 Explicit。对 Stiff 问题,宁可用隐式方法多算几步 Newton 迭代,也别用显式方法在极小步长上耗死自己。
为什么 Gear 方法最适合 Stiff 方程?
因为它的左边是纯粹的状态项 y,右边只有一项未来的斜率项 f(ti+1,yi+1)。 在代数合并和牛顿迭代时,这种结构在复平面上的绝对稳定区域极其庞大,能够像黑洞一样,把所有具有极大负实部特征值(也就是死得最快的那些快分量)的误差全部吞噬、彻底压制。
8. 边值问题BVP与打靶法(Shooting)
8.1 问题描述
边值问题:已知 y(a)=α, y(b)=β,求解 y(x)。
与初值问题的根本不同——两端都有条件。
核心打靶法就是:
把未知的初始斜率
当成一个可调参数,不断试,直到积分到 b 时满足
打靶法核心思路:把边值问题转化为初值问题!
8.2 线性打靶法
优势之处:线性方程满足叠加原理
对线性方程:
步骤:
- 解两个辅助初值问题:
用 RK4 分别求 y1(b), y2(b)
混合两个解: y(x)=y1(x)+t⋅y2(x)
其中系数 t 由边界条件确定:
两个解都满足方程(因为是线性叠加),调整混合比例 t 来同时满足两端边界条件。
线性打靶法本质
你可以理解为:
是“先打一枪,斜率取 0”的轨迹。
是“单位斜率造成的响应”。
然后真正需要的斜率修正量就是:
即:
注意:什么时候会失败?
如果
那么公式分母为零。
这说明初始斜率的变化对终点 y(b) 没有影响,或者对应的齐次边值问题存在非零解。
这通常意味着:
- 问题无解;
- 或者解不唯一;
- 或者参数正好落在某个本征值上。
例如 Sturm-Liouville 本征值问题中,齐次解可以同时满足两端边界,这时 Green 函数分母 Wronskian 也会出问题。
8.3 非线性打靶法 + 割线法求 t
对于非线性问题:
边界条件:
我们仍然设:
每给一个 t,从 a 积分到 b,得到:
定义残差函数:
目标:
割线法推导
假设已经有两个试探斜率:
对应残差:
我们用一条直线近似 F(t)。
割线斜率是:
直线方程为:
我们希望下一次 tk+1 使得这个线性近似为 0:
移项:
所以
得到:
由于
所以
这就是你写的公式。
非线性打靶法算法
- 选两个初始猜测:
- 用 RK4 积分:
- 计算残差:
- 用割线法更新:
- 重复直到:
💡 为什么叫"打靶":你站在 a 点,调整出射角 t=y′(a),"射击"目标是 b 点的边界值。射偏了就调整角度再来一枪,直到命中靶心。
8.4 割线法与牛顿法的选择
如果能算出:
那么可以用牛顿法:
| 方法 | 每步计算 | 收敛速度 | 适用 |
|---|
| 割线法 (Secant) | 1 次积分 | 超线性 (~1.618 阶) | 微分难求时 |
| 牛顿法 | 1 次积分 + 微分 | 二次收敛 | 微分好求时 |
对打靶法,$dy(b,t)/dt$ 往往不好求,割线法更实用。
9. Numerov 方法
9.1 专门为二阶 ODE 设计
Numerov 方法针对以下形式的方程(无 y′ 项):
$$y'' + k(x)y = F(x)$$
普通二阶方程:
$$
y''+p(x)y'+q(x)y=r(x)
$$
包含 y′,Numerov 不能直接套。
但有时可以通过变量替换消去一阶导。
令:
设
所以:
求导:
再求导:
代入原方程:
得到:
两边乘以 eS:
因为
所以一阶导项系数:
一阶导消失。
剩下 u 的系数:
代入:
所以:
因此变成:
这就变成了 Numerov 形式:
其中:
9.2 递推公式
我们从 Taylor 展开开始。
在网格:
记:
方程:
也就是:
第一步:中心差分展开
Taylor 展开:
相加:
所以:
第二步:用差分近似 y(4)
因为:
所以可以用二阶中心差分近似:
代入:
整理:
把括号内合并:
所以:
第三步:代入微分方程
由:
所以:
代入:
把所有含 yi+1 的项放左边:
左边:
右边整理 yi 项:
整理 yi−1 项:
因此:
最终得到 Numerov 递推公式:
齐次情形 F=0 时:
对二阶方程的 Taylor 展开到 6 阶,巧妙构造消去误差项:
💡 局部误差正比于 h6,比 RK4 还高一阶!
9.3 特例:F=0 的齐次形式
当 F(x)=0(齐次方程 y′′+k(x)y=0):
9.4 ⚠️ 注意事项
- 需要两个起点值 y(0) 和 y(h) 才能启动——不像 RK4 只要一个值 是三点递推
- 通常先知道 y(0) 和 y′(0),需用其他方法(如 Taylor 展开/RK4走一步)先求出 y(h)
- 比 RK4 精度高且计算量小(每步只需一次 k(x) 估值)
10. Schrödinger 方程数值求解
10.1 球对称势的径向方程
定义有效势:
径向方程就是:
整理成 Numerov 形式:
其中:
也就是:
所以 Schrödinger 径向方程天然适合 Numerov。
其中 u(r)=rR(r),边界条件:$u(0) = 0,\ u(\infty) \to 0$
正好利用 上边学到的Numerov方法!!!
10.2 打靶法求解束缚态
束缚态能量要求离散!!!
单纯从 r=0 往外积分有问题:
- 如果 E 不准,远处通常指数发散;
- 即使 E 很准,数值误差也可能激发发散解。
所以常用双向积分。
向外积分 Outward
从小 r=ϵ 开始,使用原点行为:
可以取:
然后用 Taylor 或 RK4/Numerov 得到第二个点,开始向外积分。
向内积分 Inward
在足够大的 Rmax 处,束缚态远处衰减。
如果远处势能趋于 0,且束缚态 E<0,则:
因为 E<0,令:
则:
解为:
束缚态要求不发散,所以:
因此在 Rmax 取:
然后从 Rmax 向内积分到匹配点 rm。
匹配条件
因为线性方程的解可以整体乘任意常数,所以不能直接要求:
因为振幅可以缩放。
真正关键是形状要一致,也就是对数导数一致:
定义匹配函数:
本征能量满足:
等价地,可以写成 Wronskian:
如果两者对数导数相等,则:
所以:
因此求本征能量就是求:
或者:
可以用:
寻找束缚态本征解流程:
1. 从 r=0 向外积分 (outward integration)
2. 从 r=R∞ 向内积分 (inward integration)
3. 在大距离 ρₘ 处匹配 (matching point)
4. 要求两个解的 Wronskian 满足:u_in·u_out' - u_out·u_in' 连续
5. 调节能量 E,重复上述步骤
→ 找到使匹配条件成立的 E = 本征能量
Copy
💡 物理直觉:束缚态能量是离散的——就像吉他弦只有特定频率才能共振。向外积分和向内积分只有在正确的能量 En 下才能光滑连接。
在一维或径向 Sturm-Liouville 问题中,束缚态有节点定理:
- 基态没有节点;
- 第一激发态有一个节点;
- 第二激发态有两个节点;
- 依此类推。
所以扫描能量时,可以通过节点数判断你找到的是哪一个本征态。
10.3 散射态与共振态
- 散射边界条件:$u(r) \sim \sin(kr + \delta)$,提取相移 δ
- 共振态:能量为复数 E−iΓ/2,相移突然增加 π/2
- 求解方法:复标度法 (Complex-scaling)、稳定化法 (Stabilization)、S-矩阵极点法
11. Green 函数与 Wronskian
11.1 核心问题
Green 函数是线性微分方程的“响应函数”。
考虑:
如果能找到 G(x,ξ) 满足:
并满足同样的边界条件,那么解就是:
设:
边界条件:
我们要构造:
当 x=ξ 时:
所以 Green 函数在左右两边都满足齐次方程:
11.2 构造左右基本解
令 u<(x) 是满足左边界条件的齐次解:
令 u>(x) 是满足右边界条件的齐次解:
于是 Green 函数可以写成:
因为:
- x<ξ 时要满足左端边界;
- x>ξ 时要满足右端边界。
11.3 连续条件
Green 函数本身必须连续。
如果 G 在 x=ξ 处跳跃,那么 G′ 会有 delta,$G''$ 会有 delta 的导数 δ′,而方程右边只有 δ,没有 δ′。
所以:
即:
11.4 导数跳跃条件
对方程在 ξ−ϵ 到 ξ+ϵ 积分:
右边:
左边第一项:
第二项:
因为区间长度趋于 0,且 G 有限。
所以:
代入左右表达式:
因此:
11.5 解出 A,B
我们有:
所以:
代入跳跃条件:
提取 B:
通分:
定义 Wronskian:
于是:
所以:
再由连续条件:
因此:
如果方程没有一阶导数项,Wronskian 是常数,所以也可以写成:
而不写 W(ξ)。
11.7 Green 函数和本征值的关系
如果:
说明 u{<} 和 u{>} 线性相关。
也就是说存在非零解同时满足左右边界条件。
这正是本征值问题的条件。
所以:
Green 函数的分母 W 在本征值处为零。
这也解释了物理中常说:
Green 函数在能量本征值处有极点。
构造流程(配合 Numerov):
1. 从 a→b 向外积分齐次方程,得到 u<(x)|满足 a 端边界条件
2. 从 b→a 向内积分齐次方程,得到 u>(x)|满足 b 端边界条件
3. 计算 Wronskian W = u<·u>' - u>·u<'
4. 构造 G(x,ξ),积分求最终解
Copy
💡 物理直觉:Green 函数 = 线性响应函数。$G(x,\xi)$ 是"$\xi$ 处的单位脉冲在 x 处引起多大的响应",与外场 f 无关。这是整个线性响应理论的数学基础。
12. 有限差分法(Finite Difference)
12.1 基本思想
将导数用差分代替,把微分方程变成代数方程组。
一阶和二阶导数的中心差分近似(精度 O(h2)):
u′(x)=2hu(x+h)−u(x−h)+O(h2)
ui′′≈h2ui+1−2ui+ui−1
12.2 📐 数值例子:一维泊松方程
离散化【$N$ 个内点(未知),$h = L/(N+1)$ 】:
通常写成:
其中:
内部点方程
对于 i=1:
因为:
所以:
对于中间点 i=2,…,N−1:
对于 i=N:
因为:
所以:
对于内部点转化为线性方程组 Au=b:
说人话:一个二阶 ODE 的边值问题变成了一个三对角线性方程组!用 Thomas 算法 O(N) 秒解,干净利落。
12.3 有限差分法 vs 打靶法
| 特性 | 打靶法 (Shooting) | 有限差分法 |
|---|
| 问题类型 | 转化为 IVP + 迭代 | 直接离散为线性系统 |
| 精度 | 继承 RK/Numerov 精度 | O(h2) 或更高阶差分 |
| 非线性方程 | 需牛顿/割线迭代 | 需解非线性代数方程 |
| 收敛依赖 | 对初始猜测敏感 | 依赖网格足够细 |
| 适合 | 一维问题 | 多维问题扩展性好 |
💡 口诀:
一维光滑问题,打靶法很灵活;
线性边值问题,有限差分很稳;
多维边值/PDE,有限差分、有限元更主流。
13. 应用实例:TOV 方程与中子星
13.1 TOV 方程 (Tolman-Oppenheimer-Volkoff)
广义相对论中描述中子星结构的耦合 ODE 组:
13.2 状态方程 (EOS)
核物理提供密度-压强关系。简化多方形式:
$$P = K \rho^\gamma$$
典型参数:$\gamma = 2.75$,$K = 1.982 \times 10^{-6}$ [cgs]
13.3 求解策略
初始条件 (r=0):
ρ(0) 给定(中心密度)
P(0) = P(ρ(0)) 从 EOS 得到
m(0) = 0
用 RK4 从 r=0 向外积分
→ 每次步进:r → r + dr
更新 P(r), m(r)(耦合方程组)
→ 直到 P(R) = 0(压力降到零)
→ 此时 R = 中子星半径,M = m(R) = 中子星质量
改变中心密度 ρ(0),重复 → 得到质量-半径关系
→ 最大质量 ≈ 2-3 倍太阳质量(取决于 EOS)
Copy
💡 物理直觉:用 RK4 从恒星中心往外积,密度(因此压力)一路下降。到表面压力为 0 → 这就是星体边界。不同的中心密度 → 不同的总质量 → 找到最大质量(超过此值星体会坍缩为黑洞)。
13.4 前沿研究
- Chong-Ji Jiang et al., Chin. Phys. Lett. 2021, 38(5): 052101
- 中子星物质状态方程仍是核物理前沿
- 引力波观测 (GW170817) 给 EOS 带来新的约束
14. 总结对比表
14.1 初值问题方法总览
| 方法 | 类型 | 局部误差 | 步数 | 每步求值 | 稳定性 | 典型用途 |
|---|
| Euler | 显式 | O(h2) | 1 | 1 | 差 | 教学演示 |
| Taylor 2阶 | 显式 | O(h3) | 1 | 需手动求导 | 中等 | 高精度小系统 |
| Heun (RK2) | 显式 | O(h3) | 1 | 2 | 中等 | 入门级实际计算 |
| RK4 | 显式 | O(h5) | 1 | 4 | 好 | ⭐ 工业标准 |
| RKF45 | 显式-自适应 | O(h6) | 1 | 6 | 好 | 自适应步长 |
| AB4 (多步) | 显式 | O(h5) | 4 | 1 | 中等 | 长时间演化 |
| AM3 (多步) | 隐式 | O(h5) | 4 | 1+迭代 | 好 | 配合 AB 做 PECE |
| 隐式梯形 | 隐式 | O(h3) | 1 | 迭代 | 极好 | Stiff 方程 |
| Gear | 隐式-多步 | O(h3)~ | 多步 | 迭代 | 极好 | Stiff 方程标准解 |
| Numerov | 专用显式 | O(h6) | 2 | 1 | 好 | 无 y′ 的二阶 ODE |
14.2 边值问题方法总览
| 方法 | 核心思路 | 精度 | 计算量 | 适用 |
|---|
| 线性打靶法 | 两解叠加 | 继承积分器精度 | 两次积分 | 线性 BVP |
| 非线性打靶+割线 | 参数搜索 | 继承积分器精度 | 多次积分 | 非线性 BVP |
| Green 函数法 | 脉冲响应分解 | 取决于积分方法 | 构造+积分 | 线性非齐次 BVP |
| 有限差分法 | 离散→线性方程组 | O(h2) 或更高 | 解三对角/稀疏矩阵 | 任意 BVP |
14.3 关键联系与口诀
📚 核心记忆卡片
| 🔑 概念 | 💭 一句话总结 |
|---|
| Euler 法 | 切线代替曲线,最粗糙但最直观 |
| RK4 | 四点取样加权平均,性价比之王 |
| 多步法 | 利用历史信息减少函数求值次数 |
| Stiff 方程 | 快慢分量混杂,显式方法必须小步长 |
| 隐式方法 | 牺牲每步计算量换取大步长稳定性 |
| 打靶法 | 把边值问题转化成带参数搜索的初值问题 |
| Numerov | 专攻无 y' 的二阶方程,精度超 RK4 |
| Green 函数 | 系统的"脉冲响应",线性理论基石 |
| 有限差分 | 导数变差分,ODE 变矩阵方程 |
| TOV 方程 | RK4 积分 + EOS = 中子星质量-半径关系 |
Akuiro 整理补充 · 2026-07-01