视频加载失败

18.06 第 18 讲:行列式的性质

4676 字
23 分钟
18.06 第 18 讲:行列式的性质
  1. 118.06 第 1 讲:方程组的行图与列图
  2. 218.06 第 2 讲:用矩阵做消元
  3. 318.06 第 3 讲:矩阵乘法的四种看法与逆矩阵
  4. 418.06 第 4 讲:分解成 A = LU
  5. 518.06 第 5 讲:转置、置换与向量空间
  6. 618.06 第 6 讲:列空间与零空间
  7. 718.06 第 7 讲:解 Ax = 0,主变量与特解
  8. 818.06 第 8 讲:解 Ax = b,行最简形 R
  9. 918.06 第 9 讲:线性无关、基与维数
  10. 1018.06 第 10 讲:四个基本子空间
  11. 1118.06 第 11 讲:矩阵空间、秩 1 矩阵与小世界图
  12. 1218.06 第 12 讲:图、网络与关联矩阵
  13. 1318.06 第 13 讲:第一次测验复习
  14. 1418.06 第 14 讲:正交向量与正交子空间
  15. 1518.06 第 15 讲:投影到子空间
  16. 1618.06 第 16 讲:投影矩阵与最小二乘
  17. 1718.06 第 17 讲:正交矩阵与 Gram-Schmidt
  18. 1818.06 第 18 讲:行列式的性质本文
  19. 1918.06 第 19 讲:行列式公式与代数余子式
  20. 2018.06 第 20 讲:Cramer 法则、逆矩阵与体积
  21. 2118.06 第 21 讲:特征值与特征向量
  22. 2218.06 第 22 讲:对角化与矩阵的幂
  23. 2318.06 第 23 讲:微分方程与 exp(At)
  24. 2418.06 第 24 讲:Markov 矩阵与 Fourier 级数
  25. 2518.06 第 24b 讲:第二次测验复习
  26. 2618.06 第 25 讲:对称矩阵与正定性
  27. 2718.06 第 26 讲:复矩阵与快速 Fourier 变换
  28. 2818.06 第 27 讲:正定矩阵与极小值
  29. 2918.06 第 28 讲:相似矩阵与 Jordan 标准形
  30. 3018.06 第 29 讲:奇异值分解
  31. 3118.06 第 30 讲:线性变换及其矩阵
  32. 3218.06 第 31 讲:基变换与图像压缩
  33. 3318.06 第 32 讲:第三次测验复习
  34. 3418.06 第 33 讲:左逆、右逆与伪逆
  35. 3518.06 第 34 讲:期末复习

Strang 这一讲没有先给行列式的公式,而是只给三条性质:单位矩阵的行列式是 1,换行变号,对每一行单独线性。其余七条都从这三条推出来,包括最实用的一条:三角矩阵的行列式是对角线乘积,所以行列式等于主元乘积。我手写了一个只做消元的行列式函数,对一个 100 × 100 的随机矩阵,它算出 3.449180e+77,和 np.linalg.det 的相对差是 9.99e-13。三条性质,连同 det⁡A≠0\det A \ne 0 等价于 AA 可逆,就把行列式立住了。

本讲速览

视频:Lecture 18: Properties of determinants(OCW 页面与字幕) 教材:《Introduction to Linear Algebra》第 6 版 §5.1 3 by 3 Determinants and Cofactors;§5.2 Computing and Using Determinants 前置:第 17 讲:正交矩阵与 Gram-Schmidt 一句话:行列式由“det⁡I=1\det I = 1、换行变号、对每一行线性”这三条性质唯一确定,由此得到 det⁡A\det A 等于主元乘积(带上换行的符号),det⁡A≠0\det A \ne 0 当且仅当 AA 可逆。

配合视频
  • 开头他先说这一讲是后半门课的开始:行列式是为特征值服务的。这几句值得听,后面几讲为什么要学行列式,这里交代得最清楚。
  • 他推导性质 4(两行相等则行列式为 0)、性质 5(消元不改变行列式)、性质 7(三角矩阵是对角线乘积)的那几段要认真看。每一条都只用前面的性质,是“从三条公理往外长”的范例。本篇的代码只做验证,不替代推导。
  • 性质 9 以后(乘积、逆、转置)讲得快,本篇有对应的数值验证,可以倍速。最后他用 A=LUA = LU 证明 det⁡AT=det⁡A\det A^\mathsf{T} = \det A,没有讲完,留了一个“排列的奇偶性”的尾巴,下一讲接上。
  • 字幕是自动转写的,有几处断句错位(比如性质 5 和性质 9 的位置夹进了别的句子),读不通时以我这里列出的性质为准。

