线性多步法
线性多步法(linear multistep methods)用若干个过去网格点的解值和导数值共同推进常微分方程初值问题。保存历史能够减少每个新步所需的函数评价,但也让启动误差与过去的扰动进入递推。方法是否相容、扰动是否稳定传播以及给定步长下是否绝对稳定,是需要分别检查的性质。
本篇以二步Adams–Bashforth方法为主线,从导数外推推导公式,计算一个可手算的初值问题,再证明其二阶收敛,并用一个相容却不稳定的格式说明边界。
一般形式与启动
在等距网格 上,记 。一个 步线性多步格式写为 系数不依赖于 。当 时,新解值可直接算出,属于显式格式;当 时,新值还出现在右端函数内,一般需解一个方程,属于隐式格式。
初值问题通常只给一个起点值。二步法需要另一个起始值,常用合适的单步法产生。启动误差必须与目标全局精度相容;把第二个值随意指定,即使递推公式本身精度很高,也不能保证最终准确。这里始终讨论固定正步长,变步长需要重新计算系数。[1]
积分导数外推得到AB2
精确解满足 已知最近的两个斜率 ,用通过它们的直线外推下一步内的导数。令 ,则前一个网格点对应 ,当前点对应零,外推多项式为 在 上积分,得到 这就是二步Adams–Bashforth方法,简称AB2。负权重来自把旧斜率的直线延长到新时间段,不是任意选取的修正项。每次推进只需增加一个新网格点的右端评价,已有斜率可以复用。
从局部缺陷到全局二阶
假设真解有连续四阶导数,并在固定时间区间上有界。将精确的过去解代入公式。在 处展开 因此格式右端给出的下一步值为 而真解的三次项是 ,所以精确解与格式预测之差为 这里用的是每步解值缺陷 ;有些文献将它除以 称为局部截断误差,采用后一约定时阶为二。
还要控制误差传播。假设 对解变量的Lipschitz常数为 ,记 。数值递推减去精确解递推,取绝对值得 令 ,则 逐步迭代并用 ,对 有 所以只要两个启动值误差均为 ,全局误差也是 。由精确初值做一次Heun步可产生 的第二点误差,足够满足这里的启动要求。
指数增长的手算与变式
对 取 。先用Heun启动:起点斜率一,欧拉预测值 ,故 随后AB2使用 ,得到 这里真解在 为 。第二步的导数外推直线在 取 ,其单位区间积分为 ;乘实际步长二分之一后才是状态增量 。
变式。 若右端恒为二,两个斜率都等于二,AB2每步增加 ;Heun启动也精确,因而整个离散轨迹精确。更一般地,沿真解的导数若是时间的一次多项式,直线外推的积分没有插值误差;还需要历史值本身准确,才能将这个精确性传递给数值轨迹。
可运行的固定步长实现
下面的标量实现用一次Heun步启动,保存全部轨迹。每个新AB2步只评价一次新点的右端,过去两点的斜率保持为滚动状态。
from math import isfinite
def adams_bashforth_two(function, t0, y0, h, steps):
t0, y0, h = float(t0), float(y0), float(h)
if not all(isfinite(v) for v in (t0, y0, h)) or h <= 0:
raise ValueError("finite data and positive step required")
if type(steps) is not int or steps < 0:
raise ValueError("steps must be a nonnegative integer")
output = [(t0, y0)]
if steps == 0:
return output
previous_f = float(function(t0, y0))
predicted = y0+h*previous_f
predictor_f = float(function(t0+h, predicted))
if not all(isfinite(v) for v in (previous_f, predicted, predictor_f)):
raise ValueError("nonfinite starting stage")
y = y0+h*(previous_f+predictor_f)/2
if not isfinite(y):
raise ArithmeticError("nonfinite starting value")
output.append((t0+h, y))
if steps == 1:
return output
current_f = float(function(t0+h, y))
if not isfinite(current_f):
raise ValueError("nonfinite derivative")
for n in range(1, steps):
y += h*(1.5*current_f-0.5*previous_f)
t = t0+(n+1)*h
if not isfinite(y) or not isfinite(t):
raise ArithmeticError("nonfinite updated state")
output.append((t, y))
if n+1 < steps:
new_f = float(function(t, y))
if not isfinite(new_f):
raise ValueError("nonfinite derivative")
previous_f, current_f = current_f, new_f
return output
trajectory = adams_bashforth_two(lambda t, y: y, 0, 1, 0.5, 2)
assert trajectory == [(0.0, 1.0), (0.5, 13/8), (1.0, 83/32)]
assert adams_bashforth_two(lambda t, y: 2, 0, 1, 0.25, 4)[-1][1] == 3
assert adams_bashforth_two(lambda t, y: 0, 0, 7, 0.1, 5)[-1][1] == 7
assert adams_bashforth_two(lambda t, y: y, 0, 1, 0.1, 0) == [(0.0, 1.0)]
print(trajectory)
计算 步需要 次固定成本的右端评价,存储全部输出需 空间;只滚动保存当前状态和两点斜率时,额外工作空间为 。本实现不支持非均匀步长或向量状态。
相容与零稳定不是同一条件
对一般多步法,定义 代入常数真解和一次真解,分别得到基本相容条件 与 。零稳定的根条件还要求 的所有根模长不超过一,且单位圆上的根都是单根。它控制微小初始扰动在固定时间区间、步数随 增长时的传播。AB2有 ,根为零和一,满足该条件。[2]
相容本身不够。考虑 其 、,仍有 与 ,但根二违反零稳定。对最简单的 ,若起始误差为 ,递推的显式解是 ;即使 ,在固定正时刻 也会失控。
零稳定也不保证任意给定步长下绝对稳定。对 ,AB2的误差模式满足 若 ,则 ,根为 ,其中一个模长大于一。真解衰减时,数值递推仍可能出现增长的振荡分量。因此,多步法需要将阶数、启动方式、零稳定与问题所需的步长稳定性一起判断。
参考文献
- ↑ Tobin A. Driscoll、Richard J. Braun:Fundamentals of Numerical Computation: Multistep methods,定义6.6.1、表6.6.1以及§6.6.2–6.6.3,线性多步形式、Adams与BDF方法、启动及局部误差。访问日期:2026-10-22。
- ↑ Tobin A. Driscoll、Richard J. Braun:Zero-stability of multistep methods,定理6.8.1给出根条件,例6.8.2讨论Adams方法。访问日期:2026-10-22。