本节属“补充篇 · 数值线性代数工具箱”:2.x 是补充篇内部编号,不是课堂第 2 章的节号。课堂第 2 章是线性方程组的解法。
2.1 说「标准正交基下求解退化成做内积」,2.2 说「投影与最小二乘的关键是列空间的正交结构」——两句话指向同一个技术需求:把一组线性无关的向量造成标准正交基。最有用的产物是 QR 分解 A=QR(m>n 时 Q 为列正交的长方阵,见 定义 2.1.3):最小二乘、特征值、SVD 共用一个稳定内核。本节给出四种正交化方法(经典与修正 Gram–Schmidt、Householder 反射、Givens 旋转),并用实测数据说明取舍标准。
直觉 · 一步一步「减去投影」
想造一组互相垂直的向量,手上却只有一组歪斜的 a1,…,an。做法极其朴素:
- 拿第一个向量,拉成单位长,作为第一个坐标轴方向 q1;
- 拿第二个向量,减去它在 q1 上的投影(投影公式见 2.2.3),剩下的部分自然垂直于 q1;单位化,得 q2;
- 拿第三个向量,减去它在 q1 和 q2 上的投影,剩下的部分垂直于前两个;单位化,得 q3;
- 依此类推,直到第 n 个向量。
每加一个新轴,就把新向量里「已经用过的方向」全部扣掉,剩下的必然是全新的垂直方向。这就是 Gram–Schmidt。
算法 2.3.1 · 经典 Gram–Schmidt(CGS)
输入 线性无关向量组 a1,…,an(即 A 的列,A 列满秩)。
输出 标准正交组 q1,…,qn 与上三角矩阵 R,使 A=QR。
for j = 1, ..., n:
v = a_j # 从原始向量出发
for i = 1, ..., j-1:
r_ij = q_i^T a_j # 注意:用 q_i 内积【原始】a_j
v = v - r_ij * q_i
r_jj = ||v||_2
q_j = v / r_jj
复杂度: 每个 (i,j) 组合要做一次内积(2m flops)和一次向量修正(2m flops),共约 2mn2 flops(m≥n),与 QR 分解同量级。
定理 2.3.1(新增) · Gram–Schmidt 的正确性与 QR 分解
设 a1,…,an 线性无关(A=[a1,…,an]∈Rm×n,m≥n),则由算法 2.3.1 得到的 {q1,…,qn} 是标准正交组,且
A=QR,Q=[q1,…,qn]∈Rm×n (QTQ=In),R=[rij]∈Rn×n 上三角且对角元>0.(2.14)
证明
归纳证明正交性。 假设 q1,…,qj−1 已标准正交。算法中 v=aj−∑i<jrijqi。对任意 k<j,
qkTv=qkTaj−i<j∑rijδkiqkTqi=qkTaj−rkj=qkTaj−qkTaj=0.
(CGS 里 rkj=qkTaj 恰好就是 qk 在原向量上的系数,所以 qkTv=0 在精确算术下精确成立。)所以新向量与所有旧轴都正交;且 v=0(否则 aj∈span{q1,…,qj−1}=span{a1,…,aj−1},与线性无关矛盾),因此可以单位化。
A=QR: 由 v=rjjqj 与 v 的定义反解,
aj=rjjqj+rijqi+⋯+rj−1,jqj−1,
即 A 的第 j 列是 Q 前 j 列的线性组合,系数正好是 R 的第 j 列——这正是 A=QR。又 rjj=∥v∥2>0。
几何含义: Q 与 A 逐列张成同一个子空间,即 C(Q)=C(A),但 Q 的列标准正交;R 记录的是「正交坐标 → 原坐标」的换算。由上面的证明还能看出更强的结构:
span{q1,…,qk}=span{a1,…,ak}(k=1,…,n).(2.15)
(2.15)是 QR 分解区别于 SVD 的关键性质,也是 QR 迭代求特征值(原书第 3 章 3.3 节)的基础。
∎
例 2.3.1(新增) · 三向量的 Gram–Schmidt 手算
对
A=110101011
做 Gram–Schmidt,求出 Q 与 R(保留根号形式)。
解
第 1 列。 a1=(1,1,0)T,∥a1∥=2,故
r11=2,q1=21110.
第 2 列。 r12=q1Ta2=21⋅1+1⋅0+0⋅1=21。
v=a2−r12q1=101−21110=21−211.
∥v∥=41+41+1=26,r22=26,q2=611−12.
第 3 列。 r13=q1Ta3=20+1+0=21;r23=q2Ta3=61(0−1+2)=61。
v=a3−r13q1−r23q2=011−21110−611−12=−323232.
∥v∥=32,r33=32,q3=23⋅32−111=31−111.
结果:
Q=2121061−6162−313131,R=20021260216132.
核对: q1Tq2=121−1+0=0 ✓;q1Tq3=6−1+1+0=0 ✓;q2Tq3=18−1−1+2=0 ✓;三个范数均为 1 ✓;QR 能复原 A ✓。
注意 R 的对角元都为正——这是 定理 2.3.2 唯一性条款的来源。
手算口诀: 每一列先算它与已有各轴的内积(即 rij),逐个扣掉;剩余向量的长度就是 rjj。全程只需要「内积」和「减法」。
∎
定理 2.3.2(新增) · QR 分解的存在与唯一性
存在性: 设 A∈Rm×n(m≥n)列满秩,则存在 Q∈Rm×n 列正交(QTQ=In)与对角元为正的上三角 R∈Rn×n,使 A=QR。(由 定理 2.3.1 直接得到。)
唯一性: 在「R 的对角元全为正」的规范化下,这样的分解唯一。
全 QR(full QR): 若把 Q 补全为 m 阶正交方阵 Qˉ=[Q, Q⊥](Q⊥ 的列是 C(A)⊥=N(AT) 的标准正交基,见 2.2.1),则
A=QˉRˉ,Rˉ=(RO)∈Rm×n,
其中 Q⊥ 的列在乘积中不起作用,对应的系数是零块。
证明(唯一性)
设有两种分解 A=Q1R1=Q2R2,Q1TQ1=Q2TQ2=I,R1,R2 上三角且对角元为正。由 Q1 列满秩,Q2TQ1 是 n×n 可逆阵,且
Q2TQ1=R2R1−1=:S.
等号左边列正交、右边上三角(上三角阵的逆仍是上三角,乘积也是上三角),所以 S 是上三角阵。又由 Q1=Q2S 得
I=Q1TQ1=STQ2TQ2S=STS,
故 S 也是正交矩阵。上三角且正交的矩阵必为对角阵,对角元为 ±1:正交性要求 S 的各列是单位向量且两两正交;看 S 的第 1 列,它的第 2,…,n 个分量为零,于是只剩 s11=±1;归纳地看第 k 列,它的第 k+1,…,n 个分量为零,又要与前 k−1 列正交(那些列都只有第 i 个分量非零),于是也只剩 skk=±1。
最后由 S=R2R1−1 得 sii=rii(2)/rii(1)>0(对角元都为正),结合 ∣sii∣=1 得 sii=1。故 S=I,于是 Q1=Q2、R1=R2。
∎
秩亏时怎么办
若 A 不满秩,Gram–Schmidt 会在某一步得到 v=0(rjj=0):分解仍然存在,但不再唯一(唯一性证明中断在「R1 可逆」处)。实用做法是列主元 QR(column-pivoted QR):每一步把剩余部分范数最大的列换到前面,得到
AP=QR,∣r11∣≥∣r22∣≥⋯≥∣rnn∣,
这样得到的 ∣rkk∣ 还能给出秩的估计:它相对 ∣r11∣ 骤降的位置,就是数值秩的候选。这正是 LAPACK 的 geqp3 与 MATLAB qr(A,0) 走的路径。与 SVD 的对比见 2.6.5。
算法 2.3.1(CGS)在精确算术下完美无缺,在浮点下却有一个严重缺陷:它看起来很好,实际很差——正是 2.1 提到的「静默失效」的第二个实例。
直觉 · 差别只有一个「时态」
CGS 与修正 Gram–Schmidt(MGS)的代码只差一行:算 rij 时,CGS 用原始的 aj,MGS 用被前几轮修正过的 qj 当前值。
CGS:rij=qiTajMGS:rij=qiTv (v 是当前值)
精确算术下两者数学等价(内积值逐次相同)。但在浮点下,MGS 的每次内积都作用在「已经扣掉旧分量」的向量上,误差不会被后续步骤反复引用;CGS 恰好相反:每个 qj 携带的舍入误差都会被后面所有列的修正一遍遍引用,并在那些列上累积出一个 cond(A) 的额外因子。一行之差,稳定性量级相差一个 cond。
算法 2.3.2 · 修正 Gram–Schmidt(MGS)
for j = 1, ..., n:
v = a_j # 从原始向量出发
for i = 1, ..., j-1:
r_ij = q_i^T v # 注意:用 q_i 内积【当前】v
v = v - r_ij * q_i # v 被就地更新
r_jj = ||v||_2
q_j = v / r_jj
与 CGS 的唯一差别是:rij 用当前的 v(即 qj 的当前值),而不是原始的 aj。精确算术下两者给出的 Q,R 完全相同;浮点下 MGS 的正交误差小得多。
代价: 两者 flops 几乎相同(MGS 略多),但 MGS 的访存模式不如 CGS 适合向量化(CGS 的内积可以一次性对所有列批量做)。历史上因此出现过「CGS + 再正交化」方案:做两遍 CGS 就能达到 MGS 的量级。现代库则直接用 Householder。
实测对照 · 正交性损失(本章运行数据)
对 n×n Vandermonde 矩阵 A=[xij−1](xi 均匀取自 [0.1,1],cond2 随 n 快速增大)分别用三种方法做 QR 分解,报告正交性损失 ∥QTQ−I∥2:
| n |
cond2(A) |
CGS |
MGS |
Householder |
| 6 |
1.02×104 |
8.8×10−10 |
1.5×10−13 |
1.2×10−15 |
| 8 |
7.40×105 |
2.5×10−5 |
2.3×10−11 |
1.3×10−15 |
| 10 |
5.60×107 |
1.19 |
8.1×10−10 |
2.5×10−15 |
| 12 |
4.34×109 |
2.46 |
1.2×10−7 |
2.1×10−15 |
| 14 |
3.42×1011 |
4.48 |
4.7×10−6 |
2.2×10−15 |
| 16 |
2.73×1013 |
6.55 |
4.7×10−4 |
3.0×10−15 |
三点结论:
- Householder 永远 ∼u(10−15):与 cond(A) 完全无关。这是「每一步都用正交变换」的回报。
- MGS 按 cond(A)⋅u 增长:n=14 时 cond⋅u=3.8×10−5,实测 4.7×10−6;n=16 时 3.0×10−3 对 4.7×10−4——同量级 ✓。
- CGS 按 cond2(A)⋅u 增长,随后饱和:n=10 时 cond2u=3.5×10−1、实测已到 1.2;n≥12 时 cond2u≫1,实测停在 O(1)∼O(10)——正交性完全丧失(∥QTQ−I∥2 的平凡上界是 n+1,n=16 时为 17)。
分界线: cond(A)2u≳1,即 cond(A)≳u−1/2≈108(双精度)。超过它,CGS 彻底失效;MGS 还能撑到 cond≈u−1≈1016。结论:要用 Gram–Schmidt 就必须用 MGS;要最好的正交性,用 Householder。
例 2.3.2(新增) · 亲手看见 CGS 失效(数值实验)
用 Python 复现上面这张表,并回答:为什么 CGS 的 ∥QTQ−I∥ 会「饱和」在 O(1),而 MGS 不会?
解
import numpy as np
u = np.finfo(float).eps / 2 # 单位舍入误差 ≈ 1.11e-16
def cgs(A): # 经典: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] @ 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): # 修正: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
print(f"{'n':>3} {'cond':>10} {'CGS':>11} {'MGS':>11} {'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(n))
print(f"{n:3d} {c:10.2e} {f(cgs(A)[0]):11.2e} {f(mgs(A)[0]):11.2e} {c*u:10.2e} {c*c*u:10.2e}")
n cond CGS MGS cond*u cond^2*u
6 1.02e+04 8.78e-10 1.47e-13 1.1e-12 1.2e-08
8 7.40e+05 2.51e-05 2.28e-11 8.2e-11 6.1e-05
10 5.60e+07 1.19e+00 8.11e-10 6.2e-09 3.5e-01
12 4.35e+09 2.47e+00 1.24e-07 4.8e-07 2.1e+03
14 3.42e+11 4.48e+00 4.70e-06 3.8e-05 1.3e+07
16 2.73e+13 6.55e+00 4.70e-04 3.0e-03 8.3e+10
为什么 CGS 饱和而 MGS 不饱和?
(i)误差的「引用结构」不同。 CGS 的系数 rij 用原始 aj 计算,而 aj 在第 j 步之前从未被修正过;于是 qj 一旦带上舍入误差,这个误差就会被后来所有更大下标 j′ 的系数计算反复引用,按 cond 逐层累积。而 MGS 每一步用的都是已经扣掉旧分量的当前向量,误差只被引用一次。
(ii)「饱和」只是平凡的几何效应,不代表安全。 ∥QTQ−I∥2 的最大可能值是 ∥QTQ∥2+1≤∥Q∥22+1。当 CGS 算出的 Q^ 已经几乎不正交(各列趋于平行)时,∥Q^∥2 增长到 n 量级,这个指标就只能停在 O(n)——错到顶了,再坏也坏不下去。
n=10 时理论值 cond2u=0.35 尚小于 1,实测 1.19 已饱和;n≥12 时理论值远大于 1,实测停在 2.5∼6.6。所以「不再增长」恰恰是最坏的情形。
验收标准: 输出应与上表同量级(逐位因平台的 BLAS 实现而异);把 x 换成更病态的分布(如几何分布 xi=0.5i−1),观察 cond 与表中 CGS、MGS 两列误差是否同步增大。
∎
MGS 补上了 CGS 的漏洞,误差却仍按 cond⋅u 增长。要彻底摆脱条件数,就得让每一步都是正交变换——正交变换不放大误差(定理 2.1.6)。Householder 反射是这种做法最简洁的实现。
直觉 · 用一面镜子把向量「拍」到坐标轴上
给你 x∈Rk,想把它变成 ±∥x∥2e1(除第一个分量外全部清零)。怎么办?
找一面镜子。 镜像保长度,所以 x 只能映成同样长度的 ±∥x∥e1。镜面的法线应沿着「x 与它的像的中点方向」——因为入射向量与出射向量关于镜面对称。Householder 反射就是这面镜子:
H=I−2vTvvvT.
H 对称、正交(HTH=I)、且 H2=I(照两次镜子回到原处),detH=−1。一次矩阵乘法,就能消掉一整列的对角线以下元素。
定义 2.3.1 · Householder 变换
设 v∈Rk、v=0,称
H=I−vTv2vvT∈Rk×k(2.16)
为 Householder 变换(或初等反射矩阵),v 为反射向量。
定理 2.3.3(新增) · Householder 变换的性质
H 由(2.16)定义,则
- 对称: HT=H;
- 正交: HTH=I,故 H−1=H、H2=I;
- 反射几何: 对任意 x,Hx 是 x 关于超平面 v⊥={y:vTy=0} 的镜像;特别地 Hv=−v,而 v⊥ 上的向量不动;
- 行列式: detH=−1;
- 特征值: −1(一重,特征向量 v)与 1(k−1 重,特征空间 v⊥)。
证明
记 β=2/(vTv),H=I−βvvT。
1: (vvT)T=vvT,故 HT=H。
2: 用 HT=H,
HTH=H2=I−2βvvT+β2v(vTv)vT=I−2βvvT+β2⋅β2vvT=I.
3: 对任意 x,分解 x=x∥+x⊥,其中 x∥=vTvvTxv、x⊥⊥v。则
Hx=x−vTv2vTxv=x−2x∥=−x∥+x⊥,
即 v 方向翻转、v⊥ 方向不动——正是镜像。
4、5: 由 3,Hv=−v;u⊥v⇒Hu=u,而 dimv⊥=k−1。故特征值为 −1(一重)与 1(k−1 重),detH=(−1)⋅1k−1=−1。
∎
算法 2.3.3 · 用一个 Householder 变换消去一列的对角线以下元素
给定 x∈Rk(k≥2,x=0),构造 H 使 Hx=αe1。
目标只能取 α=±∥x∥2(保长度)。取
α=−sgn(x1)∥x∥2(规定 x1=0 时 sgn(x1)=1),
v=x−αe1=x+sgn(x1)∥x∥2e1,
则 H=I−2vvT/(vTv) 满足 Hx=αe1。
为什么这样取符号? 若取 α=+∥x∥,当 x1>0 时 v 的首分量就是 x1−∥x∥,这是两个相近数相减(1.2.4 原则 (3))。反过来取符号后,首分量变成 x1+sgn(x1)∥x∥,是相加,永远不损失有效数字。这是数值分析里反复出现的符号选择技巧:能用加就不用减。
例 2.3.3(新增) · 手算一个 Householder 反射
对 x=(3,4)T 构造 Householder 矩阵使 Hx=αe1。
解
∥x∥2=9+16=5,x1=3>0,故 α=−5。
v=x−αe1=(34)−(−50)=(84).
vTv=64+16=80,H=I−802(64323216)=(1001)−(1.60.80.80.4)=(−0.6−0.8−0.80.6).
验证: Hx=(−1.8−3.2−2.4+2.4)=(−50)=−5e1 ✓。
验证性质: HT=H ✓;H2=(0.36+0.640.48−0.480.48−0.480.64+0.36)=I ✓;detH=(−0.6)(0.6)−(−0.8)(−0.8)=−0.36−0.64=−1 ✓。
几何: H 是以 v=(8,4)T(斜率 1/2)为法线的镜面反射,把 (3,4)T 映成 (−5,0)T——长度不变,方向关于镜面对称。
规范化形式: 令 u=v/∥v∥=(2/5, 1/5)T,则 H=I−2uuT=(1−8/5−4/5−4/51−2/5),与上面的结果相同 ✓。代码里用规范化形式更稳(避免 vTv 上溢 / 下溢)。
∎
算法 2.3.4 · 用 Householder 做 QR 分解
对 A∈Rm×n(m≥n),逐列消元:
R = copy(A); Q = I_m
for k = 1, ..., n:
x = R[k:m, k] # 当前列从对角线开始的片段
if ||x|| == 0: continue
alpha = -sign(x_1) * ||x||_2
v = x - alpha * e_1
v = v / ||v||_2 # 规范化,避免溢出
R[k:m, k:n] = R[k:m, k:n] - 2*v*(v' * R[k:m, k:n]) # 左乘 H_k
Q[:, k:m] = Q[:, k:m] - 2*(Q[:, k:m]*v)*v' # 累积 Q = H_1 H_2 ... H_n
第 k 步把第 k 列中第 k+1,…,m 个元素全部化为零,同时不破坏已经产生的零。最终 R 上三角,Q 是 m 阶正交阵(只保留前 n 列,就得到「瘦」QR)。
复杂度: 约 2mn2−32n3 flops,与 Gram–Schmidt 同量级,但正交性总是 ∼u,与 cond(A) 无关。
例 2.3.4(新增) · 用 Householder 做一次 QR
对 A=(3412) 做 Householder QR。
解
第 1 步。 x=(3,4)T,由例 2.3.3 已得
H1=(−0.6−0.8−0.80.6),H1A=(−1.8−3.2−2.4+2.4−0.6−1.6−0.8+1.2)=(−50−2.20.4).
2×2 情形第 1 步即完成:R=(−50−2.20.4),Q=H1T=H1。
验证 A=QR: H1R=(34−0.6(−2.2)−0.8(0.4)−0.8(−2.2)+0.6(0.4))=(341.32−0.321.76+0.24)=(3412) ✓。
注意 R 的对角元是负的(r11=−5)。要化成「对角元为正」的规范形式,把 Q 的第 1 列与 R 的第 1 行同时变号(R→diag(−1,1)R、Q→Qdiag(−1,1),QR 不变)。工程上常直接允许负对角元(LAPACK 不保证正号),因为唯一性只在规范形式下成立。
∎
Householder 一次消掉一整列,效率高,但不局部:它要改动整个子块 Rk:m,k:n。矩阵稀疏,或者只需消掉少数几个元素时,Householder 会把零结构破坏掉——这时该改用 Givens 旋转。
直觉 · 用一次平面旋转「点掉」一个元素
想把 (x1,x2)T 变成 (r,0)T?这正是 2.1 的平面旋转矩阵 Rθ:取合适的 θ 就能把它「转」到 x1 轴上:
(c−ssc)(x1x2)=(r0),c=rx1, s=rx2, r=x12+x22 (c2+s2=1).
一次只消一个元素,而且想消哪个就消哪个——这正是稀疏矩阵需要的。Givens 是「外科手术」,Householder 是「大扫除」。
定义 2.3.2 · Givens 旋转
设 c2+s2=1。在 Rk 中只作用在第 i,j 两个坐标上的矩阵 G(i,j,c,s)(单位阵基础上把 (i,i),(i,j),(j,i),(j,j) 位置改为 c,s,−s,c)称为 Givens 旋转(或 Givens 变换)。它是正交矩阵,且 detG=1(保定向,与 Householder 相反)。
定理 2.3.4(新增) · Givens 旋转消元
给定 x=(x1,x2)T=0,取
r=x12+x22 (实际用 hypot),c=rx1,s=rx2,
则 (c−ssc)(x1x2)=(r0)。若 x2=0,取 c=1, s=0。
证明
直接代入:cx1+sx2=rx12+x22=r,−sx1+cx2=r−x2x1+x1x2=0。正交性:c2+s2=(x12+x22)/r2=1。
∎
算 r 时不要写成 x12+x22
直接算 x12+x22,当 x 的分量接近 10154 时会上溢(超出 double 范围),接近 10−160 时会下溢成零。正确做法是用 hypot:
r=x12+x22 ⟶ r=hypot(x1,x2)(先按量级缩放再开方).
Python 用 math.hypot / np.hypot,C 用 hypot(C99 标准)。同理:∥x∥2 在代码里用 np.linalg.norm(内部做缩放),不要手写 np.sqrt(x @ x)。 这是「浮点可表示范围有限」(1.2.5)带来的工程细节。
例 2.3.5(新增) · 手算一个 Givens 旋转
用 Givens 旋转把 A=(3443) 化为上三角。
解
对第 1 列 (3,4)T 消元:r=5,c=3/5=0.6,s=4/5=0.8。
G=(0.6−0.80.80.6),GA=(1.8+3.2−2.4+2.42.4+2.4−3.2+1.8)=(504.8−1.4).
2×2 已上三角:R=(504.8−1.4),Q=GT=(0.60.8−0.80.6)。
验证 A=QR: QR=(0.60.8−0.80.6)(504.8−1.4)=(342.88+1.123.84−0.84)=(3443) ✓。
与 Householder 对比: 同一个矩阵,Householder 给出的 Q 满足 detQ=−1(含反射),Givens 给出 detQ=+1(纯旋转)。两者都能完成 QR,选哪个取决于要消多少元素:本例 2×2 只需一次旋转,成本相同;但若是 1000×1000 的三对角矩阵,「只动两行」的局部性才是 Givens 的杀手锏。
∎
四种正交化方法的对比(本节结论)
| 方法 |
一次消多少 |
flops(m×n,m≥n) |
∥QTQ−I∥2 |
适用场合 |
| CGS |
一列(逐步投影) |
2mn2 |
cond2u(勿用于生产) |
教学、理论推导 |
| MGS |
一列(逐步投影) |
2mn2 |
condu |
需要显式 Q、条件数温和 |
| Householder |
一整列(>1 个元素) |
2mn2−32n3 |
u(与 cond 无关) |
稠密矩阵的默认选择 |
| Givens |
一个元素 |
约 6mn2(3 倍) |
u(与 cond 无关) |
稀疏 / 带状 / 局部修改 / 并行 |
口诀:稠密用 Householder,稀疏用 Givens;MGS 在条件数温和且需要 Q 的显式列时可用;CGS 只用于教学。 LAPACK 的 geqrf(Householder)是 NumPy np.linalg.qr、MATLAB qr、SciPy 的共同默认路径。
课程预告 · QR 的另一个大用途
本节把 QR 当作「解最小二乘的稳定工具」。QR 还有一个更深刻的应用:QR 迭代求全部特征值(原书第 3 章 3.3 节)。核心思路正是(2.15)后面的观察——A=QR⇒A′=RQ=QTAQ 与 A 相似(特征值不变),反复迭代后 R 的对角元趋于特征值。「QR 分解」与「QR 方法」是两件事,但后者完全建立在前者之上。
直觉 · 把「平方」消掉
正态方程 ATAx=ATb 的毛病出在 cond(ATA)=cond(A)2(ATA 的奇异值是 A 的奇异值的平方,见 2.6.3):条件数一平方,有效数字就掉一半。
QR 的做法是代入 A=QR,利用正交不变性(定理 2.1.6):
∥Ax−b∥22=∥QRx−b∥22=∥Rx−QTb∥22,
于是问题变成解一个上三角方程组,从头到尾都不形成 ATA:条件数保持 cond(A),没有被平方。
定理 2.3.5(新增) · QR 解最小二乘与误差量级
设 A∈Rm×n(m≥n)列满秩,取其全 QR 分解 A=Qˉ(RO)(Qˉ 为 m 阶正交,R 为 n 阶非奇异上三角)。把 QˉTb 分块为 (cd)(c∈Rn、d∈Rm−n),则
∥Ax−b∥22=∥Rx−c∥22+∥d∥22.
因此最小二乘解由上三角方程组 Rx^=c 给出(回代,O(n2)),最小残差范数为 ∥d∥2。误差量级:
正态方程:∥x∥∥x^−x∥∼cond(A)2u;QR:∥x∥∥x^−x∥∼cond(A)u.
证明
分解最小化问题: 由 定理 2.1.6(正交矩阵保 2-范数),
∥Ax−b∥2=∥QˉT(Ax−b)∥2=(RO)x−(cd)2=(Rx−c−d)2.
取平方即得。第二项与 x 无关,故只能令第一项为零;R 非奇异(A 列满秩)故解唯一,最小值 ∥d∥22。
误差量级(要点): 正态方程显式形成并求解一个条件数为 cond(A)2 的问题,任何后向稳定的解法(如 Cholesky)只能保证相对误差 ∼cond(A)2u;QR 路径中所有变换都是正交的(定理 2.1.6:不放大误差),误差只经过 cond(R)=cond(A) 一次。严格的后向误差分析见 Higham《Accuracy and Stability of Numerical Algorithms》§20.3–20.5。
∎
实测对照 · QR vs 正态方程(本章运行数据)
取 Vandermonde 设计矩阵与真解 c=1,构造相容右端 b=Ac(残差理论上为零),比较两种方法恢复 c 的最大分量误差:
| m |
n |
cond(A) |
正态方程误差 |
QR 误差 |
| 12 |
10 |
6.84×106 |
1.3×10−4 |
5.4×10−11 |
| 20 |
15 |
6.03×1010 |
2.8×101(完全失效) |
2.2×10−6 |
第二行是最重要的一格: 条件数 6×1010 时,正态方程解出的系数误差达到 28——相比真值 1 毫无意义;QR 仍给出 10−6 量级。理论预测: 正态方程 cond2u≈(6.03×1010)2×1.1×10−16≈4×105(实测 28,误差被部分抵消但仍不可用);QR 为 cond⋅u≈6×10−6,实测 2.2×10−6 ✓。
结论:条件数超过约 108≈u−1/2 时,必须放弃正态方程,改用 QR 或 SVD。 这是「公式最简的写法未必是最稳定的算法」(1.1.3)在矩阵层面的重演。
例 2.3.6(新增) · 用 QR 解一个最小二乘问题
用 QR 分解求下面方程组的最小二乘解:
110101(x1x2)=211.
解
记 A=110101、b=(2,1,1)T。用 Householder 化 A(数值结果,符号可能因实现而异):
Q=−0.7071−0.707100.4082−0.40820.8165,R=(−1.41420−0.70711.2247),QTb=(−2.12131.2247).
回代解 Rx^=QTb: 由第二行 1.2247x2=1.2247⇒x2=1;由第一行 −1.4142x1−0.7071x2=−2.1213⇒x1=−1.4142−2.1213+0.7071=1。
x^=(1,1)T.
核对: Ax^=(1,1,0)T+(1,0,1)T=(2,1,1)T=b——残差为零,这说明 b∈C(A),最小二乘解恰好就是精确解。
换一个右端: 取 b=(1,1,1)T,此时 b∈/C(A)。QTb=(−1.4142, 0.8165)T,解得 x2=0.6667、x1=0.6667,即 x^=(0.6667,0.6667)T=(32,32)T(正态方程 (2112)x=(2,2)T 的解),残差 ∥Ax^−b∥2=0.5774=1/3 ✓,且 ATr=0 成立。
∎
本节公式速查
| 概念 |
公式 / 结论 |
| CGS 系数 |
rij=qiTaj(原始列) |
| MGS 系数 |
rij=qiTv(当前列)← 数值上必须用它 |
| QR 分解 |
A=QR,QTQ=I,R 上三角;对角元为正时唯一 |
| 分层张成 |
span{q1..qk}=span{a1..ak} |
| Householder |
H=I−2vvT/vTv;HT=H,H2=I,detH=−1 |
| 消元向量 |
v=x+sgn(x1)∥x∥2e1,α=−sgn(x1)∥x∥2 |
| Givens |
c=x1/r,s=x2/r,r=hypot(x1,x2);detG=+1 |
| 正交性损失 |
CGS:cond2u;MGS:condu;Householder / Givens:u |
| 复杂度 |
GS:2mn2;Householder:2mn2−32n3;Givens:约 6mn2 |
| QR 最小二乘 |
Rx^=(QTb)1:n;∥r∥2=∥d∥2 |
常见错误清单 · 自查七条
- 把经典 Gram–Schmidt 当生产代码。 条件数超过 108 时,CGS 给出的 Q 完全不正交(见 2.3.3 的表格),后续所有基于 Q 的结论同时失效。代码里请用 Householder(
np.linalg.qr),至少也要用 MGS。
- MGS 写成 CGS。 两者只差「内积用原始列还是当前列」,数学上等价,数值上差别巨大。写代码时必须逐列就地更新,不能先留一份原始备份再用备份算系数。
- Householder 消元向量取错符号。 若取 α=+∥x∥ 且 x1>0,v 的首分量 x1−∥x∥ 是相近数相减,损失有效数字(1.2.4)。正确取 α=−sgn(x1)∥x∥,让首分量变成相加。
- 手写 x12+x22 算 Givens 的 r。 会溢出 / 下溢;用
hypot。同理 ∥x∥2 用 np.linalg.norm,别手写 sqrt(x@x)。
- 以为 QR 分解唯一。 只有「R 对角元为正」的规范形式才唯一;LAPACK 与 NumPy 允许负对角元,所以不同库给出的 Q,R 可以差若干列的符号。比较 Q,R 前先统一符号,或改为比较 QR 与 A、QTQ 与 I。
- 用 QR 解最小二乘时把整个 Qˉ 都算出来。 如果只要解,可以只存 Householder 向量(O(mn) 内存)并直接作用在 b 上,不必形成 m×m 的 Qˉ(O(m2) 内存)——这是大规模最小二乘的标准优化。
- 秩亏时仍用普通 QR 判断秩。 A 秩亏或接近秩亏时,R 的对角元只会出现「很小但不为零」的值,靠它判断秩并不可靠——请用列主元 QR(定理 2.3.2 后的注)或 SVD(2.6.5)。
课程预告 · 本节之后
- 「cond 平方」这个坑有多深?下节 2.4 把条件数讲成「能保住几位有效数字」的定量工具;
- R 的对角元为什么能判秩?2.6 用 SVD 给出更可靠、更几何的判据;
- QR 迭代求特征值:原书第 3 章 3.3 节;列主元 QR:原书第 2 章 2.2.2 与 2.6.5。