Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.7 数值实验:亲手看见正交与谱

本节属“补充篇 · 数值线性代数工具箱”:2.x 是补充篇内部编号,不是课堂第 2 章的节号。课堂第 2 章是线性方程组的解法。

本章的四个核心论断,在教科书证明里都是干净的定理,但在浮点现实里是否成立、以什么量级成立,必须亲手测一遍。本节的前三组实验分别对应:

  • 实验一(对应 2.3.3):四种正交化方法的正交性损失量级——cond2u\mathrm{cond}^2u vs cond u\mathrm{cond},u vs uu;
  • 实验二(对应 2.4.1、2.3.6):条件数如何预测有效数字损失,以及正态方程 vs QR 的分水岭;
  • 实验三(对应 2.6.5、2.6.6):Eckart–Young 最优性与低秩压缩 / PCA。

所有代码都可以直接复制运行,只依赖 numpy;各段输出均为本教材运行环境下的真实结果。

实验报告的统一要求

每题都给出「报告要求」;提交时请包含四部分:① 可复现的脚本(含随机种子);② 实测输出(原样粘贴);③ 与理论预测的逐项对照表(理论值 / 实测值 / 比值);④ 对「为什么是这个量级」的一段解释。只贴代码、不给对照表的报告不合格——这些实验的目的始终是「用数据检验理论」,而不是「跑通代码」。

实验一 · 四种正交化方法的正交性退化

目的: 复现 2.3.3 的结论:三种正交化方法的正交性损失分属「cond2u\mathrm{cond}^2u / cond u\mathrm{cond},u / uu」三档,分界线则位于 cond≈u−1/2\mathrm{cond}\approx u^{-1/2}。预期看到:CGS 先按 cond2u\mathrm{cond}^2u 增长、越过 cond≈u−1/2\mathrm{cond}\approx u^{-1/2} 后迅速饱和,MGS 按 cond u\mathrm{cond},u 增长,Householder 始终停在 uu 量级。

做法: 取 Vandermonde 矩阵 A=[xi j−1]A=[x_i^{,j-1}](xix_i 均匀取自 [0.1,1][0.1,1]),令 nn 从 66 增到 1616,cond2(A)\mathrm{cond}_2(A) 随之增长约 10 个数量级;分别用经典 Gram–Schmidt(CGS)、修正 Gram–Schmidt(MGS)、Householder 反射做 QR 分解,测量每种方法得到的 ∥QTQ−I∥2\lVert Q^{\mathrm T}Q-I\rVert_2,它直接反映算出的 QQ 偏离正交的程度。通过标准:三种方法的实测值分别落在 cond2u\mathrm{cond}^2u、cond u\mathrm{cond},u、uu 三条参考线上。

import numpy as np
u = np.finfo(float).eps / 2          # 单位舍入误差,双精度 ≈1.11e-16

def cgs(A):
    """经典 Gram–Schmidt:r_ij 用【原始】列 a_j"""
    m, n = A.shape
    Q = A.astype(float).copy(); R = np.zeros((n, n))
    for j in range(n):
        for i in range(j):
            R[i, j] = Q[:, i] @ A[:, j]          # ← 原始列
            Q[:, j] -= R[i, j] * Q[:, i]
        R[j, j] = np.linalg.norm(Q[:, j])
        Q[:, j] /= R[j, j]
    return Q, R

def mgs(A):
    """修正 Gram–Schmidt:r_ij 用【当前】列(就地更新)"""
    m, n = A.shape
    Q = A.astype(float).copy(); R = np.zeros((n, n))
    for j in range(n):
        for i in range(j):
            R[i, j] = Q[:, i] @ Q[:, j]          # ← 当前列
            Q[:, j] -= R[i, j] * Q[:, i]
        R[j, j] = np.linalg.norm(Q[:, j])
        Q[:, j] /= R[j, j]
    return Q, R

def house(A):
    """Householder 反射(逐步消元),返回 Q, R"""
    m, n = A.shape
    R = A.astype(float).copy(); Q = np.eye(m)
    for k in range(min(m - 1, n)):
        x = R[k:, k]
        alpha = -np.sign(x[0]) * np.linalg.norm(x)
        v = x - alpha * np.eye(1, len(x), 0).ravel()
        nv = np.linalg.norm(v)
        if nv < 1e-300: continue
        v = v / nv
        R[k:, :] -= 2 * np.outer(v, v @ R[k:, :])
        Q[:, k:] -= 2 * np.outer(Q[:, k:] @ v, v)
    return Q, np.triu(R[:n, :])