先给三条性质,不给公式#

他开场说得很直白:行列式是每个方阵对应的一个数,写作 det⁡A\det A 或 ∣A∣|A|。一个数装不下整个矩阵,但它想把尽可能多的信息塞进这一个数里,其中最重要的一条是:AA 可逆当且仅当 det⁡A≠0\det A \ne 0。

然后他问:先给一个大公式,一口气塞给你?他说不。公式里装了太多东西,他宁愿先给三条性质。

  • 性质 1:det⁡I=1\det I = 1。
  • 性质 2:交换两行,行列式变号。
  • 性质 3:行列式对每一行单独线性。3A:某一行乘 tt,行列式乘 tt。3B:某一行是两个向量的和,行列式等于两个行列式的和,其余各行不动。

这三条一给,置换矩阵的行列式就定了:单位矩阵是 1,每换一次行变一次号,所以结果是 +1+1 或 −1-1,取决于换行次数是偶数还是奇数。他顺手写了 2 × 2 的情形:单位矩阵是 1,换了行的是 −1-1。

“线性”到底是什么意思? 他特意强调了两遍,我也在这里停一下,因为这是最容易误会的一条。线性是对一行而言的:保持其余各行不动,只拆第一行。它不是说 det⁡(A+B)=det⁡A+det⁡B\det(A + B) = \det A + \det B,那个不成立。他甚至说“怎么才能让录像里留下我没说那句话”。

下面的脚本把性质 1 到 3 逐条验证一遍,矩阵 AA 是我随手选的一个 3 阶整数矩阵(挑它只因为行列式恰好是 10,好认)。脚本里还枚举了 3 阶的六个置换矩阵,对照“逆序数”的奇偶性:

import itertools
import numpy as np
def main() -> None:
np.set_printoptions(precision=4, suppress=True)
det = np.linalg.det
# 性质 1:det(I) = 1
print("性质 1:det(I) =", det(np.eye(4)))
# 性质 2:交换两行,符号反转;置换矩阵的行列式是 +1 或 -1
A = np.array([[1.0, 2.0, 1.0], [3.0, 8.0, 1.0], [0.0, 4.0, 1.0]])
swapped = A[[1, 0, 2]]
print(
"性质 2:det(A) =",
round(det(A), 4),
",换行后",
round(det(swapped), 4),
)
for perm in itertools.permutations(range(3)):
P = np.eye(3)[list(perm)]
# 数交换次数:逆序对的个数的奇偶性等于交换次数的奇偶性
inversions = sum(
perm[i] > perm[j] for i in range(3) for j in range(i + 1, 3)
)
print(f" {perm} 逆序数 {inversions} det(P) = {det(P):+.0f}")
# 性质 3A:一行乘 t,行列式乘 t
t = 5.0
scaled = A.copy()
scaled[0] *= t
print("性质 3A:", round(det(scaled), 4), "=", t, "*", round(det(A), 4))
# 性质 3B:只对第一行线性,其余行不动
u = np.array([1.0, 0.0, 2.0])
v = np.array([0.0, 3.0, -1.0])
rest = A[1:]
first_u = np.vstack([u, rest])
first_v = np.vstack([v, rest])
first_sum = np.vstack([u + v, rest])
print(
"性质 3B:",
round(det(first_sum), 4),
"=",
round(det(first_u), 4),
"+",
round(det(first_v), 4),
)
# 但 det(A + B) 不等于 det(A) + det(B)
B = np.array([[2.0, 0.0, 1.0], [1.0, 1.0, 0.0], [0.0, 3.0, 1.0]])
print("det(A + B) =", round(det(A + B), 4))
print("det(A) + det(B) =", round(det(A) + det(B), 4))
if __name__ == "__main__":
main()
性质 1:det(I) = 1.0
性质 2:det(A) = 10.0 ,换行后 -10.0
(0, 1, 2) 逆序数 0 det(P) = +1
(0, 2, 1) 逆序数 1 det(P) = -1
(1, 0, 2) 逆序数 1 det(P) = -1
(1, 2, 0) 逆序数 2 det(P) = +1
(2, 0, 1) 逆序数 2 det(P) = +1
(2, 1, 0) 逆序数 3 det(P) = -1
性质 3A: 50.0 = 5.0 * 10.0
性质 3B: 7.0 = 28.0 + -21.0
det(A + B) = 73.0
det(A) + det(B) = 15.0

