3. 二维泊松方程五点差分格式与迭代求解

问题设定与离散格式

在矩形区域 Ω=[0,1]×[0,1] 上求解二维泊松方程 −Δu=f,边界取齐次 Dirichlet 条件 u=0。取均匀网格 xi=ih、yj=jh,h=1/N,内点 (i,j) 满足 1≤i,j≤N−1。对拉普拉斯算子用二阶中心差分,得到五点差分格式:

−(u[i+1,j] + u[i−1,j] + u[i,j+1] + u[i,j−1] − 4u[i,j]) / h² = f[i,j]

整理为便于迭代的形式,记 Ui,j 为数值解:

U[i,j] = ( U[i+1,j] + U[i−1,j] + U[i,j+1] + U[i,j−1] + h²·f[i,j] ) / 4

该格式截断误差为 O(h²),边界值直接取 0,无需额外处理。下面用制造解验证误差阶:取真解 u*(x,y)=sin(πx)sin(πy),则 f=2π²sin(πx)sin(πy),边界自然为零。

五点差分模板与网格内点更新关系 网格内点 (i,j) 及其四邻 C N S W E 边界 u=0(Dirichlet) h = 1/N 更新式(Jacobi / Gauss-Seidel 共用) U[i,j] = (U[N]+U[S]+U[W]+U[E] + h²·f[i,j]) / 4 Jacobi 迭代 全部用旧值 U^k 更新 U^(k+1)[i,j] = (U^k[N]+U^k[S] +U^k[W]+U^k[E] +h²f)/4 Gauss-Seidel 迭代 立即使用已更新的新值 U^(k+1)[i,j] = (U^(k+1)[N]+U^(k+1)[S] +U^(k+1)[W]+U^(k+1)[E] +h²f)/4 谱半径 ρ(Jacobi)=cos(πh),ρ(GS)=cos²(πh) → GS 迭代次数约为 Jacobi 的一半

完整可运行实现

下面用 Python 实现网格生成、右端项计算、两种迭代求解与误差统计。所有参数在本例中定义,可直接运行。

import numpy as np

def solve_poisson(N, tol=1e-10, max_iter=200000):
    h = 1.0 / N
    x = np.linspace(0, 1, N + 1)
    y = np.linspace(0, 1, N + 1)
    X, Y = np.meshgrid(x, y, indexing='ij')
    # 制造解 u* = sin(pi x) sin(pi y),f = 2 pi^2 sin(pi x) sin(pi y)
    f = 2 * np.pi**2 * np.sin(np.pi * X) * np.sin(np.pi * Y)
    u_exact = np.sin(np.pi * X) * np.sin(np.pi * Y)

    # 初始化内点为 0,边界保持 0
    U = np.zeros((N + 1, N + 1))
    h2f = h * h * f

    # Jacobi
    Uj = U.copy()
    it_j = 0
    for it_j in range(1, max_iter + 1):
        Un = Uj.copy()
        Un[1:-1, 1:-1] = 0.25 * (Uj[2:, 1:-1] + Uj[:-2, 1:-1]
                                 + Uj[1:-1, 2:] + Uj[1:-1, :-2]
                                 + h2f[1:-1, 1:-1])
        diff = np.max(np.abs(Un - Uj))
        Uj = Un
        if diff < tol:
            break

    # Gauss-Seidel(就地更新)
    Ug = U.copy()
    it_g = 0
    for it_g in range(1, max_iter + 1):
        diff = 0.0
        for i in range(1, N):
            for j in range(1, N):
                old = Ug[i, j]
                Ug[i, j] = 0.25 * (Ug[i+1, j] + Ug[i-1, j]
                                   + Ug[i, j+1] + Ug[i, j-1]
                                   + h2f[i, j])
                diff = max(diff, abs(Ug[i, j] - old))
        if diff < tol:
            break

    err_j = np.max(np.abs(Uj - u_exact))
    err_g = np.max(np.abs(Ug - u_exact))
    return it_j, it_g, err_j, err_g

for N in [8, 16, 32, 64]:
    it_j, it_g, ej, eg = solve_poisson(N)
    print(f"N={N:3d}  Jacobi迭代={it_j:6d}  GS迭代={it_g:6d}  "
          f"Jacobi误差={ej:.3e}  GS误差={eg:.3e}")

结果判读与误差阶验证

运行后应观察到两类现象。第一,迭代次数:Gauss-Seidel 的迭代次数约为 Jacobi 的一半,与谱半径 ρGS=ρJ² 的结论一致。第二,误差阶:由于两种迭代都收敛到同一离散解,最大误差应随 N 加倍而约缩小为 1/4,即误差比 ≈4,对应 O(h²)。

NJacobi 迭代次数GS 迭代次数最大误差(示例量级)误差比(N 加倍)
8约 200约 100~1e-2—
16约 800约 400~2.5e-3≈4
32约 3200约 1600~6e-4≈4
64约 12800约 6400~1.5e-4≈4
表中迭代次数与误差为按上述格式和参数推算的示例量级,实际数值取决于容差 tol 与 max_iter 设置。判断误差阶时,用相邻两级的误差比是否接近 4 来确认二阶收敛,而不是看单次误差绝对值。
Gauss-Seidel 必须就地更新(用已更新的 U[i−1,j]、U[i,j−1]),若先复制旧数组再更新,就退化为 Jacobi,迭代次数不会减半。此外,N 增大时迭代次数按 O(N²) 增长,实际工程中应改用 SOR 或共轭梯度加速。