print(f"{'n':>3} {'cond(A)':>10} {'CGS':>11} {'MGS':>11} {'Householder':>12} "
      f"{'cond*u':>10} {'cond^2*u':>10}")
for n in [6, 8, 10, 12, 14, 16]:
    x = np.linspace(0.1, 1.0, n)
    A = np.vander(x, n, increasing=True)
    c = np.linalg.cond(A)
    f = lambda Q: np.linalg.norm(Q.T @ Q - np.eye(Q.shape[1]))
    print(f"{n:3d} {c:10.2e} {f(cgs(A)[0]):11.2e} {f(mgs(A)[0]):11.2e} "
          f"{f(house(A)[0]):12.2e} {c*u:10.2e} {c*c*u:10.2e}")

实测输出(本教材运行结果):

  n     cond(A)         CGS         MGS  Householder     cond*u   cond^2*u
  6   1.02e+04    8.78e-10    1.47e-13     1.22e-15    1.1e-12    1.2e-08
  8   7.40e+05    2.51e-05    2.28e-11     1.29e-15    8.2e-11    6.1e-05
 10   5.60e+07    1.19e+00    8.11e-10     2.45e-15    6.2e-09    3.5e-01
 12   4.35e+09    2.47e+00    1.24e-07     2.12e-15    4.8e-07    2.1e+03
 14   3.42e+11    4.48e+00    4.70e-06     2.18e-15    3.8e-05    1.3e+07
 16   2.73e+13    6.55e+00    4.70e-04     3.00e-15    3.0e-03    8.3e+10

结论(逐条对照上面的实测输出表):

  1. Householder 列始终 ∼u\sim u:从 n=6n=6 到 n=16n=16 都停在 10−1510^{-15},与 cond(A)\mathrm{cond}(A) 增长 9 个数量级毫无关系。这是「每一步都是正交变换」的直接回报(定理 2.1.6:正交变换不放大误差)。
  2. MGS 列按 cond⋅u\mathrm{cond}\cdot u 增长:表中 n=14n=14 行的理论值(cond u\mathrm{cond},u 列)为 3.8×10−53.8\times10^{-5}、实测值(MGS 列)为 4.7×10−64.7\times10^{-6};n=16n=16 行则是 3.0×10−33.0\times10^{-3} 与 4.7×10−44.7\times10^{-4}——同量级(差 6 倍左右,正是常数因子应有的幅度)。
  3. CGS 列先按 cond2u\mathrm{cond}^2u 增长,随后饱和于 O(n)O(n):表中 n=10n=10 行的理论值(cond2u\mathrm{cond}^2u 列)为 0.350.35、实测值为 1.191.19;n=16n=16 行理论值高达 8.3×10108.3\times10^{10}、实测值却只有 6.556.55——完全饱和(∥QTQ−I∥2\lVert Q^{\mathrm T}Q-I\rVert_2 的平凡上界是 n+1=17n+1=17)。饱和不代表变好,它代表 CGS 已经错到顶。

分界线的实测证据: 在实验一输出表中,从 n=8n=8 行(cond=7.4×105\mathrm{cond}=7.4\times10^{5})到 n=10n=10 行(cond=5.6×107\mathrm{cond}=5.6\times10^{7}),CGS 列的数值由 2.5×10−52.5\times10^{-5} 跳到 1.191.19;而 cond≈u−1/2=9.5×107\mathrm{cond}\approx u^{-1/2}=9.5\times10^{7} 恰好落在这个区间内——实测与「cond≳u−1/2\mathrm{cond}\gtrsim u^{-1/2} 时 CGS 失效」的预测吻合。

报告要求: ① 打印每列的「实测 / 理论」比值,确认 Householder 列的比值恒为 10−15/u=1010^{-15}/u=10 量级的常数;② 把取样点 xix_i 换成几何分布 xi=0.5 i−1x_i=0.5^{,i-1}(更病态),观察 CGS 列是否更早饱和;③ 额外加一列「CGS 跑两遍(再正交化)」的结果,验证它能把误差压到 MGS 量级(这是历史上的实用方案)。

实验二 · 条件数预测有效数字,正态方程 vs QR