性质 2 的两行 det⁡A=10\det A = 10、换行后 −10-10。六个置换矩阵的行列式里三个 +1+1、三个 −1-1,符号正好是逆序数的奇偶性(逆序数就是把 0,1,20, 1, 2 排成这个顺序需要的相邻交换次数的奇偶)。性质 3A:第一行乘 5,行列式从 10 变成 50。性质 3B:我把第一行拆成 u+vu + v,其余两行不动,7=28+(−21)7 = 28 + (-21)。

最后两行是反例:det⁡(A+B)=73\det(A + B) = 73,而 det⁡A+det⁡B=10+5=15\det A + \det B = 10 + 5 = 15。差了将近五倍,“线性”确实只是对单独一行成立。

其余的性质都是这三条推出来的#

他接下来的推导是这一讲最漂亮的部分:只用性质 1 到 3,往外长出性质 4 到 7。

性质 4:两行相等,行列式为 0。 交换这两行,矩阵没变,所以行列式没变;可是性质 2 说行列式要变号。一个数等于它的相反数,只能是 0。他说“性质 2 就是为这个准备的”。这和“两行相等则秩小于 nn,不可逆”吻合。

性质 5:从一行里减去另一行的倍数,行列式不变。 这是消元里一直在做的那一步,所以这一条最要紧:它说整个消元过程不改变行列式。证明用 2 × 2 就够:第二行变成 (c−la, d−lb)(c - la,\ d - lb),按 3B 拆成 (c,d)(c, d) 和 (−la,−lb)(-la, -lb) 两个行列式,后一个用 3A 提出 −l-l,剩下两行相等,按性质 4 是 0。所以消元前后的行列式一样,后面他反复用这一条说明 det⁡A=det⁡U\det A = \det U(没有换行的话)。

性质 6:有一行全是 0,行列式为 0。 他一开始想用 3A 乘 5,后来换成同学的办法:在 3A 里取 t=0t = 0。这对应“消元消出一行 0,说明矩阵奇异”。

性质 7:三角矩阵的行列式是对角线元素的乘积。 设对角线是 d1,d2,…,dnd_1, d_2, \dots, d_n,对角线上方是任意数。若所有 di≠0d_i \ne 0,用性质 5 往上消去所有非对角元,只剩对角矩阵;用 3A 把 d1,…,dnd_1, \dots, d_n 逐行提出来,剩下单位矩阵,轮到性质 1,等于 1。所以结果是 d1d2⋯dnd_1 d_2 \cdots d_n。若有某个 di=0d_i = 0,消元会得到一行零,性质 6 给出 0,公式依然成立。

下面验证性质 4 到 7,再看他最后检查的 2 × 2 情形:

import numpy as np
def main() -> None:
np.set_printoptions(precision=4, suppress=True)
det = np.linalg.det
A = np.array([[1.0, 2.0, 1.0], [3.0, 8.0, 1.0], [0.0, 4.0, 1.0]])
# 性质 4:两行相等,行列式为 0
equal_rows = A.copy()
equal_rows[2] = equal_rows[0]
print("性质 4:两行相等 det =", round(det(equal_rows), 4))
# 性质 5:从一行里减去另一行的倍数,行列式不变
step = A.copy()
step[1] -= 3.0 * step[0]
print("性质 5:消元前", round(det(A), 4), ",消元后", round(det(step), 4))
# 性质 6:有一行全是 0,行列式为 0
zero_row = A.copy()
zero_row[1] = 0.0
print("性质 6:零行 det =", round(det(zero_row), 4))
# 性质 7:三角矩阵的行列式是对角线上元素的乘积
U = np.triu(A)
print(
"性质 7:det(U) =",
round(det(U), 4),
",对角线乘积 =",
np.prod(np.diag(U)),
)
# 2 x 2:主元乘积就是 ad - bc
a, b, c, d = 3.0, 5.0, 2.0, 4.0
second_pivot = d - (c / a) * b
print("2 x 2:a * (d - c/a * b) =", round(a * second_pivot, 4))
print("2 x 2:ad - bc =", a * d - b * c)
if __name__ == "__main__":
main()
性质 4:两行相等 det = 0.0
性质 5:消元前 10.0 ,消元后 10.0
性质 6:零行 det = 0.0
性质 7:det(U) = 8.0 ,对角线乘积 = 8.0
2 x 2:a * (d - c/a * b) = 2.0
2 x 2:ad - bc = 2.0

