2. 三对角方程组追赶法与通用消元法性能对比

问题设定与两种算法的实现路径

本节针对同一批三对角方程组 Ax = d,其中 A 仅在主对角线、上次对角线、下次对角线非零,分别用追赶法(Thomas 算法)与通用高斯消元(带部分主元)求解,并在相同规模 n 下比较时间复杂度、内存占用与数值误差。三对角矩阵的典型形式为:

| b1  c1   0   ... |   | x1 |   | d1 |
| a2  b2  c2   ... |   | x2 |   | d2 |
|  0  a3  b3  c3 ...| * | x3 | = | d3 |
| ...               |   | .. |   | .. |

追赶法利用三对角结构,把消元与回代压缩为两次线性扫描:前向消元只更新 b、d,回代只依赖相邻分量。通用高斯消元则把矩阵视为稠密 n×n 数组,逐列选主元、消去下方元素,再回代。两者在数学上等价,但计算路径与存储需求差异显著。

追赶法(Thomas) 存储:三条对角线 a / b / c + 右端 d a[2..n] b[1..n] c[1..n-1] d[1..n] ① 前向消元(i = 2..n) m = a[i] / b[i-1] b[i] = b[i] − m·c[i-1] d[i] = d[i] − m·d[i-1] ② 回代(i = n..1) x[n] = d[n] / b[n] x[i] = (d[i] − c[i]·x[i+1]) / b[i] 复杂度与存储 时间:约 8n 次浮点运算 → O(n) 存储:4 条一维数组 → O(n) 通用高斯消元(部分主元) 存储:完整 n×n 矩阵 A + 右端 d A[1..n][1..n] d[1..n] ① 逐列选主元并消元(k = 1..n-1) 选 |A[i][k]| 最大行交换到第 k 行 对 i = k+1..n:A[i][j] −= A[i][k]/A[k][k]·A[k][j] ② 回代(i = n..1) x[i] = (d[i] − Σ A[i][j]·x[j]) / A[i][i] j 从 i+1 到 n 累加 复杂度与存储 时间:约 (2/3)n³ 次浮点运算 → O(n³) 存储:n² 个元素 → O(n²)

可运行的对比实现

下面给出 Python 实现,两种算法共用同一组三对角数据,并统一用残差无穷范数 ‖Ax − d‖∞ 与相对误差衡量数值精度。为便于观察规模效应,用 n = 100、500、1000 三档测试。

import numpy as np
import time

def thomas(a, b, c, d):
    """a: 下次对角(长度n, a[0]不用), b: 主对角, c: 上次对角(长度n, c[-1]不用), d: 右端"""
    n = len(b)
    cp = np.zeros(n); dp = np.zeros(n)
    cp[0] = c[0] / b[0]
    dp[0] = d[0] / b[0]
    for i in range(1, n):
        m = b[i] - a[i] * cp[i-1]
        cp[i] = c[i] / m if i < n-1 else 0.0
        dp[i] = (d[i] - a[i] * dp[i-1]) / m
    x = np.zeros(n)
    x[-1] = dp[-1]
    for i in range(n-2, -1, -1):
        x[i] = dp[i] - cp[i] * x[i+1]
    return x

def gauss_tridiag(a, b, c, d):
    """把三对角矩阵展开为稠密矩阵后做带部分主元的高斯消元"""
    n = len(b)
    A = np.zeros((n, n))
    for i in range(n):
        A[i, i] = b[i]
        if i > 0: A[i, i-1] = a[i]
        if i < n-1: A[i, i+1] = c[i]
    rhs = d.copy()
    for k in range(n-1):
        p = k + np.argmax(np.abs(A[k:, k]))
        if p != k:
            A[[k, p]] = A[[p, k]]
            rhs[[k, p]] = rhs[[p, k]]
        for i in range(k+1, n):
            f = A[i, k] / A[k, k]
            A[i, k:] -= f * A[k, k:]
            rhs[i] -= f * rhs[k]
    x = np.zeros(n)
    for i in range(n-1, -1, -1):
        x[i] = (rhs[i] - A[i, i+1:] @ x[i+1:]) / A[i, i]
    return x

def build_tridiag(n):
    a = np.full(n, -1.0); b = np.full(n, 4.0); c = np.full(n, -1.0)
    a[0] = 0.0; c[-1] = 0.0
    d = np.ones(n)
    return a, b, c, d

for n in (100, 500, 1000):
    a, b, c, d = build_tridiag(n)
    t0 = time.perf_counter(); x1 = thomas(a, b, c, d); t1 = time.perf_counter()
    x2 = gauss_tridiag(a, b, c, d); t2 = time.perf_counter()
    A = np.diag(b) + np.diag(a[1:], -1) + np.diag(c[:-1], 1)
    r1 = np.linalg.norm(A @ x1 - d, np.inf)
    r2 = np.linalg.norm(A @ x2 - d, np.inf)
    print(f"n={n:4d}  Thomas={t1-t0:.4f}s  Gauss={t2-t1:.4f}s  "
          f"残差 Thomas={r1:.2e}  Gauss={r2:.2e}")

运行后应观察到:追赶法耗时随 n 近似线性增长,高斯消元耗时近似按 n³ 增长;两者残差都在机器精度量级(约 1e-12 以下),说明在本例这类对角占优的三对角矩阵上,追赶法无需选主元即可保持稳定。

复杂度与内存的定量对照

指标追赶法通用高斯消元
浮点运算量(主项)约 8n约 (2/3)n³
时间复杂度O(n)O(n³)
存储元素数4n(a、b、c、d)n² + n
空间复杂度O(n)O(n²)
主元策略对角占优时无需选主元需部分主元保证稳定
适用前提严格对角占优或对称正定三对角任意非奇异方阵

以 n = 1000 为例,追赶法约 8000 次浮点运算,高斯消元约 6.7×10⁸ 次,相差约 8 万倍;存储上追赶法约 4000 个双精度数(约 32 KB),高斯消元约 10⁶ 个(约 8 MB)。这正是结构化算法相对通用算法的核心收益。

数值误差与稳定性判读

追赶法的稳定性依赖系数矩阵的对角占优性。若 |b[i]| ≥ |a[i]| + |c[i]| 且至少一处严格大于,则前向消元中分母不会趋近于零,误差不会被放大。反之,若某步 b[i] − a[i]·cp[i-1] 接近零,追赶法会出现严重误差甚至除零。此时应改用带主元的高斯消元,或对三对角矩阵做局部主元交换(部分选主元的追赶法)。

判断是否可用追赶法:先检查对角占优条件;若不满足,先用小规模算例比较两种方法的残差,再决定是否引入主元交换。不要仅凭矩阵是三对角就默认追赶法安全。
验证精度时,用原始 A 与 d 计算残差 ‖Ax − d‖∞,而不是用消元后的中间量;残差应接近机器精度乘以矩阵范数的量级。