补充篇 · 习题集
本页属“补充篇 · 数值线性代数工具箱”,不是课堂第 2 章;课堂第 2 章见线性方程组的解法。
本章习题按难度分四组:A 组概念判断(快速自检)、B 组推导证明(考试大题)、C 组计算与编程(动手实操)、D 组开放建模(迁移综合)。四组均附解答或提示(默认折叠),C 组每题另给验收标准。所有题目只使用第一章与第二章已讲的概念(外加 numpy / math),不要求后续章节的方法。
A 组 · 概念判断
判断对错并说明理由: (a) 若 且 ,则 正交; (b) 上任一范数都由某个内积诱导; (c) 长方阵 ()满足 ,则 ; (d) 两个正交矩阵之和仍是正交矩阵。
解答
本题在考:内积与范数的分工——范数要满足什么条件才来自内积,「标准」与「正交」又为什么是两件事。
(a) 错。 反例要在「两个范数都为 1、、但内积不为零」的前提下找:、 正交 ✓,不合用;而 会被 排除。改取 、,只要 ,两个范数都是 1、两向量又不同,而内积 。范数都是 1 只说明「标准」,正交必须单独验证内积为零(例 2.1.3 的教训)。
(b) 错。 只有满足平行四边形恒等式(2.2)的范数才来自内积(定理 2.1.2)。 与 都不满足(例 2.1.2),故它们不来自任何内积。推论: 没有正交、投影、SVD 这套结构。
(c) 错。 只说明列标准正交。 是 上的投影矩阵(投影到 上),,故 (定理 2.2.5、2.1.4 常见错误 4)。
(d) 错。 反例:,零矩阵不正交(,且 )。更一般地,正交矩阵对加法不封闭(对乘法才封闭:)。
判断对错并说明理由: (a) 若 ,则 一定可逆; (b) 是正交投影矩阵 ; (c) 最小二乘解总是存在且唯一; (d) 很小意味着 很接近真解。
解答
本题在考:最小二乘解的存在性与唯一性各需要什么条件,以及「残差小」为什么推不出「解准」。
(a) 错。 可逆 列满秩(定理 2.2.5 证明第一段)。若 列相关(如 ), 奇异。
(b) 错。 正交投影 对称 + 幂等。只幂等的是斜投影,它的余项不与子空间垂直。反例: 满足 但 (2.2.6 常见错误 5)。
(c) 前半对、后半错。 最小二乘解总存在(正态方程的右端 ,定理 2.2.6);但唯一 列满秩。秩亏时解成一整个集合(仿射子空间),此时「最小范数解」才唯一——由伪逆给出(定理 2.6.3、例 2.6.5)。
(d) 错。 「残差小 ≠ 误差小」。定量关系是 (定理 2.4.4);条件数大时残差可以极小而误差极大(例 2.4.4 实测:残差 、误差 )。
判断对错并说明理由: (a) 经典 Gram–Schmidt 与修正 Gram–Schmidt 在浮点下结果相同; (b) Householder 反射的行列式是 ; (c) 稀疏矩阵适合用 Householder 做 QR; (d) QR 分解唯一。
解答
本题在考:三种正交化方法各自的适用场景与浮点行为——精确算术下相同,不代表浮点下也相同,分解的唯一性也只在规范形式下成立。
(a) 错。 精确算术下两者给出完全相同的 ,但浮点下 CGS 的正交性损失是 、MGS 是 ——差一个条件数(2.3.3 实测: 时 vs )。
(b) 错。 ( 的特征值是 与 重的 ,定理 2.3.3)。 是反射(反定向); 的是 Givens 旋转(定义 2.3.2)。
(c) 错。 Householder 一次改动整个子块,会毁掉稀疏结构(fill-in)。稀疏矩阵应用 Givens 旋转(一次只改两行,定理 2.3.4 后的对比表)。
(d) 错(原命题不够严格)。 只有加上「 对角元全为正」的规范化才唯一(定理 2.3.2);一般地,、 保持 不变。LAPACK 与 NumPy 允许负对角元,所以不同库的 可以差若干列的符号。秩亏时更是完全不唯一。
判断对错并说明理由: (a) 只依赖 是否接近奇异,与行列向量的整体尺度无关; (b) 对任何矩阵成立; (c) 对长方形矩阵也成立( 列满秩); (d) 若 ,则 单调递减到零。
解答
本题在考:条件数、谱半径与奇异值三者的适用范围——涉及它们的关系式,大都是「在特定假设下」才成立的等式。
(a) 对。 (定理 2.4.1 第 3 条)——条件数对整体缩放免疫。但要注意各行 / 各列的相对尺度会影响 (行均衡化能降低它,例 2.4.5): 衡量的是「列 / 行向量之间的接近平行程度」,不是元素绝对大小。
(b) 错。 只有正规矩阵(含对称、正交、对称半正定)才有 。反例: 的 而 ;一般只有 (2.5.1)。
(c) 对。 对列满秩长方形矩阵,, 或 的谱给出 (其中 )。「条件数」这个概念用在长方形矩阵上时,要靠「相对于列空间的敏感度」来定义(2.4.1、2.6.3)。
(d) 错(不成立)。 只保证 (渐近),不保证单调递减——非正规矩阵存在「瞬态增长」(例 2.5.6)。反例: 的 ,但 可以很大。稳妥判据: 才保证每一步收缩。
B 组 · 推导证明
补全 定理 2.1.5 中未证明的等价关系,具体是三件事:由 的行向量组标准正交推出 ;补齐「由 (对一切 )推出 」的代数细节;以及证明「上三角且正交的矩阵必为对角阵,对角元为 」。
解答
行标准正交 : 行标准正交即 (逐元素是行内积 )。由 得 可逆(),左乘 :。再右乘 得 ✓。
: 两边平方: 对一切 ,即 对一切 ,其中 对称。
- 取 :;
- 取 ():,代入 得 。
故 ,即 ✓。(这个「用对称性 + 特殊向量取极化」的技巧是标准套路。)
上三角 + 正交 对角、对角元 : 设 为上三角矩阵且 (这里的 是新记号,与上一段无关)。看 的第 1 列 :上三角给 ,单位向量给 。归纳: 设第 列已证是 。第 列只有前 个分量可能非零(上三角),且要与 ()正交,故 ,再由单位向量得 。故 是对角、对角元 ✓。(这正是 定理 2.3.2 唯一性证明的关键一步。)
证明:实对称矩阵的全部特征值为实数,且存在正交矩阵 使 。(本题只要求完整证明前半句与「特征向量可正交化」;后半句可用 Schur 形式 + 归纳得到。)
解答
特征值实: 设 , 非零。两边左乘 (共轭转置):
两边取共轭转置,用 、:
两式相减:。由 得 ,即 ✓。
不同特征值的特征向量正交: 设 、,(都实)。则
重特征值情形: 若 有重数 ,其特征子空间维数恰为 (对称矩阵可对角化——由 Schur 形式 且 对称蕴含 对称、对称的上三角阵是对角阵)。在该子空间内任取标准正交基(Gram–Schmidt,2.3.1)。
拼起来: 各特征子空间两两正交、内部标准正交,合起来有 个标准正交向量,构成 。此时 ✓。
(关键的一步是「对称矩阵可对角化」——这可从 Schur 形式直接得到:任何方阵可写成 ( 正交、 上三角),而 迫使 ,上三角且对称者必为对角阵。)
设 列满秩,。证明: (1) 、; (2) ; (3) 对任意 ,,且 ; (4) 是到 上的正交投影。
解答
(1) ( 对称 其逆对称)✓。 ✓。
(2) 对称 可正交对角化(定理 2.5.1 第 5 条);幂等 特征值 ()。故 非零特征值个数 (因为 : 把 投到 上,且 在 上是恒等映射)。
(3) 由 与 (验证:)得勾股:
取 时 ,故 ;结合上式得 ✓。
(4) 对称((1))、幂等(),故是正交投影。其列空间: ✓(因为 );反过来任意 满足 。故 ✓。
证明:满足 、、、 的矩阵 唯一(即 是唯一解)。然后验证 确实满足四条件。
解答
存在性(验证 ): 关键恒等式:、、 与 都是(对称的)对角 阵。于是
(用了 、);同法 ✓。
唯一性: 设 都满足四条件。记 、(都对称且幂等,是到 上的投影):
于是 (左边 ),故 。再取转置:。
也可以直接代入四条件往下算:(用 ),只是会越算越繁;更简洁的标准做法是利用 与 :
(第一步、最后一步用 ;第三步用 ;第五步用 。)故唯一 ✓。
设 非奇异, 是精确解, 是近似解,,。证明
并说明为什么这两条的方向不对称,「残差小」不能单独作为「解准」的判据。
解答
(i) 由 与 得 ,取范数并使用相容性:
(ii) 由 得 ,故 。又由 得 ,即 。两式相乘:
方向不对称的原因: (i)是「误差小 ⟹ 残差小」,放缩只用 ,系数有界;而(ii)是「残差小 ⟹ 误差小」,放缩里出现了 ——它可以是任意大。
这就是为什么残差不能当误差的代理指标: 求解器给出的是(i)方向的保证(「我把方程解得很好」),用户想要的是(ii)方向的保证(「我的解很准」);两者之间恰好隔着一个 。当 时,残差 只保证误差 。
补充(条件化的反向界): 若额外假设 ,则可从 出发得到带修正因子的版本
但这个修正版的实用价值远低于(ii)——因为分母要求条件数已经足够小,而那正是「不需要担心」的场景。记住(ii)就够:残差要乘上 才是误差的界。
证明(2.35):,最小值由 达到。(提示:用奇异值的 Weyl 不等式 ,以及 的方向。)
解答
上界( 达到): 的奇异值是 (其余为零),故
下界(任何 都不更好): 设 ,则 至多有 个非零奇异值,即 。由 Weyl 不等式(谱范数版,即 定理 2.5.6 的「一」应用到 与 的平方根):
于是对 :。这对每个 成立,故
(第一个不等号用「奇异值的 距离不超过矩阵的 F-范数」这一 Sylvester 型不等式:,它是 von Neumann 迹不等式(例 2.6.4)的推论。)
两边开方即得下界,与上界重合 ✓。等号成立条件: 需要 的奇异向量与 在 上对齐——这正是 。
(注:,所以 Frobenius 版的下界比谱范数版弱,但结论中的最小值恰好是同一个 达到——这体现了 同时最优于两个范数。)
C 组 · 计算与编程
对
(1) 手算 与其特征值,写出全部奇异值与 ; (2) 写出 、(用 )并验证 ; (3) 写出 、、、 的标准正交基。
解答
(1)
特征值:,即 。故
(核对 : ✓。)
(2) 的特征向量:,。:,。
验证正交(必查): ✓;、 ✓。
验证 (用外积形式):
第一项系数 ,故
(3) 四个子空间(由 定理 2.6.2):
- (维数 );
- , 是与 都正交的单位向量:由 得 ,由 得 ,故 ,即 (维数 );
- (维数 );
- (维数 ,因为 列满秩)。
核对: ✓(); ✓; ✓。
验收标准: NumPy 复核——np.linalg.svd(A) 给出 ;A.T@A 的特征值为 ;np.linalg.cond(A) ;四个子空间的维数与上述一致。
对
(1) 用 Gram–Schmidt 求 (保留根号); (2) 用 Householder 反射求 (写出 ,允许 对角元为负),并说明两次结果是否一致。
解答
(1) Gram–Schmidt。 ,,
,
核对: ✓; 复原 ✓。
(2) Householder。 第 1 列 ,, 故取 (规定 时 ):
,故 :
第 2 步:对 消元。, 故 ,
数值结果(NumPy np.linalg.qr 内部同样用 Householder,与手算同一算法):
(3) 两次结果比较。 把 Gram–Schmidt 的结果(第 1 问)写成数值:
逐列对照:
- 第 1 列: 的第 1 列 的第 1 列;
- 第 2 列: 的第 2 列 的第 2 列(完全相同)。
原因: Householder 的 为负(未采纳「 对角元为正」的规范化),故 的第 1 列与 的第 1 行同时取了相反符号;而 ,第 2 列不动。两者只差一个「列符号规范化」,去掉这个差异后完全一致——这正是 定理 2.3.2 唯一性条款的含义:唯一的只有规范形式。
验收标准: 用 NumPy 验证两种方法的 都满足 、;把 的第 1 列取反后应与 逐元素一致(到机器精度)。
对下面这个「问题不好」的最小二乘问题:
(1) 手算正态方程并说明 的条件数; (2) 用 QR 与正态方程分别求 (数值); (3) 解释为什么这个问题病态,并给出「把间距缩小到 后谁先失守」的实测结论。
解答
(1) 正态方程。 记设计点 ()。
用行列式公式算条件数:,非常接近退化。
(2)数值结果(本节运行环境)——结果出人意料:
import numpy as np
for d in [1e-4, 1e-6]:
x = np.array([1., 1+d, 1+2*d, 1+3*d])
A = np.vstack([np.ones(4), x]).T
b = np.array([1., 2, 3, 4])
Q, R = np.linalg.qr(A)
print(f"d={d:.0e} cond(A)={np.linalg.cond(A):.3e} cond(A^T A)={np.linalg.cond(A.T@A):.3e}")
print(f" QR : {np.linalg.solve(R, Q.T@b)}")
print(f" 正态方程: {np.linalg.solve(A.T@A, A.T@b)}")
print(f" lstsq : {np.linalg.lstsq(A, b, rcond=None)[0]}")
d=1e-04 cond(A)=1.789e+04 cond(A^T A)=3.201e+08
QR : [-9999. 10000.]
正态方程: [-9999.00024151 10000.00024148]
lstsq : [-9999. 10000.]
d=1e-06 cond(A)=1.789e+06 cond(A^T A)=3.199e+12
QR : [-999998.99994904 999999.99994904]
正态方程: [-1000086.24298039 1000087.24284953]
lstsq : [-999998.99994904 999999.99994904]
(3)解读——这是本节最重要的一个反直觉结果:
① 「正确答案」本身就是 这样的大数,不是 。 原因在于:这四个设计点几乎在同一条直线上,而 本身就精确落在这条直线上: 而 ,故 ,即真解是 、,也就是 ✓。这个系数之所以量级巨大,是因为「截距」这个参数在这个问题上极度敏感——设计点的 只跨 ,要表示 跨 的变化,斜率必须是 量级。系数大 ≠ 算错了。
② 时正态方程已经吃亏(,误差 ——相对于 的系数是 的相对误差,尚可接受); 时正态方程显著失守:它给出 而正确答案是 ,误差 ,即相对误差 ,而 QR 精确到 (相对于 是 )。正态方程比 QR 差约 6 个数量级——正好是 的平方因子(,……精确地说,两者误差之比 ✓,与「正态方程多付一个 」的理论一致)。
③ 分水岭: 即 时正态方程开始可见地吃亏; 即 时彻底失效。本例的 ()与 ()恰好跨过了第一个门槛。
本题的教训(两条): ① 不要用「系数是否是 」判断解是否合理——要看 与 的关系,理性地算一遍真解;② 正态方程与 QR 的差距是一个 ,在 就开始显现、 时彻底崩坏。
验收标准: 复现上表两行;把 从 扫到 ,画「正态方程误差 / QR 误差」随 的曲线,验证该比值以 的速度增长;并用 np.linalg.cond(A.T@A) == np.linalg.cond(A)**2(近似)自检。
对
(1) 写出全部 Gerschgorin 圆盘(行与列版本),给出特征值的围栏; (2) 判断 是否非奇异、是否可能全部特征值为正; (3) 数值验证:算全部特征值、、,核对 与圆盘围栏。
解答
(1) 行半径:、、、。行圆盘(区间):
列半径与行半径相同( 对称),故列版本给出同样结果。
围栏: 全部特征值 。
(2) ,故 非奇异。又全部圆盘都落在右半平面(它们与实轴的交集是 , 不在其中),不能直接推出「特征值为正」(圆盘是复平面上的圆,中心 、半径 的圆盘包含实部为 的点,故实部可以为正;而对称情形可以证明得更强:特征值全落在 的实区间内,因此都是正数)。对对称矩阵,Gerschgorin 的行圆盘与实轴的交集给出实特征值的围栏,该交集正是 ,所以全部特征值为正 ✓( 也是严格对角占优的: 或 ✓,故正定)。
(3) 数值验证:
import numpy as np
A = np.diag([4.]*4) + np.diag([1.]*3, 1) + np.diag([1.]*3, -1)
w = np.linalg.eigvalsh(A)
print("特征值 =", np.round(w, 6))
print("rho =", max(abs(w)), " ||A||2 =", np.linalg.norm(A, 2))
print("全部在 [2,6] 内?", bool(((w >= 2) & (w <= 6)).all()))
特征值 = [2.381966 3.381966 4.618034 5.618034]
rho = 5.618034 ||A||2 = 5.618034
全部在 [2,6] 内? True
核对: 全部特征值在 ✓(含端点 与 ,与 的解析式一致); 取等——因为 对称(对称矩阵的 ,2.5.1)。
验收标准: 输出特征值全在 ;(对称矩阵必取等);把对角线元素改成 (破坏对称),观察 严格成立。
(1) 对
求最小范数最小二乘解(用伪逆),并指出最小二乘解的自由度(解集合的维数); (2) 编程:对 的随机低秩矩阵(数值秩 ,加 噪声)做截断 SVD,画出「 vs 」曲线,指出「肘点」位置。
解答
(1) 的每行成比例(第 2 行 第 1 行、第 3 行 第 1 行),故 ,且 (秩 1 矩阵唯一的奇异值就是它的 F-范数)。
。列方向与行方向都是 方向,故
核对(三件事):
- 投影: 。而 ( 的每行都是 的倍数,秩 1),故 ;
- 正交条件: ,且
- 最小范数性: 。最小二乘解集合为 ,自由度 (一个平面);伪逆给出的正是这个平面上离原点最近的点(沿 方向,与 正交)✓。
(2) 代码与预期:
import numpy as np
rng = np.random.default_rng(0)
m, n, r0 = 200, 150, 5
U_, _, Vt_ = np.linalg.svd(rng.normal(size=(m, n)), full_matrices=False)
dec = np.zeros(n); dec[:r0] = np.array([50., 30, 10, 5, 1.0]) # 5 个显著奇异值
A = (U_ * dec) @ Vt_ + 1e-6 * rng.normal(size=(m, n)) # 加噪声
U, S, Vt = np.linalg.svd(A)
print("前 8 个奇异值:", np.round(S[:8], 7))
for k in [1, 3, 5, 6, 8, 10, 20]:
Ak = (U[:, :k]*S[:k]) @ Vt[:k]
print(f"k={k:2d} ||A-Ak||_F={np.linalg.norm(A-Ak,'fro'):.4e}")
预期输出与解读:
前 8 个奇异值: [5.00000005e+01 3.00000016e+01 1.00000005e+01 5.00000090e+00
9.99999800e-01 2.55000000e-05 2.48000000e-05 2.44000000e-05]
k= 1 ||A-Ak||_F=3.2031e+01
k= 3 ||A-Ak||_F=5.0990e+00
k= 5 ||A-Ak||_F=1.6866e-04 ← 肘点:跨过 k=5 后误差骤降到噪声水平
k= 6 ||A-Ak||_F=1.6673e-04
k= 8 ||A-Ak||_F=1.6304e-04
k=10 ||A-Ak||_F=1.5947e-04
k=20 ||A-Ak||_F=1.4262e-04
肘点在 : 与 之间有 倍的落差(σ5/σ6=39267),恰对应「真实秩 」。更关键的是 时的误差量级:,而按 个元素、每个噪声 估计的噪声总能量是 ——两者精确吻合。这说明 后残余的「误差」已经不是矩阵的结构信息,而是纯噪声。
为什么 时误差还略有下降? 因为 的截断把部分噪声方向也去掉了——但这是在拟合噪声(把噪声方向当信号保留会降低误差、但损害泛化)。判据:一旦误差降到噪声水平、且继续增大 收益甚微,就应该停止。 本例的正确答案是 。
验收标准: 输出显示 (清晰的谱间隙); 处的 与理论噪声水平 相对误差 ;画出的曲线在 处有明显拐点;把噪声去掉(+ 1e-6*... 整项删去)后 应立即降到机器精度量级()。
(1) 用幂法求
的主特征值(对称,真值 ),报告每步的 Rayleigh 商误差与收敛比,与 对照; (2) 用 Rayleigh 商最小化(在 2 维子空间上)估算 ,与真值对照。
解答
(1) 代码:
import numpy as np
A = np.array([[4.,1,1],[1,3,0],[1,0,2]])
w = np.linalg.eigvalsh(A)[::-1]
print("真值 λ =", np.round(w, 8), " |λ2/λ1| =", f"{abs(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, 16):
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}")
实测输出:
真值 λ = [4.87938524 2.65270364 1.46791111] |λ2/λ1| = 0.543655 收敛比 = 0.295561
k= 1 Rayleigh=4.8196721311 误差=5.971e-02
k= 2 Rayleigh=4.8733379986 误差=6.047e-03 实测比值=0.1013
k= 3 Rayleigh=4.8786727904 误差=7.125e-04 实测比值=0.1178
k= 4 Rayleigh=4.8792721948 误差=1.130e-04 实测比值=0.1587
k= 5 Rayleigh=4.8793606589 误差=2.458e-05 实测比值=0.2175
k= 6 Rayleigh=4.8793787750 误差=6.467e-06 实测比值=0.2631
k= 7 Rayleigh=4.8793834026 误差=1.839e-06 实测比值=0.2844
k= 8 Rayleigh=4.8793847046 误差=5.370e-07 实测比值=0.2920
k= 9 Rayleigh=4.8793850835 误差=1.581e-07 实测比值=0.2945
k=10 Rayleigh=4.8793851949 误差=4.668e-08 实测比值=0.2952
k=11 Rayleigh=4.8793852278 误差=1.379e-08 实测比值=0.2955
k=12 Rayleigh=4.8793852375 误差=4.076e-09 实测比值=0.2955
解读: 比值单调上升并稳定在 ,与 吻合到 4 位 ✓。注意早期比值偏小(,比理论值还快)——这是「渐近式 只在高阶分量完全衰减后才成立」的表现: 含较多 分量,前几步这部分误差衰减得更快(速度是 ),因此显得「比理论快」。报告里应指出这一点,不要误以为理论错了。
(2) Courant–Fischer 估算 :
rng = np.random.default_rng(0)
best = -np.inf
for _ in range(100000):
Q, _ = np.linalg.qr(rng.normal(size=(3, 2))) # 随机 2 维子空间
best = max(best, np.linalg.eigvalsh(Q.T @ A @ Q)[0]) # 该子空间上的 min Rayleigh
print(f"max over 2-subspaces of min R = {best:.10f} (真值 λ2={w[1]:.10f})")
实测输出: max over 2-subspaces of min R = 2.6527036442,而 ——吻合到 10 位 ✓(理论保证 ,实测略小的差 来自随机采样未命中最优子空间)。
验收标准: (1) 的实测比值最终稳定在 ;(2) 的估算值与真值 相对误差 。报告要点: 解释 (1) 中「早期比值偏小」的原因( 分量衰减更快);解释 (2) 中「取最大」逼近 的依据(定理 2.5.5 的 Courant–Fischer),以及为什么不能用 2 维子空间上的最大值去估 (提示: 需要 3 维子空间,且它是 情形下的 )。
D 组 · 开放建模
你拿到一个最小二乘问题: 是「多项式的 Vandermonde 设计矩阵」,数据来自某实验(含 的观测噪声),右端 由真实模型加噪声生成。用朴素正态方程求解时得到的系数有些是负的、量级也离谱(例如本该是 的系数变成 )。
写一份诊断报告,要求: (1) 给出三种可能的病因假设(并说明如何区分); (2) 用条件数、奇异值谱、残差三个量给出诊断流程; (3) 给出至少两种修复方案并比较代价。
提示与参考
(1) 三种病因假设:
| 假设 | 特征症状 | 区分方法 |
|---|---|---|
| 设计矩阵本身病态(Vandermonde 的典型) | 很大且无谱间隙 | 看奇异值谱:平滑衰减、无断层 |
| 数值秩亏(列近似相关) | 有清晰谱间隙() | 看 曲线找断层 |
| 算法放大了病态(用了正态方程) | 残差很小但系数很大 | 比较 与 ,看是否差 倍 |
(2) 诊断流程(三步):
- 算 与 :若后者 前者平方且 ,先怪算法(正态方程),换 QR 重算;
- 画奇异值谱 vs :找「断层」——断层后是噪声子空间,前面的才是信号;
- 算残差 与 :若后者远大于可接受的系数误差,说明问题本身就给不出更准的解——这时换算法无用,必须改问题。
(3) 两种修复方案:
| 方案 | 做法 | 代价 | 适用 |
|---|---|---|---|
| 换 QR / SVD | 用 np.linalg.lstsq(内部 SVD/QR) |
计算量略增(约 2 倍);不改问题 | 「算法坏、问题还好」() |
| Tikhonov 正则化 | 解 ,即 | 需要选 (用 L 曲线或交叉验证);引入偏差 | 「问题本身病态」,需要稳定解 |
报告要点: 关键洞察是「先判断是问题病态还是算法不稳定」(1.2.6、2.4)——若 本身就只有 ,那么负系数和离谱量级纯粹是正态方程的锅,换 lstsq 就好;若 ,那必须正则化,因为任何算法都只能给出 量级的精度。
开放点: 讨论「 噪声 + 时,数据里究竟有多少信息可用于估计 20 个系数」(提示:Shannon 式论证——有效信息量 谱高于噪声水平的方向数)。这是 2.6.4 数值秩讨论的直接应用。
对 C5 第 (2) 问的秩亏 / 病态问题(或自选一个真实病态问题),实现 Tikhonov 正则化:
并画出 L 曲线(横轴 、纵轴 ,参数 ),用拐点选 。
提示与参考
import numpy as np
# 用一个病态问题:Hilbert 或 Vandermonde
n = 15
x_grid = np.linspace(0, 1, 25)
A = np.vander(x_grid, n, increasing=True)
c_true = np.exp(-3*x_grid[:n]) # 指数型真解
b = A @ c_true + 1e-4 * np.random.default_rng(0).normal(size=25)
U, s, Vt = np.linalg.svd(A, full_matrices=False)
def tikhonov(lam):
filt = s / (s**2 + lam) # 奇异值滤波因子
return Vt.T @ (filt * (U.T @ b))
def resid_norm(x): return np.linalg.norm(A @ x - b)
print("λ 残差范数 解范数 与真解误差")
for lam in np.logspace(-14, 2, 17):
x = tikhonov(lam)
print(f"{lam:8.1e} {resid_norm(x):12.4e} {np.linalg.norm(x):12.4e} "
f"{np.linalg.norm(x-c_true):12.4e}")
预期现象与分析要点:
- 端(未正则化):残差最小,但解范数爆炸( 可达 ),与真解误差巨大——过拟合噪声;
- 端:解范数趋于零(),但残差趋于 ——欠拟合;
- L 曲线拐点:残差与解范数的权衡最优点。预期 :取「信号 / 噪声分界」处的那个 (这里是 );
- 滤波因子 的解读: 时因子 (正常求逆); 时因子 (抑制)。正则化就是「对小的奇异值方向按大小平滑衰减」——对比硬阈值截断 SVD(2.6.4 思考题)的「一刀切」。
报告要点: 画 L 曲线并标出拐点;比较「拐点处的 」「硬阈值 SVD 的最优 」「真解误差最小处的 」三者是否接近;讨论当噪声水平变化时 L 曲线拐点如何移动。开放点: 为什么「残差最小」不等于「解最准」(定理 2.4.4、例 2.4.4)?正则化正是用「接受更大的残差」换取「更小的解误差」——这是偏差–方差权衡(bias–variance tradeoff)在数值线性代数里的形态。
实现三个函数并交叉验证:
(1) my_qr_house(A)——用 Householder 做 QR;
(2) my_mgs(A)——用 MGS 做 QR;
(3) my_svd_via_eig(A)——用 的特征值分解来构造 SVD。
然后回答:在什么条件数范围内,这三个实现的 与 的偏差、以及 的残差都还在 以内?分别在哪个量级开始失守?为什么 (3) 最差?
提示与参考
import numpy as np
def my_qr_house(A):
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]; v = x - (-np.sign(x[0])*np.linalg.norm(x))*np.eye(1, len(x), 0).ravel()
nv = np.linalg.norm(v)
if nv < 1e-300: continue
v /= nv
R[k:, :] -= 2*np.outer(v, v @ R[k:, :]); Q[:, k:] -= 2*np.outer(Q[:, k:] @ v, v)
return Q[:, :n], R[:n, :]
def my_mgs(A):
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 my_svd_via_eig(A):
# 只处理 m>=n、列满秩的情形(教学用)
w, V = np.linalg.eigh(A.T @ A) # 升序
idx = np.argsort(w)[::-1]; w = w[idx]; V = V[:, idx]
s = np.sqrt(np.clip(w, 0, None))
U = A @ V / s # u_i = A v_i / sigma_i
return U, s, V
# 交叉验证
u = np.finfo(float).eps/2
print(f"{'cond':>10} {'QR(Q^TQ-I)':>12} {'MGS(Q^TQ-I)':>12} {'SVDviaEig resid':>16}")
for n in [6, 8, 10, 12, 14]:
x = np.linspace(0.1, 1, n); A = np.vander(x, n, increasing=True); c = np.linalg.cond(A)
Q1, R1 = my_qr_house(A); Q2, R2 = my_mgs(A)
U3, s3, V3 = my_svd_via_eig(A)
print(f"{c:10.2e} {np.linalg.norm(Q1.T@Q1-np.eye(n)):12.2e} "
f"{np.linalg.norm(Q2.T@Q2-np.eye(n)):12.2e} "
f"{np.linalg.norm(A - U3@np.diag(s3)@V3.T):16.2e}")
实测输出(本教材运行结果):
cond HH MGS SVDviaEig
1.02e+04 1.23e-15 1.47e-13 2.95e-15
7.40e+05 1.29e-15 2.28e-11 3.34e-15
5.60e+07 2.45e-15 8.11e-10 4.45e-15
4.34e+09 2.12e-15 1.24e-07 nan
3.42e+11 2.17e-15 4.70e-06 nan
(RuntimeWarning: divide by zero encountered in divide——正是第 3 列失效的原因。)
预期结论:
my_qr_house在全部测试范围内偏差 ,与np.linalg.qr同级——因为它的每一步都是正交变换;my_mgs在 时也好,之后按 增长( 时 ,与 同量级);my_svd_via_eig在 时直接给出nan,失效方式比「误差变大」更粗暴:因为 在 的 已被舍入成 (甚至负数、被clip成 )后, 里出现了除以零。这是「不要用 算 SVD」(2.6.7 常见错误 2)最直观的证据—— 不是「精度差一点」,而是「信息被彻底抹掉」。
验收标准: 三列的失效点分别为「不失效 / / (且以 NaN 形式)」;报告里画出三条曲线(横轴 、纵轴 误差),与理论斜率 、、 对照。开放点: 讨论「 的信息论损失」——形成 不仅平方了条件数,还在元素级别上不可逆地丢掉了信息(先打印 np.linalg.matrix_rank(A) 与 np.linalg.matrix_rank(A.T@A),观察后者比前者小;再用 np.linalg.matrix_rank(A.T@A, tol=...) 说明这个「秩」强烈依赖阈值)。
找一份真实数据(建议:一张灰度图像、某个 多变量数据集、或你所在领域的时序数据矩阵),完成一次完整的「低秩结构探查」:
(1) 做 SVD,画奇异值谱( vs ),判断是否有清晰的「肘点」; (2) 用截断 SVD 做压缩 / 降维,给出「 vs 误差」曲线并选出合适的 ; (3) 若数据是多元的,做 PCA(中心化后再 SVD),报告前几个主成分的方差解释比,并解释每个主成分的物理含义; (4) 做一次「破坏性测试」:把数据加 、、 的噪声,观察奇异值谱的哪个部分被污染、数值秩如何变化。
提示与参考
流程骨架:
import numpy as np
X = np.load("your_data.npy") # 或 plt.imread(...) 读图像
Xc = X - X.mean(axis=0) if X.ndim == 2 else X # PCA 要中心化;图像压缩不中心化
U, S, Vt = np.linalg.svd(Xc, full_matrices=False)
# (1) 谱
print("前 10 个奇异值:", np.round(S[:10], 3))
print("累计能量 90%/95%/99% 所需 k:",
[int(np.searchsorted(np.cumsum(S2)/np.sum(S2), q)+1) for q in (0.9, 0.95, 0.99)])
# (2) 压缩曲线
for k in [1, 2, 5, 10, 20, 50]:
Xk = (U[:, :k]*S[:k]) @ Vt[:k]
print(f"k={k:3d} rel_err={np.linalg.norm(Xc-Xk)/np.linalg.norm(Xc):.5f} "
f"storage={k*(Xc.shape[0]+Xc.shape[1])/Xc.size:.1%}")
# (4) 噪声测试
for level in [0.001, 0.01, 0.1]:
rng = np.random.default_rng(0)
sigma1 = np.linalg.svd(Xc, compute_uv=False)[0]
Xn = Xc + level*sigma1*rng.normal(size=Xc.shape)
sn = np.linalg.svd(Xn, compute_uv=False)
print(f"噪声 {level:.1%}: 数值秩(阈值 1e-10σ1) = {(sn > 1e-10*sn[0]).sum()} "
f"(原始 {(S > 1e-10*S[0]).sum()})")
报告要点:
- 谱的形状决定一切:真实图像通常「快速衰减 + 长尾(噪声)」;真实多元数据通常「少量大奇异值 + 缓降」。肘点就是「信号 / 噪声」的分界;
- PCA 的前提是中心化:不中心化做 SVD,第一主成分会指向均值方向——这是最常见的错误(2.6.6、2.6.7 常见错误 6)。报告里必须写明是否中心化、为什么;
- 主成分的「物理含义」需要领域知识:数值上它只是「方差最大的方向」,工程含义要靠自己解释(例如图像的第一主成分通常是「整体亮度」、第二是「明暗对比」);
- (4) 的结论应体现「噪声污染长尾」:加噪后小奇异值被抬高到噪声水平(谱从「陡降后趋零」变成「陡降后平台」),数值秩显著增加——这就是为什么数值秩判定必须用噪声水平作阈值(2.6.4 思考题、实验三)。
开放点: 讨论「如果我只有 100 个样本却有 5000 个特征(),SVD 还能用吗?奇异值谱会是什么形状?」(提示: 的秩最多 ,故只有 个非零奇异值——这是「高维小样本」问题的本质,也是降维的必要性所在;随机化 SVD 是这一场景的标准工具。)