性质 4、5、6 的数字都和预期一致:把第三行抄成第一行,行列式是 0;第二行减去 3 倍第一行,行列式仍是 10;零行,行列式是 0。性质 7 里我取的 UU 是 AA 的上三角部分(np.triu),不是消元的结果,只想看 det⁡U=1×8×1=8\det U = 1 \times 8 \times 1 = 8。

最后两行是他的 2 × 2 检查:用主元乘积算行列式。第一个主元是 aa,第二个主元是 d−(c/a) bd - (c/a)\,b,两者相乘得 a⋅(d−cb/a)=ad−bca \cdot (d - cb/a) = ad - bc。我取 a,b,c,d=3,5,2,4a, b, c, d = 3, 5, 2, 4,第二个主元是 4−103=234 - \tfrac{10}{3} = \tfrac23,乘出来是 2,和 ad−bc=12−10=2ad - bc = 12 - 10 = 2 一致。他自己打趣说,所有数学教授都在等他承认:a=0a = 0 时这一步不被允许,要先换行。如果换不成,是因为矩阵奇异。

行列式就是主元乘积#

性质 7 加上性质 5,就得到他说的“MATLAB 以及任何像样的软件”是怎么算行列式的:

det⁡A=± d1d2⋯dn\det A = \pm\, d_1 d_2 \cdots d_n,did_i 是消元得到的主元,符号由换行的次数决定。

没有换行时 det⁡A=det⁡U\det A = \det U,就是主元乘积(对照第 4 讲的 A=LUA = LU:det⁡L=1\det L = 1,所以 det⁡A=det⁡U\det A = \det U)。有换行时,每换一次乘一次 −1-1。他的说法是“要特别留意加号还是减号”。

我手写了这个算法:消元到上三角,只在主元为 0 时才换行并记录符号,最后把主元乘起来。三个 3 阶矩阵(不换行、需要换行、奇异)加一个 100 × 100 随机矩阵,都和 np.linalg.det 对照:

import numpy as np
def det_by_elimination(A: np.ndarray) -> float:
"""消元到上三角,返回(主元乘积 * 换行的符号)。"""
U = A.astype(float).copy()
n = U.shape[0]
sign = 1.0
for k in range(n):
# 主元为 0 时才换行:性质 2 每换一次行,符号反转
if U[k, k] == 0.0:
rows = np.nonzero(U[k + 1 :, k])[0]
if len(rows) == 0:
return 0.0 # 该列下方全是 0,奇异,性质 6
swap = k + 1 + rows[0]
U[[k, swap]] = U[[swap, k]]
sign = -sign
for i in range(k + 1, n):
U[i] -= (U[i, k] / U[k, k]) * U[k] # 性质 5,行列式不变
return sign * float(np.prod(np.diag(U)))
def main() -> None:
np.set_printoptions(precision=4, suppress=True)
cases = {
"无需换行": np.array(
[[1.0, 2.0, 1.0], [3.0, 8.0, 1.0], [0.0, 4.0, 1.0]]
),
"需要换行": np.array(
[[0.0, 2.0, 1.0], [3.0, 8.0, 1.0], [0.0, 4.0, 1.0]]
),
"奇异": np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]),
}
for name, M in cases.items():
mine = det_by_elimination(M)
print(f"{name}:消元 {mine:.4f},np.linalg.det {np.linalg.det(M):.4f}")
# 100 x 100 的随机矩阵
rng = np.random.default_rng(1806)
M = rng.standard_normal((100, 100))
mine = det_by_elimination(M)
ref = np.linalg.det(M)
print(f"100 x 100:消元 {mine:.6e},np.linalg.det {ref:.6e}")
print("相对差:", f"{abs(mine - ref) / abs(ref):.2e}")
if __name__ == "__main__":
main()
无需换行:消元 10.0000,np.linalg.det 10.0000
需要换行:消元 6.0000,np.linalg.det 6.0000
奇异:消元 0.0000,np.linalg.det 0.0000
100 x 100:消元 3.449180e+77,np.linalg.det 3.449180e+77
相对差: 9.99e-13