目的: 验证「损失的有效数字位数 k≈log⁡10cond(A)k\approx\log_{10}\mathrm{cond}(A)」(2.4.2),并复现 2.3.6 的分水岭:条件数超过 u−1/2≈108u^{-1/2}\approx10^{8} 后,正态方程失效而 QR 仍可用。预期看到:实测误差与 cond⋅u\mathrm{cond}\cdot u 同量级;分水岭两侧,正态方程的误差由「尚可使用」跳到「完全崩溃」,QR 误差则始终停在 cond⋅u\mathrm{cond}\cdot u 量级。

做法(两部分): (a) 对 n=4,…,12n=4,\dots,12 阶 Hilbert 矩阵取真解 x=1\boldsymbol{x}=\boldsymbol{1}、构造 b=Hx\boldsymbol{b}=H\boldsymbol{x},用双精度求解,比较实测误差与 cond⋅u\mathrm{cond}\cdot u; (b) 对病态 Vandermonde 最小二乘问题,分别用正态方程与 QR 求解,比较系数的误差。通过标准:(a) 的实测误差与 cond⋅u\mathrm{cond}\cdot u 同量级;(b) 中当 cond≳u−1/2\mathrm{cond}\gtrsim u^{-1/2} 时,正态方程误差暴涨,而 QR 误差仍停在 cond⋅u\mathrm{cond}\cdot u 量级。

import numpy as np
u = np.finfo(float).eps / 2          # ≈1.11e-16

print("== (a) Hilbert 矩阵:条件数 vs 实测误差 ==")
print(f"{'n':>3} {'cond2':>10} {'log10(cond)':>12} {'实测误差':>11} {'cond*u':>10}")
for n in [4, 6, 8, 10, 12]:
    H = np.array([[1/(i + j + 1.0) for j in range(n)] for i in range(n)])
    x = np.ones(n); b = H @ x
    xh = np.linalg.solve(H, b)                   # 双精度求解
    c = np.linalg.cond(H, 2)
    print(f"{n:3d} {c:10.2e} {np.log10(c):12.1f} "
          f"{np.max(np.abs(xh - x)):11.2e} {c*u:10.2e}")

print()
print("== (b) 最小二乘:正态方程 vs QR ==")
print(f"{'m':>3} {'n':>3} {'cond(A)':>10} {'正态方程误差':>13} {'QR 误差':>11}")
for m, n in [(12, 10), (20, 15)]:
    xd = np.linspace(0, 1, m)
    A = np.vander(xd, n, increasing=True)
    c0 = np.ones(n); b = A @ c0                  # 相容右端(真解 c0)
    # 正态方程
    c_ne = np.linalg.solve(A.T @ A, A.T @ b)
    # QR
    Q, R = np.linalg.qr(A)
    c_qr = np.linalg.solve(R, Q.T @ b)
    print(f"{m:3d} {n:3d} {np.linalg.cond(A):10.2e} "
          f"{np.max(np.abs(c_ne - c0)):13.2e} {np.max(np.abs(c_qr - c0)):11.2e}")

实测输出(本教材运行结果):

== (a) Hilbert 矩阵:条件数 vs 实测误差 ==
  n     cond2  log10(cond)      实测误差     cond*u
  4  1.55e+04          4.2    4.996e-14   1.72e-12
  6  1.50e+07          7.2    2.287e-10   1.66e-09
  8  1.53e+10         10.2    1.613e-07   1.69e-06
 10  1.60e+13         13.2    7.441e-04   1.78e-03
 12  1.72e+16         16.2    2.201e-01   1.91e+00

== (b) 最小二乘:正态方程 vs QR ==
  m   n     cond(A)      正态方程误差     QR 误差
 12  10   6.84e+06      1.31e-04    5.41e-11
 20  15   6.03e+10      2.81e+01    2.23e-06

