跳到正文
格致开物MATHWIKI

超松弛迭代法

超松弛迭代法(successive over-relaxation,SOR)在 Gauss–Seidel 的逐分量修正上乘一个松弛参数。参数为一时回到 GS;大于一时沿当前坐标的修正方向多走一些,小于一时少走一些。能否加速取决于矩阵及参数,过大的超调会使迭代不收敛。

一次超调的具体数值

对系统 2x−y=1、−x+2y=1,精确解为 (1,1)。取松弛参数 ω=1.2,按先 x 后 y 的顺序更新: xk+1=(1−ω)xk+ω2(1+yk),yk+1=(1−ω)yk+ω2(1+xk+1). 从零开始,第一轮为 (0.6,0.96),第二轮为 (1.056,1.0416)。第二轮超过精确值并不自动意味着发散;收敛应看全局误差或残差,而不是每个分量是否一直从同一侧接近。

一般逐分量公式

对 Ax=b,设对角元非零。先利用新下三角、旧上三角形成当前坐标的 GS 值,再与旧值组合: xinew=(1−ω)xiold+ωAii(bi−∑j<iAijxjnew−∑j>iAijxjold). 若 A=D+L+U,迭代矩阵是 Bω=(D+ωL)−1((1−ω)D−ωU). 它由三角求解应用,不需显式形成逆。对所有初值收敛等价于 ρ(Bω)<1。Netlib 的 SOR 章节说明了松弛参数、谱与收敛条件。[1]

对称正定情形的完整收敛论证

若 A 实对称正定,二次能量 ϕ(x)=xTAx/2−bTx 严格凸且水平集有界。某个坐标更新前的残量记为 gi=(Ax−b)i,其中已经包含当前轮此前的更新。SOR 位移为 δi=−ωgi/Aii。精确展开能量变化: ϕ(x+δiei)−ϕ(x)=giδi+12Aiiδi2=−ω(2−ω)2Aiigi2. 当 0<ω<2,每次坐标更新都下降能量。能量有下界,所以各坐标所遇残量的平方总和有限,残量趋零,坐标位移也趋零。迭代有聚点;因一整轮位移趋零,聚点同时满足每个坐标的残量为零,即 Ax=b。正定性使该解唯一,所有聚点相同,因此整个序列收敛。

这个证明同时表明范围的作用。若 ω=2,能量下降量为零。主例从零开始依次得到 (1,2),(2,1),(0,0),三轮循环不收敛。更一般地,det⁡Bω=(1−ω)n,若所有特征值模都小于一,必须有 |1−ω|<1,因此实参数 0<ω<2 也是收敛的必要范围;对非正定矩阵,它不再是充分条件。

二阶对称正定例子的SOR谱半径随参数变化,在约1.072处最小,接近二时趋于一
本图的最优参数只针对给定二阶矩阵。参数一对应GS;轻度超松弛可改善谱半径,继续增大反而变慢。

本例的最优参数与适用范围

主例的迭代矩阵为 Bω=(1−ωω/2ω(1−ω)/21−ω+ω2/4). 其迹为 T=2(1−ω)+ω2/4,行列式为 (1−ω)2。特征根从实数合并为共轭对的临界点由 T2=4(1−ω)2 给出,在 1<ω<2 内得到 ω∗=8−43=42+3≈1.07179677. 临界点后两根互为共轭,模为 ω−1 并随参数增加;临界点前的较大实根下降,故此处达到最小谱半径 ω∗−1≈0.07179677。参数一的谱半径为 1/4,所以本例有明显改善。一般最优参数依赖矩阵结构,某些 Poisson 网格公式有额外排序与结构条件,不能照搬为所有 SPD 系统的公式。

有解变式取对角系统,误差每轮乘 1−ω。此时最优值是一,一步到解;任何超松弛都没有帮助。它说明“越大越快”不成立,最优参数来自具体耦合结构。

可运行的正定矩阵实现

代码以 NumPy 检查实对称正定性,并使用 0<ω<2;从零出发,以真实残差验收,返回近似与轮数,不修改输入。一次 Cholesky 检查成本约 O(n3),稠密每轮更新为 O(n2),矩阵存储为 O(n2)。若大稀疏问题的正定性已由模型保证,通常不为检查而额外做完整稠密分解。

Python 3
import numpy as np


def sor(A, b, omega=1.2, tol=1e-12, maxiter=1000):
    A, b = np.array(A, dtype=float, copy=True), np.array(b, dtype=float, copy=True)
    if b.ndim != 1 or not b.size or A.shape != (b.size, b.size):
        raise ValueError("square matrix matching b required")
    if not np.isfinite(A).all() or not np.isfinite(b).all() or not np.array_equal(A, A.T):
        raise ValueError("finite symmetric data required")
    if not np.isfinite(omega) or not 0 < omega < 2 or not np.isfinite(tol) or tol <= 0:
        raise ValueError("0 < omega < 2 and positive tolerance required")
    if not isinstance(maxiter, int) or maxiter < 0:
        raise ValueError("nonnegative integer iteration limit required")
    np.linalg.cholesky(A)
    x = np.zeros_like(b); target = tol*max(1.0, np.linalg.norm(b, np.inf))
    for k in range(maxiter+1):
        if np.linalg.norm(b-A@x, np.inf) <= target:
            return x, k
        if k == maxiter:
            break
        old = x.copy()
        for i in range(b.size):
            gs = (b[i]-A[i, :i]@x[:i]-A[i, i+1:]@old[i+1:])/A[i, i]
            x[i] = (1-omega)*old[i]+omega*gs
        if not np.isfinite(x).all():
            raise ArithmeticError("nonfinite SOR iterate")
    raise RuntimeError("true residual did not reach tolerance")


print(sor([[2, -1], [-1, 2]], [1, 1]))  # near [1, 1]
print(sor([[2, -1], [-1, 2]], [1, 1], 8-4*np.sqrt(3)))

参考资料

  1. ↑ Richard Barrett et al. Templates for the Solution of Linear Systems,Successive Overrelaxation。