三个 3 阶矩阵里,第一个的主元是 1,2,51, 2, 5,乘积是 10。第二个矩阵第一列的第一个元素是 0,要换行,换一次行之后三个主元是 3,2,−13, 2, -1,乘积 −6-6,再乘换行的符号 −1-1 得 6。第三个是 1,2,…,91, 2, \dots, 9 排成的奇异矩阵,消元之后最后一个主元是 0,行列式是 0.0000,np.linalg.det 也一样。三者都和库函数吻合。

100 × 100 那一行我最在意:我的函数没有选主元,只在 Ukk=0U_{kk} = 0 时才换行,数值上不算好的实现,但结果仍是 3.449180e+77,和 np.linalg.det 的相对差只有 9.99e-13(这个量级下 10−1310^{-13} 到 10−1210^{-12} 的差别,就是舍入误差的底噪)。顺便看一眼:一个 100 阶随机矩阵的行列式有 77 位数,教材里的“大公式”要加的项有 100!≈10158100! \approx 10^{158} 个,没人会这样算。下一讲会回到那个公式。

乘积、逆、转置#

性质 7 之后,他把剩下的两条一口气讲完。

性质 9:det⁡(AB)=det⁡A⋅det⁡B\det(AB) = \det A \cdot \det B。 他说这条有点“从天上掉下来”:之前一直没讲过矩阵相乘,行列式突然有了乘法性质。记住行列式没有加法性质(上面 73≠1573 \ne 15),但有乘法性质。

由它立刻得到三个推论,他在黑板上一个一个写:

  • det⁡A−1=1/det⁡A\det A^{-1} = 1/\det A:把 A−1A=IA^{-1}A = I 两边取行列式,det⁡A−1⋅det⁡A=det⁡I=1\det A^{-1} \cdot \det A = \det I = 1。他用 A=diag(2,3)A = \mathrm{diag}(2, 3) 检查:det⁡A=6\det A = 6,A−1=diag(12,13)A^{-1} = \mathrm{diag}(\tfrac12, \tfrac13),行列式 16\tfrac16。det⁡A=0\det A = 0 的时候 A−1A^{-1} 不存在,1/01/0 也没有意义,两边“同时荒谬”。
  • det⁡A2=(det⁡A)2\det A^2 = (\det A)^2。
  • det⁡(2A)=2ndet⁡A\det(2A) = 2^n \det A,不是 2det⁡A2 \det A。他的理由:每一行都提出一个 2,一共 nn 行,所以是 2n2^n。他把它和体积联系起来:一个盒子每条边都乘 2,体积乘 2n2^n,三维是 8。(这也是他在这一讲唯一提到行列式和体积关系的地方,第 20 讲会专门讲。)

性质 10:det⁡AT=det⁡A\det A^\mathsf{T} = \det A。 2 × 2 的 ad−bcad - bc 一眼就看出来。重要的是推论:之前所有关于“行”的性质,对“列”也成立。零列的行列式是 0,换两列变号。理由很简单:转置,把列变成行,用行的性质,再转置回去。

他的证明用到 A=LUA = LU:det⁡A=det⁡L⋅det⁡U\det A = \det L \cdot \det U,det⁡AT=det⁡UT⋅det⁡LT\det A^\mathsf{T} = \det U^\mathsf{T} \cdot \det L^\mathsf{T}。LL 是对角线全为 1 的下三角,性质 7 说它的行列式是 1,LTL^\mathsf{T} 也是;UU 和 UTU^\mathsf{T} 的对角线相同,行列式都是主元乘积。所以两边相等。他说这只证明了“几乎所有”能 A=LUA = LU 的矩阵,有换行的情形留给读者。

验证一下,用固定种子的 5 × 5 随机矩阵:

import numpy as np
def main() -> None:
np.set_printoptions(precision=4, suppress=True)
det = np.linalg.det
rng = np.random.default_rng(1806)
A = rng.standard_normal((5, 5))
B = rng.standard_normal((5, 5))
n = A.shape[0]
# 性质 9:det(AB) = det(A) det(B)
print("det(AB) =", f"{det(A @ B):.6f}")
print("det(A) det(B) =", f"{det(A) * det(B):.6f}")
# 推论:逆、平方、数乘
print("det(A) =", f"{det(A):.6f}", ",1/det(A) =", f"{1 / det(A):.6f}")
print("det(inv(A)) =", f"{det(np.linalg.inv(A)):.6f}")
print(
"det(A @ A) =",
f"{det(A @ A):.6f}",
",det(A)^2 =",
f"{det(A) ** 2:.6f}",
)
print("det(2A) / det(A) =", f"{det(2 * A) / det(A):.4f}", ",2^n =", 2**n)
# Strang 的对角例子:A = diag(2, 3)
D = np.diag([2.0, 3.0])
print(
"diag(2, 3):",
round(det(D), 4),
",逆:",
round(det(np.linalg.inv(D)), 4),
)
# 性质 10:det(A^T) = det(A),所以列也有同样的性质
print("性质 10:", np.isclose(det(A.T), det(A)))
swapped_cols = A[:, [1, 0, 2, 3, 4]]
print("换两列:", np.isclose(det(swapped_cols), -det(A)))
zero_col = A.copy()
zero_col[:, 2] = 0.0
print("零列 det =", round(det(zero_col), 4))
# 性质 10 的证明思路:A = LU,det(L) = 1,U 与 U^T 对角线相同
L = np.tril(rng.standard_normal((n, n)), -1) + np.eye(n)
U = np.triu(rng.standard_normal((n, n)))
M = L @ U
print("det(L) =", det(L), ",det(L^T) =", det(L.T))
print("det(M) =", f"{det(M):.6f}", ",det(U) =", f"{det(U):.6f}")
print("det(M^T) =", f"{det(M.T):.6f}", ",det(U^T) =", f"{det(U.T):.6f}")
if __name__ == "__main__":
main()
det(AB) = 0.334636
det(A) det(B) = 0.334636
det(A) = 0.157837 ,1/det(A) = 6.335633
det(inv(A)) = 6.335633
det(A @ A) = 0.024913 ,det(A)^2 = 0.024913
det(2A) / det(A) = 32.0000 ,2^n = 32
diag(2, 3): 6.0 ,逆: 0.1667
性质 10: True
换两列: True
零列 det = 0.0
det(L) = 1.0 ,det(L^T) = 1.0
det(M) = 0.007649 ,det(U) = 0.007649
det(M^T) = 0.007649 ,det(U^T) = 0.007649

逐条读:

  • det⁡(AB)\det(AB) 和 det⁡A⋅det⁡B\det A \cdot \det B 都是 0.334636。
  • det⁡A=0.157837\det A = 0.157837,倒数是 6.335633,和 np.linalg.inv(A) 的行列式一致。det⁡A2=0.024913=0.1578372\det A^2 = 0.024913 = 0.157837^2。
  • det⁡(2A)/det⁡A=32.0000=25\det(2A) / \det A = 32.0000 = 2^5,n=5n = 5。数乘一个 5 阶矩阵,行列式涨到 32 倍,而不是 2 倍。
  • 对角的例子 diag(2,3)\mathrm{diag}(2, 3):行列式 6.0,逆的行列式 0.1667,也就是 16\tfrac16。
  • 转置、换两列、零列,三条列性质都成立。
  • 最后两行:我取了随机的单位下三角 LL 和上三角 UU,M=LUM = LU,det⁡L=1.0\det L = 1.0,det⁡M=det⁡U=0.007649\det M = \det U = 0.007649,转置之后 det⁡MT=det⁡UT=0.007649\det M^\mathsf{T} = \det U^\mathsf{T} = 0.007649。

行列式不是数值上的奇异检验#

这一节是我自己加的,不是视频里的内容。Strang 说行列式是“可逆性的检验”,数学上完全对。但是写代码的人要小心:这个检验在浮点数里不太可靠。

最简单的例子:A=0.1I400A = 0.1 I_{400},也就是对角线全是 0.1 的 400 阶对角矩阵。它显然可逆,秩 400,条件数 1,和单位矩阵一样好。可它的行列式是 10−40010^{-400},比双精度能表示的最小正数(约 5×10−3245 \times 10^{-324})还小,会下溢:

import numpy as np
def main() -> None:
np.set_printoptions(precision=4, suppress=True)
# 可逆的矩阵,行列式在浮点数里却可能是 0.0:400 阶的 0.1 * I
n = 400
A = 0.1 * np.eye(n)
sign, logdet = np.linalg.slogdet(A)
print("det(0.1 I) =", np.linalg.det(A))
print("slogdet:符号", sign, ",log|det| =", round(logdet, 4))
print("log10|det| =", round(logdet / np.log(10.0), 1))
print("秩 =", np.linalg.matrix_rank(A), ",条件数 =", np.linalg.cond(A))
x = np.linalg.solve(A, np.ones(n))
print("solve 没问题:x[0] =", round(x[0], 4))
if __name__ == "__main__":
main()
det(0.1 I) = 0.0
slogdet:符号 1.0 ,log|det| = -921.034
log10|det| = -400.0
秩 = 400 ,条件数 = 1.0
solve 没问题:x[0] = 10.0