结论:

  1. (a) 部分验证了 k≈log⁡10condk\approx\log_{10}\mathrm{cond}:对照输出表 (a),每一行的实测误差都与 cond⋅u\mathrm{cond}\cdot u 同量级(n=10n=10 行 7.4×10−47.4\times10^{-4} 对 1.8×10−31.8\times10^{-3},n=12n=12 行 0.220.22 对 1.91.9)。但实测常比这个界小几倍(n=4n=4 行 5.0×10−145.0\times10^{-14} 对 1.7×10−121.7\times10^{-12},差 34 倍)——因为(2.18)给出的正是最坏情形界。
  2. (a) 部分的 n=12n=12 是「危险信号」的教科书案例:cond⋅u=1.9>1\mathrm{cond}\cdot u=1.9\gt1,误差已达 0.220.22(真值为 11)——求解器不报错,结果却毫无意义。工程判据:k≥16k\ge16 就必须重新表述问题。
  3. (b) 部分给出最干净的分水岭:对照输出表 (b),第一行 cond=6.8×106<u−1/2\mathrm{cond}=6.8\times10^{6}\lt u^{-1/2},正态方程误差 1.3×10−41.3\times10^{-4}(已丢 4 位,但还可用);第二行 cond=6.0×1010>u−1/2\mathrm{cond}=6.0\times10^{10}\gt u^{-1/2},正态方程误差涨到 2828(完全崩溃),而 QR 误差仍是 2.2×10−62.2\times10^{-6}。理论预测 QR 误差为 ∼cond⋅u=6.7×10−6\sim\mathrm{cond}\cdot u=6.7\times10^{-6},与实测同量级 ✓。

报告要求: ① 为 (a) 画「实测误差 / cond⋅u\mathrm{cond}\cdot u」随 nn 变化的曲线,确认该比值稳定在 10−1∼10−210^{-1}\sim10^{-2} 量级(说明这个界紧到只差常数因子);② 为 (b) 把 m,nm,n 再推大一档(如 m=30,n=22m=30,n=22),找到正态方程误差超过 11 的临界点,并与 cond=u−1/2\mathrm{cond}=u^{-1/2} 对照;③ 用 np.linalg.lstsq 作第三方对照,确认它与 QR 结果一致(lstsq 内部用 SVD 或 QR)。

实验三 · 低秩逼近、截断伪逆与 PCA

目的: 验证 Eckart–Young 定理(∥A−Ak∥2=σk+1\lVert A-A_k\rVert_2=\sigma_{k+1} 精确成立),实测低秩压缩的「存储–误差」曲线,并用 SVD 做一次 PCA,与协方差矩阵的特征值分解对照。预期看到:截断误差与 σk+1\sigma_{k+1} 一致到机器精度;很小的存储占比就能复原图像的大部分能量;SVD 的奇异值与协方差特征值满足 σi=(n−1)λi\sigma_i=\sqrt{(n-1)\lambda_i}。

import numpy as np
rng = np.random.default_rng(0)

# ---------- (a) Eckart–Young:误差 = 被砍掉的第一个奇异值 ----------
print("== (a) Eckart–Young 验证 ==")
m, n = 60, 40
U_, _, Vt_ = np.linalg.svd(rng.normal(size=(m, n)), full_matrices=False)
dec = np.exp(-np.arange(n) / 2.5)                 # 精确指定奇异值(指数衰减)
A = (U_ * dec) @ Vt_
U, S, Vt = np.linalg.svd(A)
for k in [1, 2, 3, 5, 8, 10]:
    Ak = (U[:, :k] * S[:k]) @ Vt[:k]
    e2 = np.linalg.norm(A - Ak, 2)
    eF = np.linalg.norm(A - Ak, 'fro')
    print(f"k={k:2d}  ||A-Ak||_2={e2:.6e} (σ{k+1}={S[k]:.6e})  "
          f"||A-Ak||_F={eF:.6e} (√Σσ²={np.sqrt((S[k:]**2).sum()):.6e})")

# ---------- (b) 合成图像的低秩压缩 ----------
print()
print("== (b) 图像低秩压缩 ==")
N = 120
xx, yy = np.meshgrid(np.linspace(0, 1, N), np.linspace(0, 1, N))
img = (np.sin(3*np.pi*xx)*np.cos(2*np.pi*yy) + 0.6*np.sin(7*np.pi*yy)
       + 0.4*np.cos(11*np.pi*xx*yy) + (np.abs(xx - 0.5) < 0.08)*1.2)
U, S, Vt = np.linalg.svd(img, full_matrices=False)
rngv = img.max() - img.min()
print("前 12 个奇异值:", np.round(S[:12], 3))
for k in [1, 2, 3, 5, 8, 12, 20]:
    Ak = (U[:, :k] * S[:k]) @ Vt[:k]
    rmse = np.sqrt(((img - Ak)**2).mean())
    print(f"k={k:2d}  存储 2k/N={2*k/N:5.1%}  "
          f"||E||F/||A||F={np.linalg.norm(img-Ak,'fro')/np.linalg.norm(img,'fro'):.5f}  "
          f"RMSE={rmse:.4f}  PSNR={20*np.log10(rngv/rmse):.2f} dB")

