Appearance
线性代数:矩阵运算、向量空间、特征值
概念
上一篇(微积分)讲"连续变化",这一篇讲"线性的结构"。
但先要把最常见的那个误解拆掉:
"矩阵就是一个数字方阵。" ——这只说对了它的外观。
线性代数的核心观点是:
怎么理解这句话:一个线性变换(旋转、缩放、投影、剪切)作用在空间上,会把你选的那组基向量各自搬到某个新位置。 把这些"新位置"按列排起来,就是矩阵。
⎡ a b ⎤ ⎡1⎤ ⎡a⎤
A = ⎢ ⎥ 的含义是: A 把 ⎢ ⎥ 搬到 ⎢ ⎥ , 把
⎣ c d ⎦ ⎣0⎦ ⎣c⎦
⎡0⎤ ⎡b⎤
⎢ ⎥ 搬到 ⎢ ⎥
⎣1⎦ ⎣d⎦于是"矩阵乘向量"根本不是"背公式",而是"问:这个变换把 (x, y) 搬到哪儿去":
"结果的第 1 列乘 x、第 2 列乘 y,再相加"——这就是"列的观点",它比"行乘列"好记一百倍,而且解释了为什么矩阵乘法要那么定义。
为什么主线需要它:
| 落点 | 用线性代数做什么 |
|---|---|
soft 图形学 | 平移/旋转/缩放全是 3×3 或 4×4 矩阵 |
soft 机器学习 | 数据是矩阵;SVD/特征值做降维 |
ds 图算法 | 邻接矩阵;连通性靠矩阵幂/可达性 |
arch 性能 | 矩阵乘法是访存局部性的教科书例子 |
elec 信号 | 线性系统 = 矩阵;卷积可写成矩阵乘法 |
auto 稳定性 | 状态矩阵的特征值定稳定性(与 math/10 的微分方程特征根是同一件事) |
| 搜索与排序(PageRank) | 马尔可夫链的稳态 = 特征向量 |
⚠️ 本篇的边界:只讲"主线会用到的那一层"——向量与线性无关、矩阵运算、消元与秩、特征值。 SVD、二次型、若尔当标准形只在最后点一句。
本篇在主线上的位置:它是
math/10-calculus.md的"平行篇"——一个管连续(微积分),一个管结构(线性代数);soft的图形学与机器学习要它,auto的稳定性判据也要它。
原理
一、向量、线性无关与基
向量:n 个数的有序组 v = (v₁, v₂, …, vₙ)。在几何里它是"箭头",在数据里它是"一条记录",在图形学里它是"一个点的坐标"。
三个基本概念(一个套一个):
| 概念 | 定义 | 直觉 |
|---|---|---|
| 线性组合 | c₁v₁ + c₂v₂ + … + cₖvₖ | "用这几个向量凑出别的向量" |
| 线性无关 | 只有 c₁ = c₂ = … = 0 才能让组合等于零向量 | "谁都凑不出谁"——没冗余 |
| 张成(span) | 所有线性组合构成的集合 | "这几个向量能到达的全部地方" |
基:一组"线性无关且能张成整个空间"的向量。基里向量的个数叫空间的维数。
⚠️ 两个最常考的等价说法: ① "
k个n维向量当k > n时必线性相关"(向量比维数多,一定有多余的); ② 方阵可逆 ⟺ 它的列向量线性无关 ⟺det ≠ 0——这三句是同一件事的三种说法。
内积、夹角、正交:
| 结论 | 内容 |
|---|---|
| 正交(垂直) | u·v = 0 |
| 投影 | v 在 u 上的投影长度 = (u·v)/∣u∣ |
| 正交基的好处 | 求坐标不用解方程,直接做内积(这就是傅里叶变换能"一眼看出系数"的原因,见 math/21-complex.md) |
二、矩阵:定义、乘法与"不可交换"
矩阵乘法(列的观点,最好用):
"AB 的第 j 列 = 用 A 变换 B 的第 j 列"——这立刻解释了为什么"乘积的列数是 B 的列数"。
行乘列的计数(考的最多的一个数):
两个
n × n矩阵相乘,需要n³次乘法、n³ − n²次加法。
推导:结果有 n² 个元素,每个元素是"n 次乘 + n−1 次加" → 乘 n² · n = n³;加 n²(n−1) = n³ − n²。
n | n³(乘法次数) | 说明 |
|---|---|---|
| 2 | 8 | 手算级别 |
| 3 | 27 | 图形学天天用 |
| 10 | 1000 | — |
| 1000 | 10⁹ | 这就是"矩阵乘法是计算密集型任务"的来源 |
三条必须记住的性质:
| 性质 | 成立吗 | 说明 |
|---|---|---|
结合律 (AB)C = A(BC) | ✔ | "先转 90° 再放大"和"先合并两个变换"结果一样——这是所有优化(如 Strassen、分块)的前提 |
交换律 AB = BA | ✘(一般) | "先旋转再剪切" ≠ "先剪切再旋转"——几何上显而易见 |
分配律 A(B+C) = AB + AC | ✔ | — |
行列式:一个数,说明"体积缩放了多少":
| 变换 | 行列式 | 含义 |
|---|---|---|
| 单位矩阵 | 1 | 体积不变 |
缩放 (sx, sy)(2D) | sx · sy | 面积缩放倍数 |
| 旋转(2D) | 1 | 旋转不改变面积 |
| 投影(2D,压到一条线) | 0 | 面积被压没了 → 不可逆 |
| 镜像(2D) | −1 | 面积大小不变,但翻面了 |
转置与逆:(AB)^T = B^T A^T(顺序反了!);(AB)^{-1} = B^{-1}A^{-1}(同样反了!)——"先穿袜子再穿鞋,反过来就是先脱鞋再脱袜子"。
"顺序反转"是线性代数里最容易写错的地方。 它和图形学里的一个实际现象对应:"先平移再旋转"与"先旋转再平移"结果不同,而矩阵乘法写下来的顺序必须与操作顺序相反——
soft图形学会反复踩这个坑。
三、线性方程组:消元、秩与三种解
一个方程组写成矩阵形式:
高斯消元法(两步):
| 步 | 做什么 | 目的 |
|---|---|---|
| ① 前向消元 | 用第 i 行消掉下面各行的第 i 列 | 化成上三角 |
| ② 回代 | 从最后一行往前逐个求未知数 | 得解 |
⚠️ 部分主元(partial pivoting):每一步都选"当前列绝对值最大的那一行"做主元行并交换上来。 不这样做,主元可能是 0(做不下去)或非常小(误差爆炸)。
秩(rank)= 阶梯形里非零行的个数 = 线性无关的列的个数。
解有三种情形,判据是"秩与未知数个数的关系"(A 是 m × n,r = rank(A)):
| 情形 | 条件 | 几何图像 |
|---|---|---|
| 唯一解 | r = n 且 r = m | m 个方程正好定住 n 个未知数 |
| 无穷多解 | r < n(且方程组相容) | 有 n − r 个自由变量——解是一条线/一个面 |
| 无解 | rank(A) < rank([A∣b]) | b 不在列空间里 |
秩定理(Rank-Nullity):
即"列的秩 + 零空间的维数 = 未知数个数"——"能到达的维数"加"被压成 0 的维数"等于总维数。
四、向量空间与四个基本子空间
子空间:一个向量集合,满足"含零向量 + 对加法封闭 + 对数乘封闭"。 "封闭"是关键词——两条线交叉("十字形")不是子空间,因为两个方向相加会跑出去。
矩阵 A(m × n)的四个基本子空间:
| 子空间 | 定义 | 所在空间 | 维数 |
|---|---|---|---|
列空间 C(A) | 列的所有线性组合 | R^m | r |
零空间 N(A) | Ax = 0 的全部解 | R^n | n − r |
行空间 C(A^T) | 行的所有线性组合 | R^n | r |
左零空间 N(A^T) | A^T y = 0 的全部解 | R^m | m − r |
两组正交关系("四子空间图"的两条对角线):
"Ax = b 有解 ⟺ b ∈ C(A) ⟺ b ⊥ N(A^T)"——这三句话是同一件事。
这张图在主线里的用处:
ds里"图的可达性"、net里"流量守恒",本质上都是在问"某个向量在不在某个子空间里"。
五、特征值与特征向量:变换的"不变方向"
定义:
读法:"存在某些特殊方向,变换之后方向不变,只被拉伸了 λ 倍。"
关键理解:
| 说法 | 含义 |
|---|---|
v 是特征向量 | "不被带偏的方向" |
λ 是特征值 | "这个方向被拉伸/压缩/翻转了几倍" |
λ > 1 | 放大;0 < λ < 1 缩小;λ < 0 反向 |
λ = 0 | 这个方向被压成 0——所以 det = 0 |
求法(两步,务必会手算):
| 步 | 操作 |
|---|---|
| ① | 由 Av = λv 得 (A − λI)v = 0;它要有非零解,必须 det(A − λI) = 0(特征方程) |
| ② | 解这个多项式方程得 λ,再代回去解 (A − λI)v = 0 得 v |
两条免费的校验(算完必查):
"迹 = 特征值和;行列式 = 特征值积"——只要这两个对不上,特征值一定算错了。
对角化:
好处:A^k = P \Lambda^k P^{-1}——求 A 的一百次方,只要把 λ 各自求一百次方。 "矩阵的幂变简单"就是对角化的意义。
对称矩阵的福利:实对称矩阵的特征值全为实数,且一定可以选到一组正交的特征向量——所以 P 可以是正交矩阵,P^{-1} = P^T 不用求逆。 这就是 PCA、SVD 都建立在对称矩阵上的原因。
一个立刻能用的应用——马尔可夫链的稳态:转移矩阵 P 的行和为 1,它的特征值 λ = 1 对应的特征向量就是稳态分布 π(πP = π)——PageRank 用的就是这一条。
六、线性代数在主线的落点
| 落点 | 具体的矩阵是什么 |
|---|---|
soft 图形学 | 平移/旋转/缩放/投影矩阵(3×3 或 4×4 齐次坐标) |
soft 机器学习 | 数据矩阵、协方差矩阵、SVD 降维 |
auto 稳定性 | 状态矩阵的特征值(与 math/10 的微分方程特征根同源) |
arch 性能 | n³ 次乘法 + 访存局部性(分块乘法) |
elec 线性系统 | 卷积写成矩阵乘法(Toeplitz 矩阵) |
ds 图论 | 邻接矩阵;可达性 = 布尔矩阵幂 |
| 搜索排序 | 马尔可夫稳态 = PageRank |
一句话收束这一段:
"符合叠加原理"= 线性——这正是"线性代数"这个名字的来历,也是为什么它能同时管住图形学、控制系统和信号处理。
示例
例 1:矩阵乘法——结合律成立、交换律不成立、代价是 n³
python
import unicodedata
def w(s):
return sum(2 if unicodedata.east_asian_width(c) in "WF" else 1 for c in s)
def pad(s, n):
return s + " " * max(0, n - w(s))
def matmul(A, B):
n, m, p = len(A), len(B), len(B[0])
return [[sum(A[i][k] * B[k][j] for k in range(m)) for j in range(p)] for i in range(n)]
def show(M, label):
print(" " + label)
for row in M:
print(" [" + " ".join("%4d" % v for v in row) + "]")
A = [[1, 2, 3], [4, 5, 6], [7, 8, 10]]
B = [[0, 1, 0], [1, 0, 0], [0, 0, 1]] # 置换矩阵:交换前两个坐标
C = [[1, 0, 0], [0, 1, 0], [0, 0, 2]] # 对角矩阵:z 方向放大 2 倍
show(A, "A =")
show(B, "B =(交换前两列的置换矩阵)")
show(C, "C =(只有 z 放大 2 倍的对角矩阵)")
AB, BA = matmul(A, B), matmul(B, A)
show(AB, "AB =")
show(BA, "BA =")
print(" AB == BA ? %s ← 矩阵乘法不可交换" % ("是" if AB == BA else "否(这就是几何上「先转再剪 ≠ 先剪再转」)"))
L = matmul(matmul(A, B), C)
R = matmul(A, matmul(B, C))
show(L, "(AB)C =")
show(R, "A(BC) =")
print(" (AB)C == A(BC) ? %s ← 结合律成立,所以可以任意加括号(分块/并行优化的前提)"
% ("是" if L == R else "否"))
print()
print("=== 乘法代价:n×n 乘 n×n ===")
print(" " + pad("n", 8) + pad("乘法 n^3", 14) + pad("加法 n^3-n^2", 16) + "量级")
for n in (2, 3, 10, 100, 1000):
print(" " + pad(str(n), 8) + pad(str(n ** 3), 14) + pad(str(n ** 3 - n ** 2), 16)
+ ("手算" if n <= 3 else ("10 亿次,秒级" if n == 1000 else "—")))
print(" ★ 结果矩阵有 n^2 个元素,每个元素要 n 次乘、n-1 次加 → 乘 n^3、加 n^3-n^2")
print(" ★ 这正是 arch 用「矩阵乘法」讲分块(tiling)与 Cache 命中率的原因")预期输出:
A =
[ 1 2 3]
[ 4 5 6]
[ 7 8 10]
B =(交换前两列的置换矩阵)
[ 0 1 0]
[ 1 0 0]
[ 0 0 1]
C =(只有 z 放大 2 倍的对角矩阵)
[ 1 0 0]
[ 0 1 0]
[ 0 0 2]
AB =
[ 2 1 3]
[ 5 4 6]
[ 8 7 10]
BA =
[ 4 5 6]
[ 1 2 3]
[ 7 8 10]
AB == BA ? 否(这就是几何上「先转再剪 ≠ 先剪再转」) ← 矩阵乘法不可交换
(AB)C =
[ 2 1 6]
[ 5 4 12]
[ 8 7 20]
A(BC) =
[ 2 1 6]
[ 5 4 12]
[ 8 7 20]
(AB)C == A(BC) ? 是 ← 结合律成立,所以可以任意加括号(分块/并行优化的前提)
=== 乘法代价:n×n 乘 n×n ===
n 乘法 n^3 加法 n^3-n^2 量级
2 8 4 手算
3 27 18 手算
10 1000 900 —
100 1000000 990000 —
1000 1000000000 999000000 10 亿次,秒级
★ 结果矩阵有 n^2 个元素,每个元素要 n 次乘、n-1 次加 → 乘 n^3、加 n^3-n^2
★ 这正是 arch 用「矩阵乘法」讲分块(tiling)与 Cache 命中率的原因例 2:高斯消元解 3×3 方程组(完整计算过程)
方程组:
python
import unicodedata
def w(s):
return sum(2 if unicodedata.east_asian_width(c) in "WF" else 1 for c in s)
def pad(s, n):
return s + " " * max(0, n - w(s))
def show_aug(M, label):
print(" " + label)
for row in M:
print(" [" + " ".join("%9.4f" % v for v in row[:-1]) + " | " + "%9.4f" % row[-1] + "]")
def gauss(A, b):
n = len(A)
M = [A[i][:] + [b[i]] for i in range(n)]
show_aug(M, "增广矩阵 [A | b]:")
for k in range(n):
piv = max(range(k, n), key=lambda i: abs(M[i][k]))
if piv != k:
M[k], M[piv] = M[piv], M[k]
print(" 第 %d 步:行交换 R%d ↔ R%d(部分主元:选绝对值最大的上来)"
% (k + 1, k + 1, piv + 1))
if abs(M[k][k]) < 1e-12:
raise ValueError("主元为 0,矩阵奇异")
for i in range(k + 1, n):
f = M[i][k] / M[k][k]
for j in range(k, n + 1):
M[i][j] -= f * M[k][j]
print(" 第 %d 步:R%d ← R%d − (%+.4f)·R%d" % (k + 1, i + 1, i + 1, f, k + 1))
show_aug(M, "消元到第 %d 列后:" % (k + 1))
x = [0.0] * n
for i in range(n - 1, -1, -1):
s = M[i][n] - sum(M[i][j] * x[j] for j in range(i + 1, n))
x[i] = s / M[i][i]
print(" 回代:x%d = (%9.4f − %s) / %9.4f = %9.4f"
% (i + 1, M[i][n], " + ".join("%9.4f·x%d" % (M[i][j], j + 1) for j in range(i + 1, n)) or "0",
M[i][i], x[i]))
return x
A = [[2, 1, -1], [-3, -1, 2], [-2, 1, 2]]
b = [8, -11, -3]
print("=== 前向消元 + 回代 ===")
x = gauss(A, b)
print()
print(" 解:x = %.4f,y = %.4f,z = %.4f" % tuple(x))
print()
print("=== 代回原方程校验 ===")
for i in range(3):
lhs = sum(A[i][j] * x[j] for j in range(3))
print(" 第 %d 行:%s = %.4f(原方程右端 %d)%s"
% (i + 1, " ".join("%+d×%.4f" % (A[i][j], x[j]) for j in range(3)),
lhs, b[i], "✔" if abs(lhs - b[i]) < 1e-9 else "✘"))预期输出:
=== 前向消元 + 回代 ===
增广矩阵 [A | b]:
[ 2.0000 1.0000 -1.0000 | 8.0000]
[ -3.0000 -1.0000 2.0000 | -11.0000]
[ -2.0000 1.0000 2.0000 | -3.0000]
第 1 步:行交换 R1 ↔ R2(部分主元:选绝对值最大的上来)
第 1 步:R2 ← R2 − (-0.6667)·R1
第 1 步:R3 ← R3 − (+0.6667)·R1
消元到第 1 列后:
[ -3.0000 -1.0000 2.0000 | -11.0000]
[ 0.0000 0.3333 0.3333 | 0.6667]
[ 0.0000 1.6667 0.6667 | 4.3333]
第 2 步:行交换 R2 ↔ R3(部分主元:选绝对值最大的上来)
第 2 步:R3 ← R3 − (+0.2000)·R2
消元到第 2 列后:
[ -3.0000 -1.0000 2.0000 | -11.0000]
[ 0.0000 1.6667 0.6667 | 4.3333]
[ 0.0000 0.0000 0.2000 | -0.2000]
消元到第 3 列后:
[ -3.0000 -1.0000 2.0000 | -11.0000]
[ 0.0000 1.6667 0.6667 | 4.3333]
[ 0.0000 0.0000 0.2000 | -0.2000]
回代:x3 = ( -0.2000 − 0) / 0.2000 = -1.0000
回代:x2 = ( 4.3333 − 0.6667·x3) / 1.6667 = 3.0000
回代:x1 = ( -11.0000 − -1.0000·x2 + 2.0000·x3) / -3.0000 = 2.0000
解:x = 2.0000,y = 3.0000,z = -1.0000
=== 代回原方程校验 ===
第 1 行:+2×2.0000 +1×3.0000 -1×-1.0000 = 8.0000(原方程右端 8)✔
第 2 行:-3×2.0000 -1×3.0000 +2×-1.0000 = -11.0000(原方程右端 -11)✔
第 3 行:-2×2.0000 +1×3.0000 +2×-1.0000 = -3.0000(原方程右端 -3)✔例 3:行列式的几何意义与"可逆"的判定
python
import unicodedata
def w(s):
return sum(2 if unicodedata.east_asian_width(c) in "WF" else 1 for c in s)
def pad(s, n):
return s + " " * max(0, n - w(s))
def matmul(A, B):
n, m, p = len(A), len(B), len(B[0])
return [[sum(A[i][k] * B[k][j] for k in range(m)) for j in range(p)] for i in range(n)]
def det(A):
n = len(A)
if n == 1:
return A[0][0]
if n == 2:
return A[0][0] * A[1][1] - A[0][1] * A[1][0]
s = 0
for j in range(n):
minor = [row[:j] + row[j + 1:] for row in A[1:]]
s += ((-1) ** j) * A[0][j] * det(minor)
return s
def inv(A):
n = len(A)
M = [A[i][:] + [1 if i == j else 0 for j in range(n)] for i in range(n)]
for k in range(n):
piv = max(range(k, n), key=lambda i: abs(M[i][k]))
M[k], M[piv] = M[piv], M[k]
if abs(M[k][k]) < 1e-12:
return None
pv = M[k][k]
M[k] = [v / pv for v in M[k]]
for i in range(n):
if i != k and M[i][k] != 0:
f = M[i][k]
M[i] = [M[i][j] - f * M[k][j] for j in range(2 * n)]
return [row[n:] for row in M]
CASES = [
("[[1,2],[3,4]]", [[1, 2], [3, 4]], "一般 2×2"),
("缩放 [[3,0],[0,2]]", [[3, 0], [0, 2]], "面积缩放倍数 = 3×2 = 6"),
("旋转 [[0,-1],[1,0]]", [[0, -1], [1, 0]], "旋转不改变面积"),
("投影 [[1,0],[0,0]]", [[1, 0], [0, 0]], "面积被压成 0 → 不可逆"),
("[[1,2],[2,4]]", [[1, 2], [2, 4]], "第二列 = 第一列×2 → 列线性相关"),
("例 2 的 3×3 系数矩阵", [[2, 1, -1], [-3, -1, 2], [-2, 1, 2]], "例 2 有唯一解,det 必非 0"),
]
print("=== ① 行列式 = 体积(面积)缩放因子 ===")
print(" " + pad("矩阵", 22) + pad("det", 8) + pad("可逆", 8) + "说明")
for name, A, note in CASES:
d = det(A)
print(" " + pad(name, 22) + pad("%d" % d, 8) + pad("是" if d != 0 else "否", 8) + note)
print(" ★ det = 0 ⟺ 不可逆 ⟺ 列向量线性相关 ⟺ 有非零的 Ax = 0 解(四句话一件事)")
print()
print("=== ② 逆矩阵(Gauss-Jordan:[A | I] → [I | A^(-1)]) ===")
for name, A, note in CASES:
if det(A) == 0:
print(" " + pad(name, 22) + "det = 0 → 逆矩阵不存在")
continue
B = inv(A)
P = matmul(A, B)
n = len(A)
ok = all(abs(P[i][j] - (1 if i == j else 0)) < 1e-9 for i in range(n) for j in range(n))
print(" " + pad(name, 22) + "A^(-1) 各行 =")
for row in B:
print(" [" + " ".join("%9.4f" % v for v in row) + "]")
print(" A·A^(-1) == I ? %s" % ("是 ✔" if ok else "否 ✘"))
print()
print("=== ③ 一个易错点:(AB)^(-1) = B^(-1)A^(-1),顺序要反 ===")
A2, B2 = [[1, 2], [3, 4]], [[2, 0], [1, 3]]
iA, iB = inv(A2), inv(B2)
def rnd(M):
return "[" + ", ".join("[%s]" % ", ".join("%.4f" % v for v in row) for row in M) + "]"
print(" A2 = [[1,2],[3,4]],B2 = [[2,0],[1,3]]")
print(" (A2·B2)^(-1) =", rnd(inv(matmul(A2, B2))))
print(" B2^(-1)·A2^(-1) =", rnd(matmul(iB, iA)), " ← 与上一行相等")
print(" A2^(-1)·B2^(-1) =", rnd(matmul(iA, iB)), " ← 与上一行不等")
print(" ★ 正确写法只有 B^(-1)·A^(-1) 一种(与「先穿袜后穿鞋」反过来同理)")预期输出:
=== ① 行列式 = 体积(面积)缩放因子 ===
矩阵 det 可逆 说明
[[1,2],[3,4]] -2 是 一般 2×2
缩放 [[3,0],[0,2]] 6 是 面积缩放倍数 = 3×2 = 6
旋转 [[0,-1],[1,0]] 1 是 旋转不改变面积
投影 [[1,0],[0,0]] 0 否 面积被压成 0 → 不可逆
[[1,2],[2,4]] 0 否 第二列 = 第一列×2 → 列线性相关
例 2 的 3×3 系数矩阵 -1 是 例 2 有唯一解,det 必非 0
★ det = 0 ⟺ 不可逆 ⟺ 列向量线性相关 ⟺ 有非零的 Ax = 0 解(四句话一件事)
=== ② 逆矩阵(Gauss-Jordan:[A | I] → [I | A^(-1)]) ===
[[1,2],[3,4]] A^(-1) 各行 =
[ -2.0000 1.0000]
[ 1.5000 -0.5000]
A·A^(-1) == I ? 是 ✔
缩放 [[3,0],[0,2]] A^(-1) 各行 =
[ 0.3333 0.0000]
[ 0.0000 0.5000]
A·A^(-1) == I ? 是 ✔
旋转 [[0,-1],[1,0]] A^(-1) 各行 =
[ 0.0000 1.0000]
[ -1.0000 -0.0000]
A·A^(-1) == I ? 是 ✔
投影 [[1,0],[0,0]] det = 0 → 逆矩阵不存在
[[1,2],[2,4]] det = 0 → 逆矩阵不存在
例 2 的 3×3 系数矩阵 A^(-1) 各行 =
[ 4.0000 3.0000 -1.0000]
[ -2.0000 -2.0000 1.0000]
[ 5.0000 4.0000 -1.0000]
A·A^(-1) == I ? 是 ✔
=== ③ 一个易错点:(AB)^(-1) = B^(-1)A^(-1),顺序要反 ===
A2 = [[1,2],[3,4]],B2 = [[2,0],[1,3]]
(A2·B2)^(-1) = [[-1.0000, 0.5000], [0.8333, -0.3333]]
B2^(-1)·A2^(-1) = [[-1.0000, 0.5000], [0.8333, -0.3333]] ← 与上一行相等
A2^(-1)·B2^(-1) = [[-1.1667, 0.3333], [0.8333, -0.1667]] ← 与上一行不等
★ 正确写法只有 B^(-1)·A^(-1) 一种(与「先穿袜后穿鞋」反过来同理)例 4:特征值、迹/行列式校验与马尔可夫稳态
python
import unicodedata
def w(s):
return sum(2 if unicodedata.east_asian_width(c) in "WF" else 1 for c in s)
def pad(s, n):
return s + " " * max(0, n - w(s))
def eig2(A):
a, b, c, d = A[0][0], A[0][1], A[1][0], A[1][1]
tr, de = a + d, a * d - b * c
disc = tr * tr - 4 * de
if disc < 0:
return None, tr, de
r = disc ** 0.5
return ((tr + r) / 2, (tr - r) / 2), tr, de
def matvec(A, v):
return [sum(A[i][j] * v[j] for j in range(len(v))) for i in range(len(A))]
print("=== ① 2×2 特征值:解 λ^2 − tr·λ + det = 0 ===")
print(" " + pad("矩阵", 24) + pad("λ1", 10) + pad("λ2", 10) + pad("迹校验", 18) + "行列式校验")
TWO = [
("[[2,1],[1,2]]", [[2, 1], [1, 2]]),
("[[4,1],[2,3]]", [[4, 1], [2, 3]]),
("[[0.9,0.2],[0.1,0.8]]", [[0.9, 0.2], [0.1, 0.8]]),
("[[1,2],[2,4]]", [[1, 2], [2, 4]]),
]
for name, A in TWO:
lam, tr, de = eig2(A)
print(" " + pad(name, 24) + pad("%.4f" % lam[0], 10) + pad("%.4f" % lam[1], 10)
+ pad("%.4f vs %.4f" % (tr, lam[0] + lam[1]), 18)
+ "%.4f vs %.4f" % (de, lam[0] * lam[1]))
print(" ★ 特征方程 det(A − λI) = λ^2 − tr(A)λ + det(A) = 0 —— 两条免费校验都在这里")
print(" ★ [[1,2],[2,4]] 的两个特征值是 5 和 0:det = 0 ⟺ 有一个特征值为 0")
print()
print("=== ② 3×3(分块对角):λ = 2 / 1 / 11 ===")
A3 = [[2, 0, 0], [0, 3, 4], [0, 4, 9]]
lam3 = [2, 11, 1]
tr3 = sum(A3[i][i] for i in range(3))
de3 = det3 = A3[0][0] * (A3[1][1] * A3[2][2] - A3[1][2] * A3[2][1])
print(" A3 = [[2,0,0],[0,3,4],[0,4,9]]")
print(" 分块:右上角是 0 → 特征值 = 左上角的 2 ⊕ 右下角 2×2 的特征值")
print(" 右下角 [[3,4],[4,9]]:λ^2 − 12λ + (27−16) = λ^2 − 12λ + 11 → λ = %s" % str(eig2([[3, 4], [4, 9]])[0]))
print(" 校验:迹 = %d,Σλ = %d;det = %d,Πλ = %d" % (tr3, sum(lam3), de3, 2 * 11 * 1))
print(" 结论:%s" % ("✔ 两条校验都通过" if tr3 == sum(lam3) and de3 == 2 * 11 * 1 else "✘ 有问题"))
print()
print("=== ③ 马尔可夫链的稳态:λ = 1 的特征向量(PageRank 的原理) ===")
P = [[0.9, 0.2], [0.1, 0.8]]
print(" 转移矩阵 P = [[0.9,0.2],[0.1,0.8]](列和为 1,表示「保持 0.9 / 切换 0.1」)")
lam, tr, de = eig2(P)
print(" 特征值:λ1 = %.4f(必为 1),λ2 = %.4f(= 收敛速度)" % (lam[0], lam[1]))
print(" 迹校验:%.4f = %.4f + %.4f ✔" % (tr, lam[0], lam[1]))
print()
print(" " + pad("迭代 k 次", 12) + pad("分布 v", 26) + pad("与 (2/3,1/3) 的距离", 20) + "λ2^k")
for k in [1, 2, 5, 10, 20, 30, 50]:
v2 = [1.0, 0.0]
for _ in range(k):
v2 = matvec(P, v2)
s = sum(v2)
v2 = [x / s for x in v2]
d = max(abs(v2[0] - 2 / 3), abs(v2[1] - 1 / 3))
print(" " + pad("%d" % k, 12) + pad("(%.6f, %.6f)" % (v2[0], v2[1]), 26)
+ pad("%.3e" % d, 20) + "%.3e" % (0.7 ** k))
print(" ★ 稳态 π = (2/3, 1/3):解 πP = π 得 -0.1π1 + 0.2π2 = 0 → π1 = 2π2")
print(" ★ 收敛速度正是 λ2 = 0.7:误差 ≈ 0.7^k,这就是「第二大特征值决定收敛快慢」")预期输出:
=== ① 2×2 特征值:解 λ^2 − tr·λ + det = 0 ===
矩阵 λ1 λ2 迹校验 行列式校验
[[2,1],[1,2]] 3.0000 1.0000 4.0000 vs 4.0000 3.0000 vs 3.0000
[[4,1],[2,3]] 5.0000 2.0000 7.0000 vs 7.0000 10.0000 vs 10.0000
[[0.9,0.2],[0.1,0.8]] 1.0000 0.7000 1.7000 vs 1.7000 0.7000 vs 0.7000
[[1,2],[2,4]] 5.0000 0.0000 5.0000 vs 5.0000 0.0000 vs 0.0000
★ 特征方程 det(A − λI) = λ^2 − tr(A)λ + det(A) = 0 —— 两条免费校验都在这里
★ [[1,2],[2,4]] 的两个特征值是 5 和 0:det = 0 ⟺ 有一个特征值为 0
=== ② 3×3(分块对角):λ = 2 / 1 / 11 ===
A3 = [[2,0,0],[0,3,4],[0,4,9]]
分块:右上角是 0 → 特征值 = 左上角的 2 ⊕ 右下角 2×2 的特征值
右下角 [[3,4],[4,9]]:λ^2 − 12λ + (27−16) = λ^2 − 12λ + 11 → λ = (11.0, 1.0)
校验:迹 = 14,Σλ = 14;det = 22,Πλ = 22
结论:✔ 两条校验都通过
=== ③ 马尔可夫链的稳态:λ = 1 的特征向量(PageRank 的原理) ===
转移矩阵 P = [[0.9,0.2],[0.1,0.8]](列和为 1,表示「保持 0.9 / 切换 0.1」)
特征值:λ1 = 1.0000(必为 1),λ2 = 0.7000(= 收敛速度)
迹校验:1.7000 = 1.0000 + 0.7000 ✔
迭代 k 次 分布 v 与 (2/3,1/3) 的距离 λ2^k
1 (0.900000, 0.100000) 2.333e-01 7.000e-01
2 (0.830000, 0.170000) 1.633e-01 4.900e-01
5 (0.722690, 0.277310) 5.602e-02 1.681e-01
10 (0.676083, 0.323917) 9.416e-03 2.825e-02
20 (0.666933, 0.333067) 2.660e-04 7.979e-04
30 (0.666674, 0.333326) 7.513e-06 2.254e-05
50 (0.666667, 0.333333) 5.995e-09 1.798e-08
★ 稳态 π = (2/3, 1/3):解 πP = π 得 -0.1π1 + 0.2π2 = 0 → π1 = 2π2
★ 收敛速度正是 λ2 = 0.7:误差 ≈ 0.7^k,这就是「第二大特征值决定收敛快慢」例 5:用矩阵做图形变换(soft 图形学 + auto 状态方程的同一套工具)
python
import math, unicodedata
def w(s):
return sum(2 if unicodedata.east_asian_width(c) in "WF" else 1 for c in s)
def pad(s, n):
return s + " " * max(0, n - w(s))
def mul(A, B):
return [[sum(A[i][k] * B[k][j] for k in range(len(B))) for j in range(len(B[0]))] for i in range(len(A))]
def apply(A, p):
return [sum(A[i][j] * p[j] for j in range(len(p))) for i in range(len(A))]
def show3(M, label):
print(" " + label)
for row in M:
print(" [" + " ".join("%8.4f" % v for v in row) + "]")
c, s = math.cos(math.radians(90)), math.sin(math.radians(90))
R = [[c, -s, 0], [s, c, 0], [0, 0, 1]] # 旋转 90°
T = [[1, 0, 2], [0, 1, 1], [0, 0, 1]] # 平移 (2, 1)(齐次坐标)
S = [[3, 0, 0], [0, 3, 0], [0, 0, 1]] # 缩放 3 倍
def det3(M):
return (M[0][0] * (M[1][1] * M[2][2] - M[1][2] * M[2][1])
- M[0][1] * (M[1][0] * M[2][2] - M[1][2] * M[2][0])
+ M[0][2] * (M[1][0] * M[2][1] - M[1][1] * M[2][0]))
print("=== ① 三个基本变换矩阵(3×3 齐次坐标) ===")
show3(R, "R = 旋转 90°:")
show3(T, "T = 平移 (2,1):")
show3(S, "S = 缩放 3 倍:")
print(" 行列式(面积缩放因子):det(R) = %.4f,det(T) = %.4f,det(S) = %.4f"
% (det3(R), det3(T), det3(S)))
print(" ★ 旋转与平移的 det 都是 1(保面积);缩放是 9(3×3)")
print()
print("=== ② 旋转两次 90° == 旋转一次 180° ===")
RR = mul(R, R)
c2, s2 = math.cos(math.radians(180)), math.sin(math.radians(180))
R180 = [[c2, -s2, 0], [s2, c2, 0], [0, 0, 1]]
show3(RR, "R·R =")
show3(R180, "R(180°) =")
same = all(abs(RR[i][j] - R180[i][j]) < 1e-12 for i in range(3) for j in range(3))
print(" 两者相等? %s" % ("是 ✔" if same else "否 ✘"))
print()
print("=== ③ 顺序很重要:先转后移 ≠ 先移后转 ===")
p = [1, 0, 1] # 点 (1, 0)
q1 = apply(mul(T, R), p) # 先旋转,再平移 → T·R·p
q2 = apply(mul(R, T), p) # 先平移,再旋转 → R·T·p
print(" 起点 p = (1, 0)")
print(" 先旋转 90°、再平移 (2,1):T·R·p = (%.4f, %.4f)" % (q1[0], q1[1]))
print(" 先平移 (2,1)、再旋转 90°:R·T·p = (%.4f, %.4f)" % (q2[0], q2[1]))
print(" ★ 矩阵乘法的顺序与「操作的先后」相反:先做的写在右边(右乘)")
print()
print("=== ④ 单点旋转的数值核对(旋转 30°) ===")
th = 30
R30 = [[math.cos(math.radians(th)), -math.sin(math.radians(th)), 0],
[math.sin(math.radians(th)), math.cos(math.radians(th)), 0],
[0, 0, 1]]
q = apply(R30, [1, 0, 1])
print(" 把 (1,0) 旋转 30°:(cos30°, sin30°) = (%.6f, %.6f)" % (q[0], q[1]))
print(" √3/2 = %.6f,1/2 = %.6f" % (math.sqrt(3) / 2, 0.5))
print(" ★ 长度守恒核对:旋转前 |p| = 1,旋转后 |q| = %.6f" % math.hypot(q[0], q[1]))
print(" ★ 同一套矩阵在 auto 里叫「状态转移矩阵」,特征值就是它的「极点」")预期输出:
=== ① 三个基本变换矩阵(3×3 齐次坐标) ===
R = 旋转 90°:
[ 0.0000 -1.0000 0.0000]
[ 1.0000 0.0000 0.0000]
[ 0.0000 0.0000 1.0000]
T = 平移 (2,1):
[ 1.0000 0.0000 2.0000]
[ 0.0000 1.0000 1.0000]
[ 0.0000 0.0000 1.0000]
S = 缩放 3 倍:
[ 3.0000 0.0000 0.0000]
[ 0.0000 3.0000 0.0000]
[ 0.0000 0.0000 1.0000]
行列式(面积缩放因子):det(R) = 1.0000,det(T) = 1.0000,det(S) = 9.0000
★ 旋转与平移的 det 都是 1(保面积);缩放是 9(3×3)
=== ② 旋转两次 90° == 旋转一次 180° ===
R·R =
[ -1.0000 -0.0000 0.0000]
[ 0.0000 -1.0000 0.0000]
[ 0.0000 0.0000 1.0000]
R(180°) =
[ -1.0000 -0.0000 0.0000]
[ 0.0000 -1.0000 0.0000]
[ 0.0000 0.0000 1.0000]
两者相等? 是 ✔
=== ③ 顺序很重要:先转后移 ≠ 先移后转 ===
起点 p = (1, 0)
先旋转 90°、再平移 (2,1):T·R·p = (2.0000, 2.0000)
先平移 (2,1)、再旋转 90°:R·T·p = (-1.0000, 3.0000)
★ 矩阵乘法的顺序与「操作的先后」相反:先做的写在右边(右乘)
=== ④ 单点旋转的数值核对(旋转 30°) ===
把 (1,0) 旋转 30°:(cos30°, sin30°) = (0.866025, 0.500000)
√3/2 = 0.866025,1/2 = 0.500000
★ 长度守恒核对:旋转前 |p| = 1,旋转后 |q| = 1.000000
★ 同一套矩阵在 auto 里叫「状态转移矩阵」,特征值就是它的「极点」考点
考点
1. 三个等价说法(背成一条链)
2. 矩阵乘法的代价与两条性质
- 两个
n × n矩阵相乘:乘法n³次、加法n³ − n²次(结果n²个元素,每个n次乘); - 结合律 ✔(可以任意加括号 → 分块、并行、Strassen 的前提);
- 交换律 ✘(一般
AB ≠ BA); (AB)^T = B^T A^T、(AB)^{-1} = B^{-1}A^{-1}——顺序都要反过来。
3. 线性方程组解的三情形(判据是秩)
| 情形 | 条件 |
|---|---|
| 唯一解 | rank(A) = n 且 rank(A) = m |
| 无穷多解 | rank(A) < n 且 rank(A) = rank([A∣b]) |
| 无解 | rank(A) < rank([A∣b])(b 不在列空间里) |
- 秩定理:
rank(A) + nullity(A) = n; - 高斯消元两步 = 前向消元 + 回代;必须做部分主元(选当前列绝对值最大的行)。
4. 四个基本子空间
| 子空间 | 所在空间 | 维数 |
|---|---|---|
列空间 C(A) | R^m | r |
零空间 N(A) | R^n | n − r |
行空间 C(A^T) | R^n | r |
左零空间 N(A^T) | R^m | m − r |
- 行空间 ⊥ 零空间;列空间 ⊥ 左零空间;
Ax = b有解 ⟺b ∈ C(A)⟺b ⊥ N(A^T)。
5. 特征值:定义、求法、两条免费校验
- 定义
Av = λv:"方向不变的向量叫特征向量,拉伸倍数叫特征值"; - 特征方程
det(A − λI) = 0; - 校验一:
tr(A) = Σλᵢ(迹等于特征值之和); - 校验二:
det(A) = Πλᵢ(行列式等于特征值之积); λ = 0出现 ⟺det(A) = 0⟺ 不可逆。
6. 对角化与两个"特权"
A = PΛP^{-1}→A^k = PΛ^kP^{-1}(求幂变简单);- 实对称矩阵:特征值全实、特征向量可取正交基,
P^{-1} = P^T; - 马尔可夫转移矩阵:必有一个
λ = 1,其对应特征向量就是稳态分布π(πP = π);收敛速度由第二大特征值λ₂决定。
7. 本节五个易错点
- 矩阵乘法不可交换:写
A·B前先想清"谁是先做的"——图形学里"先转后移 ≠ 先移后转"; (AB)^{-1} = B^{-1}A^{-1},不是A^{-1}B^{-1};n³是"乘 + 加都算"还是"只算乘",读题要看清——本项目口径:乘法n³、加法n³ − n²;- 行列式不是"元素的乘积":它是带符号的展开,
2×2的ad − bc、3×3要按行展开; - 秩的定义容易被忽略:"非零行数"和"最大线性无关组的个数"是同一件事,别把
rank与n混。
小结
- 线性代数研究"线性结构":矩阵不是数字表格,是线性变换的记账本(列 = 基向量被搬到的位置)。
- 向量三件套:线性组合 → 线性无关 → 张成;基里向量个数 = 维数;
k > n个n维向量必线性相关。 - 矩阵乘法:代价
n³次乘、n³−n²次加;结合律成立、交换律不成立;转置与求逆都要反过来写。 - 行列式 = 体积缩放因子:
det = 0是不可逆的判据;旋转/平移det = 1,投影det = 0。 - 方程组三情形看秩:唯一解 / 无穷多解 / 无解;秩定理
rank + nullity = n;消元必须选主元。 - 四个基本子空间:列空间、零空间、行空间、左零空间,维数
r / n−r / r / m−r。 - 特征值:
Av = λv;tr = Σλ、det = Πλ两条免费校验;对角化让幂变简单;马尔可夫稳态是λ=1的特征向量。 - 三个手算锚点:
[[1,2],[3,4]]的det = −2;[[2,1],[1,2]]的λ = 3, 1;[[0.9,0.2],[0.1,0.8]]的稳态是(2/3, 1/3),收敛速度0.7^k。
回到主线:这一篇与 math/10-calculus.md 是一对——一个管连续,一个管结构。
它把主线里几处"已经在用但没说破"的东西补上了出处: ①
arch用「矩阵乘法」讲Cache与分块优化——支撑它的是"结合律成立 +n³次运算";②auto里"系统稳定 ⟺ 极点在左半平面"——就是本篇的"状态矩阵特征值实部为负"(与math/10的微分方程特征根是同一个东西的两种语言);③ds里邻接矩阵的幂与可达性、PageRank 的稳态——就是"矩阵的幂"与"λ=1的特征向量"。再往
soft走,这套语言会更密集:图形学的变换矩阵、机器学习的数据矩阵与 SVD、密码学的有限域矩阵,全是本篇的直接延伸。
下一篇是概率论与数理统计:它给 arch 的"命中率与性能评价"、ds 的"散列表性能分析"、os 的"排队与平均等待时间"提供工具。 核心是三个问题:随机变量怎么描述?一堆样本怎么估计参数?"两个东西到底有没有关系"怎么判断?
下一篇:概率论与数理统计
评论(0)
当前浏览器不允许本地存储,评论无法保存。
还没有评论,来说两句。