18.06 第 34 讲:期末复习
- 118.06 第 1 讲:方程组的行图与列图
- 218.06 第 2 讲:用矩阵做消元
- 318.06 第 3 讲:矩阵乘法的四种看法与逆矩阵
- 418.06 第 4 讲:分解成 A = LU
- 518.06 第 5 讲:转置、置换与向量空间
- 618.06 第 6 讲:列空间与零空间
- 718.06 第 7 讲:解 Ax = 0,主变量与特解
- 818.06 第 8 讲:解 Ax = b,行最简形 R
- 918.06 第 9 讲:线性无关、基与维数
- 1018.06 第 10 讲:四个基本子空间
- 1118.06 第 11 讲:矩阵空间、秩 1 矩阵与小世界图
- 1218.06 第 12 讲:图、网络与关联矩阵
- 1318.06 第 13 讲:第一次测验复习
- 1418.06 第 14 讲:正交向量与正交子空间
- 1518.06 第 15 讲:投影到子空间
- 1618.06 第 16 讲:投影矩阵与最小二乘
- 1718.06 第 17 讲:正交矩阵与 Gram-Schmidt
- 1818.06 第 18 讲:行列式的性质
- 1918.06 第 19 讲:行列式公式与代数余子式
- 2018.06 第 20 讲:Cramer 法则、逆矩阵与体积
- 2118.06 第 21 讲:特征值与特征向量
- 2218.06 第 22 讲:对角化与矩阵的幂
- 2318.06 第 23 讲:微分方程与 exp(At)
- 2418.06 第 24 讲:Markov 矩阵与 Fourier 级数
- 2518.06 第 24b 讲:第二次测验复习
- 2618.06 第 25 讲:对称矩阵与正定性
- 2718.06 第 26 讲:复矩阵与快速 Fourier 变换
- 2818.06 第 27 讲:正定矩阵与极小值
- 2918.06 第 28 讲:相似矩阵与 Jordan 标准形
- 3018.06 第 29 讲:奇异值分解
- 3118.06 第 30 讲:线性变换及其矩阵
- 3218.06 第 31 讲:基变换与图像压缩
- 3318.06 第 32 讲:第三次测验复习
- 3418.06 第 33 讲:左逆、右逆与伪逆
- 3518.06 第 34 讲:期末复习本文
最后一讲没有新内容,Strang 的做法是翻出老试卷,把题一道一道念出来,让全班一起想。我把每道题都交给 NumPy 过了一遍:一个 3 × 2 矩阵同时满足“一个右端项无解、另一个恰好一个解”, 而 ;Markov 矩阵从 出发迭代 10 步,离稳态 只剩 。
视频:Lecture 34: Final course review(OCW 页面与字幕) 教材:《Introduction to Linear Algebra》第 6 版 全书第 1 到 8 章 前置:第 33 讲:左逆、右逆与伪逆 一句话:期末题考的几乎都是同一件事:看到 、、 就想到四个子空间,看到解的个数就想到零空间。
- 开头几句是考试安排,可以跳过。第一道题(3 × 矩阵,一个右端项无解、一个有唯一解)值得认真看,他一步步把 、、 的关系推出来。
- 求 Markov 矩阵稳态向量的那一段,他在黑板上猜 ,猜之前冷了一会儿场,可以倍速。“总人数不变所以 ”这一句要听。
- 2 × 2 矩阵的快问快答和最后的最小二乘,本篇都已经算过,倍速即可。
- 结尾他向全班道谢。这是整门课的最后几句话,我觉得值得原速看完。
一个矩阵,三个数
他的第一道题: 一个 矩阵 ,已知 无解, 恰好有一个解。、、 能说什么?
推理只有两步。有的右端项无解,说明不是每一行都有主元,所以 ,而 本来就是 3。有解时只有一个解,说明零空间里只有零向量,所以 ,列无关。合起来是 。
他要一个例子。我猜他心里想的是最省事的那个:只有一列 。他也允许加第二列,我取 ,这样 恰好是单位阵,后面的判断题好查:
接着是三道判断题:; 可逆; 正定。最后他还要求证明 对每个 都有解,而且有无穷多个解。下面一次全算:
import numpy as np
def main() -> None: np.set_printoptions(precision=4, suppress=True) # 3 x 2 的例子:列空间里有 (0, 1, 0) 和 (0, 0, 1),没有 (1, 0, 0) A = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]) e1 = np.array([1.0, 0.0, 0.0]) e2 = np.array([0.0, 1.0, 0.0])
m, n = A.shape r = np.linalg.matrix_rank(A) print("m, n, r =", m, n, r)
# Ax = (1, 0, 0) 没有解:最小二乘的残差不为 0 for name, rhs in [("(1, 0, 0)", e1), ("(0, 1, 0)", e2)]: x, *_ = np.linalg.lstsq(A, rhs, rcond=None) print( f"Ax = {name}:x = {x},残差 = {np.linalg.norm(A @ x - rhs):.4f}" )
# 三个判断题 AtA = A.T @ A AAt = A @ A.T print("AtA =") print(AtA) print("det(AtA) =", np.linalg.det(AtA), ",det(AAt) =", np.linalg.det(AAt)) print("AAt 的特征值:", np.linalg.eigvalsh(AAt)) print("AAt 的秩:", np.linalg.matrix_rank(AAt))
# A^T y = c 对每个 c 有解,而且解不唯一:左零空间的维数是 m - r c = np.array([2.0, -5.0]) y, *_ = np.linalg.lstsq(A.T, c, rcond=None) print("A^T y = c 的一个解 y =", y, ",残差 =", np.linalg.norm(A.T @ y - c)) print("左零空间的维数 m - r =", m - r) y_other = y + 7.0 * e1 print( "加上左零空间里的 7 * (1, 0, 0) 之后仍然是解:", np.allclose(A.T @ y_other, c), )
if __name__ == "__main__": main()m, n, r = 3 2 2Ax = (1, 0, 0):x = [0. 0.],残差 = 1.0000Ax = (0, 1, 0):x = [1. 0.],残差 = 0.0000AtA =[[1. 0.] [0. 1.]]det(AtA) = 1.0 ,det(AAt) = 0.0AAt 的特征值: [0. 1. 1.]AAt 的秩: 2A^T y = c 的一个解 y = [ 0. 2. -5.] ,残差 = 0.0左零空间的维数 m - r = 1加上左零空间里的 7 * (1, 0, 0) 之后仍然是解: True输出对着题目一条条读。
- ,和推理一致; 的最小二乘残差是 1.0000,没有精确解, 的残差是 0.0000。
- 是单位阵,可逆(列无关),为真。
- ,,所以第一条为假。陷阱在于 只对两个方阵成立: 是 3 × 2 时, 是 3 × 3、秩只有 2。
- 的特征值是 ,有 0,所以不是正定,只是半正定。他说的“第一行一定全是 0”在这个例子里就是特征值 0 的来源。
- 是 ,行无关(),所以 总有解;左零空间的维数是 ,所以解有无穷多个。代码里给解加上 ,仍然满足方程。
整道题没有一步用到消元,全靠 、、 和四个子空间的维数(这套账 、、、 在第 10 讲里算过)。
三个列向量能说明什么
第二道题: 有三列 。(a) 解 ;(b) 如果这个组合等于零,解唯一吗;(c) 如果三个向量标准正交, 与 的哪个组合最接近 。
(a) 是第一讲的“按列乘”,。(b) 组合为零,说明这个 在零空间里,解永远不唯一,因为总能把它加到任何解上。(c) 想象 是 、、 轴, 轴到 平面的最近点就是原点,答案是 0 份和 0 份。
我用固定种子的随机向量验证三问,(c) 用 np.linalg.qr 造一组标准正交的列:
import numpy as np
def main() -> None: np.set_printoptions(precision=4, suppress=True) rng = np.random.default_rng(1806)
# (a) A x = v1 - v2 + v3,按列乘 V = rng.standard_normal((4, 3)) x = np.array([1.0, -1.0, 1.0]) print( "(a) A @ x 等于 v1 - v2 + v3:", np.allclose(V @ x, V[:, 0] - V[:, 1] + V[:, 2]), )
# (b) 如果 v1 - v2 + v3 = 0,则 (1, -1, 1) 在零空间里,解不唯一 W = V.copy() W[:, 2] = W[:, 1] - W[:, 0] print("(b) W @ x =", W @ x) print(" W 的秩:", np.linalg.matrix_rank(W), "< 3,零空间不只有 0")
# (c) 如果 v1, v2, v3 标准正交,v3 在 v1, v2 平面上的最近点是 0 Q, _ = np.linalg.qr(rng.standard_normal((5, 3))) coef, *_ = np.linalg.lstsq(Q[:, :2], Q[:, 2], rcond=None) print( "(c) Q^T Q 与单位阵的距离:", f"{np.linalg.norm(Q.T @ Q - np.eye(3)):.1e}", ) print( " 最近组合的系数 =", np.round(coef, 6) + 0.0, ",误差 =", f"{np.linalg.norm(Q[:, :2] @ coef - Q[:, 2]):.4f}", )
if __name__ == "__main__": main()(a) A @ x 等于 v1 - v2 + v3: True(b) W @ x = [0. 0. 0. 0.] W 的秩: 2 < 3,零空间不只有 0(c) Q^T Q 与单位阵的距离: 3.3e-16 最近组合的系数 = [0. 0.] ,误差 = 1.0000(b) 里 是零向量,秩是 2,小于 3。(c) 里 离单位阵只差 ,最近组合的系数是两个 0,误差 1.0000,恰好是 自己的长度。标准正交的好处就在这里:投影时各个方向互不干扰,不用解正规方程。
Markov 矩阵与稳态
第三道题: 给一个 Markov 矩阵,问特征值,再问从 出发, 趋向哪里。
字幕是语音转写,矩阵的数字被听写得一团糟。我按他念的性质还原:每列和为 1、第一列加第二列等于两倍第三列、迹是 0.8、 是 的特征向量,得到
这个矩阵同时满足上面全部四条,我认为就是黑板上的那个,不过这是还原,不是抄录。他的推理顺序是:列有线性关系,所以奇异,有一个特征值 0;Markov 矩阵必有特征值 1;迹是 0.8,所以第三个是 。稳态只剩 , 是 的特征向量, 靠“总人数不变”:起点总共 10 人,所以 ,。
import numpy as np
def main() -> None: np.set_printoptions(precision=4, suppress=True) A = np.array([[0.2, 0.4, 0.3], [0.4, 0.2, 0.3], [0.4, 0.4, 0.4]]) u0 = np.array([0.0, 10.0, 0.0])
print("列和:", A.sum(axis=0)) print( "第一列加第二列等于两倍第三列:", np.allclose(A[:, 0] + A[:, 1], 2 * A[:, 2]), ) print("迹:", np.trace(A), ",det:", f"{np.linalg.det(A):.1e}") print("特征值:", np.sort(np.linalg.eigvals(A).real))
# 稳态:lambda = 1 的特征向量,按总人数 10 缩放 lam, S = np.linalg.eig(A) x = S[:, np.argmin(np.abs(lam - 1.0))].real steady = 10.0 * x / x.sum() print( "稳态 u_inf =", steady, ",A @ u_inf 不变:", np.allclose(A @ steady, steady), )
# 误差按 (-0.2)^k 衰减 u = u0.copy() for k in range(1, 11): u = A @ u if k in (1, 2, 5, 10): print( f"k = {k:2d}:u = {u},到稳态的距离 =", f"{np.linalg.norm(u - steady):.2e}", )
if __name__ == "__main__": main()列和: [1. 1. 1.]第一列加第二列等于两倍第三列: True迹: 0.8 ,det: -6.7e-18特征值: [-0.2 0. 1. ]稳态 u_inf = [3. 3. 4.] ,A @ u_inf 不变: Truek = 1:u = [4. 2. 4.],到稳态的距离 = 1.41e+00k = 2:u = [2.8 3.2 4. ],到稳态的距离 = 2.83e-01k = 5:u = [3.0016 2.9984 4. ],到稳态的距离 = 2.26e-03k = 10:u = [3. 3. 4.],到稳态的距离 = 7.24e-07特征值是 ,稳态正好是 ,每一步误差大约缩小到原来的 倍: 时离稳态 , 时 ,从第 1 步到第 10 步缩小了约 倍。行列式打印出 ,那是浮点误差,真值是 0。
2 × 2 快问快答
第四组: 他念性质,全班给 2 × 2 的例子,没有就说没有。
- 投影到过 的直线:,分母是个数,直接除。
- 特征值 0 和 3,特征向量 和 :。
- 不能写成 的实矩阵: 总是对称的,所以任意一个不对称矩阵都行。
- 特征向量正交但不对称:反对称矩阵或正交(旋转)矩阵,特征向量是复的,但在复内积下正交。
import numpy as np
def main() -> None: np.set_printoptions(precision=4, suppress=True)
# 1. 投影到过 a = (4, -3) 的直线 a = np.array([[4.0], [-3.0]]) P = a @ a.T / (a.T @ a) print("P =") print(P) print( "P @ P == P:", np.allclose(P @ P, P), ",P.T == P:", np.allclose(P.T, P), )
# 2. 特征值 0 与 3,特征向量 (1, 2) 与 (2, 1) S = np.array([[1.0, 2.0], [2.0, 1.0]]) Lam = np.diag([0.0, 3.0]) A = S @ Lam @ np.linalg.inv(S) print("A = S Lam S^-1 =") print(A) print("特征值:", np.sort(np.linalg.eigvals(A).real))
# 3. 不对称的矩阵不可能写成 B^T B N = np.array([[1.0, 2.0], [0.0, 1.0]]) print("N 对称吗:", np.allclose(N, N.T))
# 4. 反对称与正交矩阵:特征向量复正交,但矩阵不对称 theta = 0.7 K = np.array([[0.0, 1.0], [-1.0, 0.0]]) Q = np.array( [[np.cos(theta), np.sin(theta)], [-np.sin(theta), np.cos(theta)]] ) for name, M in [("反对称 K", K), ("旋转 Q", Q)]: lam, V = np.linalg.eig(M) inner = np.vdot(V[:, 0], V[:, 1]) print( name, ":特征值", lam, ",复内积 =", f"{abs(inner):.1e}", ",对称:", np.allclose(M, M.T), )
if __name__ == "__main__": main()P =[[ 0.64 -0.48] [-0.48 0.36]]P @ P == P: True ,P.T == P: TrueA = S Lam S^-1 =[[ 4. -2.] [ 2. -1.]]特征值: [0. 3.]N 对称吗: False反对称 K :特征值 [0.+1.j 0.-1.j] ,复内积 = 0.0e+00 ,对称: False旋转 Q :特征值 [0.7648+0.6442j 0.7648-0.6442j] ,复内积 = 0.0e+00 ,对称: False 满足 与 。 给出 ,特征值回到 0 和 3。最后一条里,反对称矩阵的特征值是 ,旋转 0.7 弧度的矩阵是 ,两者的特征向量复内积都是 0,矩阵却都不对称。复内积我用 np.vdot 算,它会自动取共轭,这是第 26 讲的内容。
最小二乘:给答案,找问题
最后一道: 他说像 Jeopardy 节目,先给答案再问问题。系统是
最小二乘解是 。问题有三个: 在列空间上的投影 是什么;对应的直线拟合问题画出来是什么样;再找一个不全为零的 ,让最小二乘解变成零。
答案依次是:,最小二乘找的就是“列的最近组合”;直线在 处拟合高度 ,截距 、斜率 ;最后, 要和两列都正交,比如 。
import numpy as np
def main() -> None: np.set_printoptions(precision=4, suppress=True) A = np.array([[1.0, 0.0], [1.0, 1.0], [1.0, 2.0]]) b = np.array([3.0, 4.0, 1.0])
# 正规方程与 lstsq x_hat = np.linalg.solve(A.T @ A, A.T @ b) x_lstsq, *_ = np.linalg.lstsq(A, b, rcond=None) print("x_hat =", x_hat, ",11/3 =", 11 / 3) print("两种解法一致:", np.allclose(x_hat, x_lstsq))
# 投影 p = A x_hat:11/3 倍列一,减一倍列二 p = A @ x_hat print("p =", p) print( "p = 11/3 * 列一 - 列二:", np.allclose(p, 11 / 3 * A[:, 0] - A[:, 1]) ) print("误差 b - p 与两列正交:", np.allclose(A.T @ (b - p), 0)) print( "||b|| =", f"{np.linalg.norm(b):.4f}", ",||p|| =", f"{np.linalg.norm(p):.4f}", ",||b - p|| =", f"{np.linalg.norm(b - p):.4f}", )
# 最小二乘解是 0 的 b:与两列都正交,比如 (1, -2, 1) b0 = np.array([1.0, -2.0, 1.0]) print("A^T b0 =", A.T @ b0) x0, *_ = np.linalg.lstsq(A, b0, rcond=None) print("b0 的最小二乘解 =", x0 + 0.0)
if __name__ == "__main__": main()x_hat = [ 3.6667 -1. ] ,11/3 = 3.6666666666666665两种解法一致: Truep = [3.6667 2.6667 1.6667]p = 11/3 * 列一 - 列二: True误差 b - p 与两列正交: True||b|| = 5.0990 ,||p|| = 4.8305 ,||b - p|| = 1.6330A^T b0 = [0. 0.]b0 的最小二乘解 = [0. 0.]。勾股定理在这里看得很清楚:,投影 ,误差 ,。,所以 的最小二乘解是零。
他念完这道题就收了,说一个小时只能做这么多,考试有三个小时。
如果你是从第 1 讲一路跟下来的,到这里 35 篇就结束了。接下来可以去看 Strang 的另一门课 18.065(Matrix Methods in Data Analysis, Signal Processing, and Machine Learning),它默认你已经学过 18.06,SVD、低秩近似和优化会用得更多。以前我看到 只会想到“解”,现在想到的是“列的组合”,这个变化,大概是这个系列最大的收获。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!