# ---------- (c) PCA:SVD 与协方差特征值分解对照 ----------
print()
print("== (c) PCA ==")
rng2 = np.random.default_rng(3)
L = np.linalg.cholesky(np.array([[3., 2.], [2., 2.]]))
X = rng2.normal(size=(400, 2)) @ L.T
Xc = X - X.mean(0)                                 # 必须中心化!
C = Xc.T @ Xc / (len(X) - 1)                       # 样本协方差
w, V = np.linalg.eigh(C)                           # 特征值分解
print("样本协方差 =", np.round(C, 4).tolist())
print("特征值 =", np.round(w, 4), " (真实协方差的理论值 4.5616 / 0.4384)")
print("主方向 =", np.round(V[:, 1], 4), " 方差解释比 =", np.round(w[::-1]/w.sum(), 4))
print("SVD 对照: σ =", np.round(np.linalg.svd(Xc, compute_uv=False), 4),
      " sqrt((n-1)λ) =", np.round(np.sqrt((len(X)-1)*w), 4))

# ---------- (d) 截断伪逆:截断点决定成败 ----------
print()
print("== (d) 截断伪逆 ==")
eps_n = 1e-9
A3 = rng.normal(size=(30, 3)) @ rng.normal(size=(3, 20))
A3n = A3 + eps_n * rng.normal(size=(30, 20))        # 加噪声
s = np.linalg.svd(A3n, compute_uv=False)
thr = eps_n * np.linalg.norm(A3, 2)
print(f"噪声阈值 ≈ {thr:.3e};σ3={s[2]:.3e}  σ4={s[3]:.3e}")
print(f"默认阈值 rank = {np.linalg.matrix_rank(A3n)}  →  按噪声阈值截断 rank = "
      f"{(s > thr).sum()}")

实测输出(本教材运行结果):

== (a) Eckart–Young 验证 ==
k= 1  ||A-Ak||_2=6.703200e-01 (σ2=6.703200e-01)  ||A-Ak||_F=9.033085e-01 (√Σσ²=9.033085e-01)
k= 2  ||A-Ak||_2=4.493290e-01 (σ3=4.493290e-01)  ||A-Ak||_F=6.055058e-01 (√Σσ²=6.055058e-01)
k= 3  ||A-Ak||_2=3.011942e-01 (σ4=3.011942e-01)  ||A-Ak||_F=4.058827e-01 (√Σσ²=4.058827e-01)
k= 5  ||A-Ak||_2=1.353353e-01 (σ6=1.353353e-01)  ||A-Ak||_F=1.823748e-01 (√Σσ²=1.823748e-01)
k= 8  ||A-Ak||_2=4.076220e-02 (σ9=4.076220e-02)  ||A-Ak||_F=5.493025e-02 (√Σσ²=5.493025e-02)
k=10  ||A-Ak||_2=1.831564e-02 (σ11=1.831564e-02)  ||A-Ak||_F=2.468175e-02 (√Σσ²=2.468175e-02)

== (b) 图像低秩压缩 ==
前 12 个奇异值: [75.876 71.599 23.769 10.148 10.148 10.148 10.142  9.827  7.829  6.711  5.492  3.607]
k= 1  存储 2k/N= 1.7%  ||E||F/||A||F=0.72427  RMSE=0.6642  PSNR=17.27 dB
k= 2  存储 2k/N= 3.3%  ||E||F/||A||F=0.31815  RMSE=0.2917  PSNR=24.42 dB
k= 3  存储 2k/N= 5.0%  ||E||F/||A||F=0.23359  RMSE=0.2142  PSNR=27.10 dB
k= 5  存储 2k/N= 8.3%  ||E||F/||A||F=0.19379  RMSE=0.1777  PSNR=28.72 dB
k= 8  存储 2k/N=13.3%  ||E||F/||A||F=0.11216  RMSE=0.1029  PSNR=33.47 dB
k=12  存储 2k/N=20.0%  ||E||F/||A||F=0.01524  RMSE=0.0140  PSNR=50.81 dB
k=20  存储 2k/N=33.3%  ||E||F/||A||F=0.00000  RMSE=0.0000  PSNR=201.71 dB
== (c) PCA ==
样本协方差 = [[3.0504, 2.0862], [2.0862, 2.0822]]
特征值 = [0.4247 4.708 ]  (真实协方差的理论值 4.5616 / 0.4384)
主方向 = [-0.783  -0.6221]  方差解释比 = [0.9173 0.0827]
SVD 对照: σ = [43.3414 13.0172]  sqrt((n-1)λ) = [13.0172 43.3414]