np.linalg.det 返回 0.0。如果拿 det == 0 判断奇异,这个矩阵会被判成奇异。但 np.linalg.slogdet 分开算符号和 log⁡∣det⁡∣\log|\det|:符号是 +1+1,log⁡∣det⁡A∣=−921.034\log|\det A| = -921.034,除以 ln⁡10\ln 10 就是 −400.0-400.0,恰好是 10−40010^{-400}。秩是 400,条件数是 1.0,np.linalg.solve 解出 x0=10.0x_0 = 10.0,一点问题没有。

所以我的习惯是:理论里 det⁡A≠0\det A \ne 0,代码里看秩、条件数,或者用 slogdet。(det 是为了写证明,不是为了写 if。)反过来,上面 det⁡(2A)=2ndet⁡A\det(2A) = 2^n \det A 也提醒我们:行列式对整体缩放极其敏感,它的大小本来就不代表矩阵“离奇异有多远”。

最后一件事。他在视频末尾留了一个尾巴:每个置换要么是奇数次换行,要么是偶数次,不能两者兼有,否则性质 2 自相矛盾,“整个讲座就完了”。这个事实只需要再多一点代数技巧,他说,而一旦成立,行列式就由性质 1、2、3 唯一确定。这一点正好连到下一讲:他会从这三条性质直接写出 n!n! 项的大公式,再按行展开出代数余子式,见 18.06 第 19 讲:行列式公式与代数余子式。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
18.06 第 18 讲:行列式的性质
https://bfmhno3.github.io/posts/linear-algebra-18-determinant-properties/
作者
bfmhno3
发布于
2026-10-18
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
18.06 第 19 讲:行列式公式与代数余子式
NoteMIT 18.06 第 19 讲的配套笔记:我用 Python 3.13 和 NumPy 2.4 以 itertools.permutations 实现行列式的大公式、余子式展开和三对角矩阵的递推,6 × 6 随机矩阵的 720 项之和与 np.linalg.det 都是 -7.439860,61 阶三对角矩阵的行列式则是 1。
2
18.06 第 20 讲:Cramer 法则、逆矩阵与体积
NoteMIT 18.06 第 20 讲的配套笔记:我用 Python 3.13 和 NumPy 2.4 验证了余子式求逆、Cramer 法则和 |det| 等于面积与体积,并数了一下:解 10 阶方程组,Cramer 法则(用余子式算行列式)要 68588300 次乘法,消元大约 333 次,差了 205971 倍。
3
18.06 第 22 讲:对角化与矩阵的幂
NoteMIT 18.06 第 22 讲的配套笔记:我用 Python 3.13 和 NumPy 2.4 把 A = SΛS⁻¹ 用在 Fibonacci 的 2 × 2 矩阵上,靠两个特征值直接算出第 100 项,和 Python 整数精确值的相对误差只有 3.33e-15。
4
18.06 第 34 讲:期末复习
NoteMIT 18.06 第 34 讲的配套笔记:我用 Python 3.13 和 NumPy 2.4 把 Strang 期末复习课上的每道题都算了一遍,Markov 矩阵从 (0, 10, 0) 出发迭代 10 步,离稳态 (3, 3, 4) 只剩 7.24e-07。
5
18.06 第 1 讲:方程组的行图与列图
NoteMIT 18.06 第 1 讲的配套笔记:我用 Python 3.13 和 NumPy 2.4 把 2 × 2 与 3 × 3 方程组分别按行图、列图和 A @ x 解了一遍,再把第三列换成前两列之和,秩从 3 掉到 2,最近的可达点与 b 差了 0.4082。
随机文章随机推荐

评论区

Profile Image of the Author
bfmhno3
Hello, I'm bfmhno3.
公告
本站曾使用 Jekyll + Minimal Mistakes 主题,现已迁移至 Astro + Firefly。博客内容未变,但文章链接有所调整,请自行查找需要的文章。
分类
标签
最新动态
站点统计
文章
123
分类
2
标签
239
总字数
753,253
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录