== (d) 截断伪逆 ==
噪声阈值 ≈ 3.750e-08;σ3=4.766e-08  σ4=3.758e-08
默认阈值 rank = 20  →  按噪声阈值截断 rank = 3

结论:

  1. (a) 部分的每一行都精确成立:∥A−Ak∥2=σk+1\lVert A-A_k\rVert_2=\sigma_{k+1}、∥A−Ak∥F=σk+12+⋯\lVert A-A_k\rVert_F=\sqrt{\sigma_{k+1}^2+\cdots},与输出中括号里的理论值逐位吻合到机器精度——这是 定理 2.6.4 的直接验证。注意误差随 kk 下降极快:k=10k=10 时为 1.8×10−21.8\times10^{-2}、k=20k=20 时 3.4×10−43.4\times10^{-4}、k=30k=30 时 6.1×10−66.1\times10^{-6},k=40k=40(满秩)时降到 3×10−153\times10^{-15}(机器精度)——因为构造时奇异值按指数衰减,前 kk 个就抓住了几乎全部能量(k=12k=12 已占 99.993%99.993%)。
  2. (b) 中合成图像的「奇异值谱形状」决定可压缩性:σ4,σ5,σ6,σ7≈10.15\sigma_4,\sigma_5,\sigma_6,\sigma_7\approx10.15 四个几乎相等(σ3=23.77\sigma_3=23.77 不在此列)(对应图像里那条亮带:它低秩,贡献几个几乎相同的奇异值,同时也是 sin⁡\sin 与 cos⁡\cos 项的重合之处);k=3→5k=3\to5 时 PSNR 只从 27.127.1 涨到 28.728.7(收益小),k=8→12k=8\to12 时却从 33.533.5 跳到 50.850.8(收益巨大,因为跨过了 10.14→3.610.14\to3.6 这段陡降)。k=20k=20 时 RMSE 已降到 4×10−104\times10^{-10}(PSNR 201.7201.7 dB,即机器精度级别)——该合成图像的数值秩只有 2121(前 2121 个奇异值超过 10−10σ110^{-10}\sigma_1),所以用 33%33% 的存储即可无损重建。可见压缩性能由奇异值的衰减速度(即矩阵的「有效秩」)决定,而与矩阵大小无关。
  3. (c) 中 SVD 与特征值分解给出同一答案:σi=(n−1)λi\sigma_i=\sqrt{(n-1)\lambda_i} 精确成立(43.3443.34 对 399×4.708\sqrt{399\times4.708} ✓)。但两者的排列顺序相反——eigh 输出升序、svd 输出降序。样本特征值 4.708/0.4254.708/0.425 与真实值 4.5616/0.43844.5616/0.4384 有偏差,这是采样波动(n=400n=400 的有限样本)造成的,不是算法误差。
  4. (d) 是本节最重要的「实践警告」:加噪后 σ3=4.77×10−8\sigma_3=4.77\times10^{-8}、σ4=3.76×10−8\sigma_4=3.76\times10^{-8}——两者都在噪声水平 3.75×10−83.75\times10^{-8} 附近。此时 np.linalg.matrix_rank 的默认阈值(max⁡(m,n)uσ1≈3×10−14\max(m,n)u\sigma_1\approx3\times10^{-14})判定 rank =20=20,完全错误;按噪声水平截断得 rank =3=3,才是真相。这就是「数值秩的判定必须依赖数据噪声水平」(2.6.4 思考题)的实测证据。

报告要求: ① 为 (a) 画 ∥A−Ak∥2\lVert A-A_k\rVert_2 与 σk+1\sigma_{k+1} 的重合图(二者应完全重合),再叠加一条「200 个随机秩 kk 矩阵的最小误差」曲线(它应始终在 σk+1\sigma_{k+1} 曲线上方);② 为 (b) 画「存储占比 vs PSNR」曲线,标出拐点,解释拐点与奇异值谱的对应;③ 为 (d) 把噪声水平从 10−1210^{-12} 扫到 10−610^{-6},画出「默认 rank 判定」与「噪声阈值 rank 判定」的对照曲线,指出两者开始分离的噪声水平(提示:当噪声超过 σ4\sigma_4 量级时)。

实验四(选做)· 幂法、Gerschgorin 与特征值敏感性

目的: 验证 2.5.3 的收敛速度 ∣λ2/λ1∣2\lvert\lambda_2/\lambda_1\rvert^2、定理 2.5.2 的圆盘围栏,以及 2.5.5 的「对称 O(ε)O(\varepsilon) vs 非正规 O(ε)O(\sqrt\varepsilon)」。预期看到:幂法相邻两次误差的比值稳定在 (λ2/λ1)2(\lambda_2/\lambda_1)^2;三个行圆盘罩住全部特征值;同一扰动量级下,非正规矩阵的特征值移动 ∼ε\sim\sqrt\varepsilon,对称矩阵却只有 ∼ε\sim\varepsilon。

import numpy as np
np.set_printoptions(precision=6, suppress=True)

# (a) 幂法 + Rayleigh 商:验证速度 (λ2/λ1)^2
A = np.array([[5., 2, 0], [2, 4, 1], [0, 1, 3]])
w = np.sort(np.abs(np.linalg.eigvals(A)))[::-1]
print("== (a) 幂法:|λ2/λ1| =", f"{w[1]/w[0]:.6f}", "  理论误差比 =",
      f"{(w[1]/w[0])**2:.6f}")
x = np.ones(3) / np.sqrt(3); prev = None
for k in range(1, 13):
    y = A @ x; x = y / np.linalg.norm(y); r = x @ A @ x
    ratio = "" if prev is None else f"  实测比值={abs(r-w[0])/prev:.4f}"
    prev = abs(r - w[0])
    print(f"  k={k:2d} Rayleigh={r:.10f} 误差={abs(r-w[0]):.3e}{ratio}")

# (b) Gerschgorin 圆盘
print()
print("== (b) Gerschgorin ==")
A = np.array([[10., -1, 2], [0, 4, 1], [-2, 1, 5]])
print("真实特征值 =", np.sort(np.linalg.eigvals(A)))
for i in range(3):
    r_i = np.abs(A[i]).sum() - abs(A[i, i])
    print(f"  行圆盘 {i+1}: 中心 {A[i,i]:5.1f}  半径 {r_i:4.1f}  "
          f"区间 [{A[i,i]-r_i:6.2f}, {A[i,i]+r_i:6.2f}]")

# (c) 特征值对扰动的敏感性
print()
print("== (c) 对称 vs Jordan ==")
J = np.array([[1., 1], [0, 1]]); S = np.array([[1., 0], [0, 2.]])
for t in [1e-2, 1e-4, 1e-8, 1e-12]:
    ej = np.linalg.eigvals(J + np.array([[0., 0], [t, 0]]))
    es = np.linalg.eigvalsh(S + np.array([[0., t], [t, 0]]))
    print(f"  t={t:.0e}  Jordan 移动={max(abs(ej-1)):.3e} (√t={np.sqrt(t):.1e})   "
          f"对称 移动={max(abs(es-np.array([1.,2.]))):.3e}")

预期结果与报告要求: (a) 输出中「实测比值」一列应稳定在 0.270.27 附近(理论值 0.27170.2717);若改用 ∥Ax∥\lVert A\boldsymbol{x}\rVert 计算特征值估计,比值约为 0.520.52(只有一阶收敛、慢一倍)。(b) 三个行圆盘 [8,12]∪[3,5]∪[2,8][8,12]\cup[3,5]\cup[2,8] 覆盖全部真特征值 {9.0687,6.2778,3.6535}{9.0687,6.2778,3.6535},且 [8,12][8,12] 孤立地只含 11 个特征值。(c) Jordan 矩阵的特征值移动量精确为 t\sqrt t,对称矩阵的特征值移动量约为 t2t^2——同一扰动量级下,tt 越小差得越悬殊:t=10−2t=10^{-2} 时约 10310^{3} 倍,t=10−4t=10^{-4} 时已达 10610^{6} 倍。报告要点:解释 (c) 中「为什么对称矩阵的移动量是 t2t^2 而不是 tt」(提示:本例扰动的一阶项 u1TEu1\boldsymbol{u}_1^{\mathrm T}E\boldsymbol{u}_1 恰为零,主导的是二阶项,见 2.5.4 的变分刻画)。