~4 giờ · cần xong bài 6 trước
Bài 6 dạy bạn rằng cùng một phép biến đổi trông khác nhau trong các cơ sở khác nhau (
P⁻¹AP). Bài 7 trả lời câu hỏi tiếp theo, và là câu hỏi thật sự: cơ sở nào làm nó trông ĐƠN GIẢN NHẤT? Đáp án: cơ sở tạo bởi chính các eigenvector — ở đó ma trận biến thành đường chéo.
🧭 Làm theo thứ tự này
- Làm ô ⚡ bên dưới (chưa cần xem video).
- Đọc §1-2 (định nghĩa, và vì sao
det(A − λI) = 0). - Tự giải tay §3 và §4 (ma trận
AvàB) trước khi xem đáp án. - Xem tập 14 Eigenvectors and eigenvalues sau khi đã tự giải tay. Hai đoạn đáng tua lại:
det(A − λI) = 0, và phép quay không có eigenvector thực. Tập 15 tuỳ chọn. - Đọc §5, §6 (ý trung tâm của bài) và §7. (§5b và §6b là phần mở rộng, để sau.)
- Làm 3 việc tay bắt buộc ở cuối bài (chéo hoá
C = [[4,1],[2,3]]trên giấy, numpy chỉ để chấm), rồi code (tự viết xong mới mở code mẫu trong bài). - Tự kiểm.
Xong bài khi: nói được eigenvector là trục không đổi hướng, và vì sao lấy chúng làm cơ sở thì ma trận thành đường chéo.
⚡ 30 phút đầu — làm trước, đọc sau
import numpy as np
B = np.array([[2., 1.], [1., 2.]])
R = np.array([[0., -1.], [1., 0.]]) # quay 90°
for v in [np.array([1., 0.]), np.array([1., 1.]), np.array([1., -1.])]:
print(v, "->", B @ v) # vector nào GIỮ NGUYÊN HƯỚNG, chỉ đổi độ dài?
for v in [np.array([1., 0.]), np.array([1., 1.]), np.array([3., 2.])]:
print(v, "->", R @ v) # có vector nào giữ nguyên hướng không?
Đích cụ thể phải ra:
[1. 0.] -> [2. 1.] ← đổi hướng, KHÔNG phải eigenvector
[1. 1.] -> [3. 3.] ← cùng hướng, dài gấp 3 → eigenvalue = 3
[ 1. -1.] -> [ 1. -1.] ← đứng im hoàn toàn → eigenvalue = 1
[1. 0.] -> [0. 1.] ← quay đi
[1. 1.] -> [-1. 1.] ← quay đi
[3. 2.] -> [-2. 3.] ← quay đi. R KHÔNG có eigenvector thực nào
Ra đúng ba dòng đầu → bạn đã nhìn thấy toàn bộ định nghĩa của bài này bằng mắt, trước khi đọc một chữ lý thuyết nào. Ba dòng sau là cái bẫy lớn nhất của bài, xử ở §5.
1. Định nghĩa — và nó ngắn hơn bạn tưởng
Từ bài 2, ma trận là động từ: nó di chuyển mọi điểm của không gian. Hầu hết vector bị nó vừa kéo giãn vừa xoay lệch đi.
Nhưng với một số ma trận, có vài hướng đặc biệt sống sót:
Eigenvector = vector KHÔNG bị đổi hướng qua phép biến đổi — chỉ bị co hoặc giãn. Eigenvalue = hệ số co/giãn đó.
Viết thành một dòng, và đây là dòng quan trọng nhất bài:
A v = λ v với v ≠ 0
Đọc bằng lời: "Đưa v qua cả một phép biến đổi phức tạp cũng chỉ tương đương với việc nhân v với một con số."
Chú ý cỡ của hai vế: bên trái là ma trận nhân vector (n² phép nhân), bên phải là một số nhân vector (n phép nhân). Đó là toàn bộ sức hấp dẫn: trên đúng những hướng đó, một phép biến đổi O(n²) sụp xuống thành O(n).
Vài quy ước phải nhớ ngay, kẻo mất thời gian tranh cãi vô ích:
| Câu hỏi | Trả lời |
|---|---|
v = 0 có phải eigenvector không? |
Không. A·0 = λ·0 đúng với mọi λ → vô nghĩa. Loại từ định nghĩa. |
λ = 0 có hợp lệ không? |
Có. Nghĩa là A v = 0: v bị bóp về gốc. λ = 0 ⟺ det(A) = 0 ⟺ ma trận suy biến. |
Nếu v là eigenvector thì 2v, −v, 100v? |
Cũng là eigenvector, cùng λ. Eigenvector không phải một vector — nó là cả một đường thẳng (trục). |
| Ngược hướng có tính là "không đổi hướng"? | Có. λ < 0 nghĩa là lật ngược nhưng vẫn trên cùng trục đó. Trục mới là thứ được giữ, không phải chiều. |
Chuẩn hoá cách nói: eigenvector là một trục bất biến, không phải một mũi tên cụ thể.
numpytrả về mũi tên độ dài 1 chỉ vì phải in ra cái gì đó — dấu và độ dài của nó không mang thông tin.
2. Tìm chúng thế nào — vì sao lại là det(A − λI) = 0
Đây là chỗ đa số người học chỉ học thuộc. Đừng. Suy ra nó trong 4 dòng.
Xuất phát từ định nghĩa, dồn hết về một vế:
A v = λ v
A v − λ v = 0
A v − λ I v = 0 ← chèn I vào để λ thành MA TRẬN, mới trừ được
(A − λ I) v = 0
Dòng thứ 3 là dòng duy nhất có mẹo.
λlà một số,Alà ma trận — không trừ trực tiếp được.λIlà "nhân mọi thứ với λ" viết dưới dạng ma trận. ChènIlà ép hai vế cùng kiểu dữ liệu — đúng nghĩa một cúcast.
Giờ nhìn dòng cuối bằng con mắt bài 2 (ma trận = hành động) và bài 4 (định thức):
(A − λI) là một phép biến đổi. Nó ăn v khác 0 và nhả ra 0. Nghĩa là nó bóp dẹp không gian — có cả một trục bị nén về gốc toạ độ (v nằm trong null space).
Và bạn đã biết dấu hiệu của việc bóp dẹp:
det(A − λI) = 0Định thức = hệ số co giãn diện tích. Bóp dẹp mặt phẳng thành đường thẳng ⟹ diện tích ra 0 ⟹ định thức bằng 0.
Vế trái, khi khai triển, là một đa thức bậc n theo λ — gọi là đa thức đặc trưng. Nghiệm của nó chính là các eigenvalue.
Quy trình 3 bước, dùng nguyên cho phần còn lại của bài:
Bước 1: viết det(A − λI) = 0 → đa thức đặc trưng
Bước 2: giải ra λ₁, λ₂, ... → các eigenvalue
Bước 3: với mỗi λ, giải (A − λI) v = 0 → trục eigenvector tương ứng
Trường hợp 2×2 rút gọn rất đẹp (kiểm bằng số ở §3, §4):
det(A − λI) = λ² − trace(A)·λ + det(A) = 0
trace = tổng đường chéo. Hệ quả tính nhẩm được dùng suốt bài:
λ₁ + λ₂ = trace(A) λ₁ · λ₂ = det(A)
3. Ví dụ tay số 1 — ma trận tam giác A = [[3,1],[0,2]]
A = [ 3 1 ]
[ 0 2 ]
Bước 1 — đa thức đặc trưng. Trừ λ trên đường chéo:
A − λI = [ 3−λ 1 ]
[ 0 2−λ ]
det = (3−λ)(2−λ) − (1)(0) = (3−λ)(2−λ)
Ma trận tam giác nên số hạng dưới bị 0 xoá sạch — không cần nhân bung ra.
Bước 2 — giải.
(3−λ)(2−λ) = 0 → λ₁ = 3, λ₂ = 2
Kiểm chéo bằng trace/det: trace = 3 + 2 = 5, det = 3·2 − 1·0 = 6. Và λ₁+λ₂ = 5 ✓, λ₁·λ₂ = 6 ✓. Khai triển ra λ² − 5λ + 6 — numpy.poly(A) trả về [1, -5, 6], khớp.
Rút ra một quy tắc miễn phí: với ma trận tam giác (trên hoặc dưới), eigenvalue chính là các số trên đường chéo. Không cần tính gì. Lý do: định thức của ma trận tam giác = tích đường chéo, nên đa thức đặc trưng đã ở dạng phân tích thành nhân tử sẵn.
Bước 3 — tìm trục.
λ = 3:
A − 3I = [ 0 1 ] hệ: 0·x + 1·y = 0 → y = 0
[ 0 −1 ] 0·x − 1·y = 0 → y = 0 (trùng dòng trên)
x tự do → v₁ = (1, 0)
λ = 2:
A − 2I = [ 1 1 ] hệ: x + y = 0 → y = −x
[ 0 0 ] dòng 2 rỗng — đúng như kỳ vọng
→ v₂ = (1, −1)
Dấu hiệu bạn làm đúng: sau khi trừ
λI, hệ luôn phải thừa — một dòng biến mất hoặc trùng dòng kia. Nếu hệ ra nghiệm duy nhấtv = 0, bạn đã giải saiλ. Đây là cáiasserttự nhiên của bước 3.
Kiểm bằng số:
A·(1, 0) = (3·1 + 1·0, 0·1 + 2·0) = (3, 0) = 3·(1, 0) ✓
A·(1,−1) = (3·1 + 1·(−1), 0 + 2·(−1)) = (2, −2) = 2·(1,−1) ✓
Và một vector không phải eigenvector, để thấy sự khác biệt:
A·(1, 1) = (4, 2) ← (4,2) không phải bội của (1,1). Hướng đã lệch.
numpy.linalg.eig(A) trả về eigenvalue [3, 2] và ma trận eigenvector:
[[ 1. -0.707107]
[ 0. 0.707107]]
Cột 1 là (1, 0) — trùng v₁. Cột 2 là (−0.707107, 0.707107) = −1/√2 · (1, −1) — cùng trục với v₂, chỉ khác dấu và độ dài. Không phải hai đáp án khác nhau, là một đáp án viết hai kiểu.
Chú ý: v₁·v₂ = 1·1 + 0·(−1) = 1 ≠ 0 — hai trục này lệch nhau 45°, không vuông góc. Ghi nhớ, §4 sẽ so.
4. Ví dụ tay số 2 — ma trận đối xứng B = [[2,1],[1,2]]
B = [ 2 1 ]
[ 1 2 ]
Bước 1-2. trace = 4, det = 2·2 − 1·1 = 3:
λ² − 4λ + 3 = 0 → (λ − 3)(λ − 1) = 0 → λ₁ = 3, λ₂ = 1
(numpy.poly(B) = [1, -4, 3] ✓)
Bước 3.
λ = 3:
B − 3I = [ −1 1 ] hệ: −x + y = 0 → y = x
[ 1 −1 ] dòng 2 = −(dòng 1), không thêm thông tin
→ v₁ = (1, 1)
λ = 1:
B − 1I = [ 1 1 ] hệ: x + y = 0 → y = −x
[ 1 1 ] hai dòng trùng hệt nhau
→ v₂ = (1, −1)
Kiểm bằng số:
B·(1, 1) = (2+1, 1+2) = (3, 3) = 3·(1, 1) ✓ giãn gấp 3
B·(1,−1) = (2−1, 1−2) = (1,−1) = 1·(1,−1) ✓ ĐỨNG YÊN
B·(1, 0) = (2, 1) ✗ đổi hướng — vector thường
Điểm khác biệt so với A, và nó không phải trùng hợp:
v₁ · v₂ = 1·1 + 1·(−1) = 0 → hai trục VUÔNG GÓC (90°)
| Ma trận | Đối xứng? | Góc giữa hai trục | |
|---|---|---|---|
A = [[3,1],[0,2]] |
A ≠ Aᵀ |
không | 45° |
B = [[2,1],[1,2]] |
B = Bᵀ |
có | 90° |
Ma trận đối xứng luôn có đủ eigenvalue thực, và các eigenvector luôn trực giao với nhau. Đây là định lý phổ (spectral theorem) — bạn chưa cần chứng minh, nhưng phải nhớ kết luận. Nó là lý do PCA hoạt động đẹp đến vậy: ma trận hiệp phương sai luôn đối xứng, nên các trục chính nó tìm ra luôn vuông góc nhau — tức là các thành phần không chia sẻ thông tin (bài 1).
numpy.linalg.eig(B) trả về [3, 1] và:
[[ 0.707107 -0.707107]
[ 0.707107 0.707107]]
0.707107 = 1/√2 = 0.7071067811865475. Cột 1 = (1,1)/√2, cột 2 = (−1,1)/√2. Vẫn đúng hai trục bạn giải tay, chỉ chuẩn hoá về độ dài 1.
5. BẪY LỚN NHẤT — phép quay R = [[0,−1],[1,0]] không có eigenvector thực
Đây là chỗ bạn phải chạm vào, không được đọc lướt.
R = [ 0 −1 ] ← quay 90° ngược chiều kim đồng hồ (đúng ma trận của bài 2)
[ 1 0 ]
Nghĩ bằng hình trước khi tính. Bạn cầm một mũi tên bất kỳ trên mặt phẳng và xoay nó 90°. Có mũi tên nào sau khi xoay vẫn nằm trên cùng đường thẳng cũ không?
Không. Không một cái nào. Xoay 90° đưa mọi trục sang một trục vuông góc với nó. Không có trục nào bất biến. Chấm hết — đây là câu trả lời, phần đại số chỉ đi xác nhận.
Đại số xác nhận: trace = 0, det = 0·0 − (−1)·1 = 1.
λ² − 0·λ + 1 = 0 → λ² + 1 = 0 → λ² = −1
Biệt thức Δ = 0² − 4·1 = −4 < 0. Không có nghiệm thực. (numpy.poly(R) = [1, 0, 1] ✓)
Kiểm bằng vài vector cụ thể — không cái nào giữ hướng:
R·(1, 0) = ( 0, 1)
R·(1, 1) = (−1, 1)
R·(3, 2) = (−2, 3)
numpy.linalg.eig(R) không báo lỗi — nó trả về [0.+1.j, 0.-1.j], tức ±i.
Đây là bẫy thật sự, và nó là bẫy vận hành chứ không phải bẫy lý thuyết:
numpykhông ném exception. Nó âm thầm đổi kiểu dữ liệu sangcomplex. Nếu bạnassertbằngnp.allclose(...)hay đem so sánh với số thực, mọi thứ vẫn "chạy" và bạn nhận kết quả sai ở tầng trên. Cùng loại lỗi với việc một API trả200 OKkèm body rỗng: không có tín hiệu đỏ, chỉ có dữ liệu hỏng chảy xuôi. Luôn kiểm.imagtrước khi tinnp.linalg.eigtrên ma trận không đối xứng.
Bỏ qua được gì: eigenvalue phức có ý nghĩa hình học đẹp (a ± bi mã hoá quay + co giãn) nhưng không cần cho lộ trình này. Biết "quay → phức → tôi phải xử lý riêng" là đủ. Đừng sa vào.
Bẫy phụ — có đủ eigenvalue vẫn có thể không đủ eigenvector
S = [ 1 1 ] ← phép trượt ngang (shear) của bài 2
[ 0 1 ]
λ² − 2λ + 1 = (λ − 1)² = 0 → λ = 1 nghiệm kép. Eigenvalue thực, đủ số lượng.
Nhưng:
S − 1·I = [ 0 1 ] → y = 0, x tự do → CHỈ MỘT trục: (1, 0)
[ 0 0 ]
numpy.linalg.matrix_rank(S − I) = 1 → null space chỉ có 1 chiều. numpy.linalg.eig(S) trả về ma trận eigenvector [[1, −1], [0, 2.22e−16]] — cột 2 chỉ là −1 lần cột 1 cộng rác làm tròn, tức hai cột song song, không phải hai trục độc lập. Định thức của nó ra 2.22e-16, tức bằng 0 → không nghịch đảo được.
Ý nghĩa: shear giữ nguyên trục hoành và trượt mọi thứ khác — không có trục thứ hai để giữ. Ma trận như vậy gọi là defective, và §6 sẽ cho biết hậu quả: không chéo hoá được.
| Ma trận | Eigenvalue | Số trục độc lập | Chéo hoá được? |
|---|---|---|---|
A = [[3,1],[0,2]] |
3, 2 (thực, phân biệt) | 2 | có |
B = [[2,1],[1,2]] |
3, 1 (thực, phân biệt) | 2 | có (và trực giao) |
R = [[0,−1],[1,0]] |
±i (phức) | 0 (trên số thực) | không |
S = [[1,1],[0,1]] |
1, 1 (kép) | 1 | không |
5b. ⭐ Chéo hoá được và khả nghịch là HAI chuyện ĐỘC LẬP
(bổ sung 19/8 — nguồn: Strang tr.274, §5.2)
Bảng vừa rồi có cột "chéo hoá được". Bài 4 có khái niệm "khả nghịch". Cả hai đều được kiểm bằng
det(A − λI), nên não tự động gộp chúng lại thành một thứ. Chúng không phải một thứ. Strang
viết nguyên văn, hai câu liền nhau:
"Diagonalizability of A depends on enough eigenvectors. Invertibility of A depends on nonzero eigenvalues."
Hai điều kiện, hai đối tượng: một cái đếm số trục, một cái nhìn giá trị trên các trục. Không cái nào suy ra cái nào — và cách chắc chắn nhất để không bao giờ nhầm lại là nhìn đủ bốn ca:
import numpy as np
for ten, M in [("[[0,1],[0,0]]", np.array([[0., 1.], [0., 0.]])),
("[[3,1],[0,3]]", np.array([[3., 1.], [0., 3.]])),
("[[1,0],[0,0]]", np.array([[1., 0.], [0., 0.]])),
("I", np.eye(2))]:
lam, V = np.linalg.eig(M)
r = np.linalg.matrix_rank(V) # số trục ĐỘC LẬP thật sự
print(f"{ten:>14} | λ = {np.round(lam,4)} | rank(V) = {r}/2 | "
f"chéo hoá {r == 2} | khả nghịch {abs(np.linalg.det(M)) > 1e-12}")
# hai ca chéo nhau — đây mới là chỗ đáng assert
S = np.array([[3., 1.], [0., 3.]]) # KHẢ NGHỊCH mà KHÔNG chéo hoá được
assert np.linalg.matrix_rank(np.linalg.eig(S)[1]) == 1
assert abs(np.linalg.det(S)) > 1e-12
Pj = np.array([[1., 0.], [0., 0.]]) # CHÉO HOÁ ĐƯỢC mà KHÔNG khả nghịch
assert np.linalg.matrix_rank(np.linalg.eig(Pj)[1]) == 2
assert abs(np.linalg.det(Pj)) < 1e-12
| Ma trận | trị riêng | hạng(V) | chéo hoá được | khả nghịch |
|---|---|---|---|---|
[[0,1],[0,0]] |
0, 0 | 1/2 | ❌ | ❌ |
[[3,1],[0,3]] |
3, 3 | 1/2 | ❌ | ✅ |
[[1,0],[0,0]] (chiếu) |
1, 0 | 2/2 | ✅ | ❌ |
I |
1, 1 | 2/2 | ✅ | ✅ |
Cả bốn ô của bảng chân trị đều có người ở. Hai dòng giữa là hai phản ví dụ, và mỗi cái giết một trực giác sai:
[[3,1],[0,3]]—det = 9, nghịch đảo được thoải mái,λ = 3khác 0 hoàn toàn. Vẫn chỉ có một trục ((1,0)). Nó làS = [[1,1],[0,1]]của §5 đã nhân 3 — cùng bệnh, khác thang. → "khả nghịch nên chắc chéo hoá được" là sai.[[1,0],[0,0]]— phép chiếu lên trụcx.det = 0, không nghịch đảo được (bài 3: nó bóp cả một chiều về gốc). Nhưng nó đã là ma trận đường chéo,P = I, chéo hoá xong từ đầu. → "suy biến nên chắc không chéo hoá được" cũng sai.
Vì sao khó tự nghĩ ra
Vì chéo hoá chỉ CÓ THỂ hỏng khi có trị riêng lặp — nên bộ não rút gọn thành "lặp là hỏng".
Nhưng I có λ = 1 lặp và chéo hoá hoàn hảo. Điều kiện thật là số trục độc lập, và trị riêng
lặp chỉ mở ra khả năng thiếu trục chứ không đảm bảo. Một quy tắc phải nhớ đúng hai vế:
trị riêng lặp → CÓ THỂ thiếu trục → phải đi ĐẾM, không được đoán
trị riêng khác nhau đôi một → LUÔN đủ trục → chéo hoá được, khỏi đếm
🪤 BẪY: np.linalg.eig luôn trả về một ma trận V có đủ n cột, kể cả khi ma trận
defective — ở §5 bạn đã thấy S cho [[1, −1], [0, 2.22e−16]], hai cột song song. Không có
exception, V.shape hoàn toàn hợp lệ, và V⁻¹ thì ném LinAlgError hoặc tệ hơn là trả về rác
khổng lồ. Kiểm matrix_rank(V), đừng kiểm V.shape — đây đúng loại lỗi "chạy được, số sai
lặng lẽ" mà quy tắc 1 nói tới.
6. Ý TRUNG TÂM — trong cơ sở eigenvector, ma trận là ĐƯỜNG CHÉO
Đây là chỗ bài 6 và bài 7 khớp vào nhau. Nếu bạn chỉ giữ lại một mục của bài này, giữ mục này.
Bài 6 cho công thức đổi cơ sở: nếu P là ma trận có các cột là vector cơ sở mới viết bằng toạ độ cũ, thì cùng một phép biến đổi A được viết trong cơ sở mới là:
A' = P⁻¹ A P
Bài 6 để P tuỳ ý. Bài 7 chọn P cho khéo: lấy chính các eigenvector làm cột.
Với B = [[2,1],[1,2]], hai eigenvector là (1,1) và (1,−1):
P = [ 1 1 ] det(P) = 1·(−1) − 1·1 = −2 ≠ 0 → nghịch đảo được
[ 1 −1 ]
Nghịch đảo 2×2 (1/det nhân với ma trận phụ hợp):
P⁻¹ = (1/−2) · [ −1 −1 ] = [ 0.5 0.5 ]
[ −1 1 ] [ 0.5 −0.5 ]
Nhân ra — làm trên giấy, số rất dễ:
B P = [ 2 1 ] [ 1 1 ] = [ 3 1 ]
[ 1 2 ] [ 1 −1 ] [ 3 −1 ]
P⁻¹ (B P) = [ 0.5 0.5 ] [ 3 1 ] = [ 3 0 ]
[ 0.5 −0.5 ] [ 3 −1 ] [ 0 1 ]
P⁻¹ B P = [ 3 0 ] = diag(3, 1)
[ 0 1 ]
Hai eigenvalue, nằm đúng trên đường chéo, phần còn lại sạch trơn. (Kiểm ngược: P·diag(3,1)·P⁻¹ ra lại đúng [[2,1],[1,2]].)
Làm lại với A = [[3,1],[0,2]], eigenvector (1,0) và (1,−1):
P_A = [ 1 1 ] det = −1
[ 0 −1 ]
P_A⁻¹ A P_A = [ 3 0 ] = diag(3, 2) ✓
[ 0 2 ]
Vì sao ĐƯỜNG CHÉO là dạng đơn giản nhất có thể: ma trận đường chéo không trộn các toạ độ với nhau. Mỗi trục được nhân với đúng một số, độc lập với các trục khác. Nó không còn là một phép biến đổi rối rắm — nó là
nphép nhân số vô hướng chạy song song.
Đây chính là câu trả lời cho "vì sao eigenvector quan trọng":
Eigenvector không phải một đại lượng cần tính cho có. Nó là hệ toạ độ riêng của phép biến đổi — hệ toạ độ mà trong đó phép biến đổi trở nên tầm thường.
Phép biến đổi không hề phức tạp. Cơ sở chuẩn
î, ĵmới là thứ tuỳ tiện.Btrông rối vì bạn đang nhìn nó qua một hệ toạ độ không liên quan gì tới nó. Đổi sang hệ của chính nó — nó làdiag(3,1).
Ẩn dụ đúng: một truy vấn WHERE user_id = ? trên bảng chưa đánh index là full scan O(n); đánh đúng index thì thành O(log n). Dữ liệu không đổi, truy vấn không đổi — chỉ cách tổ chức đổi. Eigenvector là index tự nhiên của một ma trận. Và giống index, không phải bảng nào cũng đánh được (§5: R và S).
Thấy nó chạy trên một vector cụ thể
Lấy x = (5, 1). Toạ độ của nó trong cơ sở eigenvector:
P⁻¹ x = [ 0.5 0.5 ] [ 5 ] = [ 3 ] → x = 3·(1,1) + 2·(1,−1)
[ 0.5 −0.5 ] [ 1 ] [ 2 ] (kiểm: 3(1,1)+2(1,−1) = (5,1) ✓)
Bây giờ áp B. Không cần nhân ma trận — chỉ nhân từng toạ độ với eigenvalue của nó:
toạ độ mới = (3 · 3, 1 · 2) = (9, 2)
→ về toạ độ gốc: 9·(1,1) + 2·(1,−1) = (11, 7)
Đối chiếu trực tiếp: B·(5,1) = (2·5+1·1, 1·5+2·1) = (11, 7) ✓
Cùng kết quả. Một đường đi qua ma trận, một đường chỉ nhân hai số.
6b. ⭐ Khử Gauss — công cụ của cả ba bài đầu — PHÁ trị riêng
(bổ sung 19/8 — nguồn: Strang tr.260, 266 (§5.1), tr.278 (§5.2))
Bài 3 §8 và bài 4 xây gần như mọi thứ trên phép trừ hàng. Nó giải hệ, nó tính hạng, nó tìm null space, nó cho định thức. Sau ba bài như thế, phản xạ tự nhiên khi gặp một ma trận mới là: khử đi rồi tính. Chương 5 của Strang gạt phản xạ đó đi ngay từ trang đầu:
Khử Gauss LÀM THAY ĐỔI trị riêng.
Ucó thể tam giác đẹp đẽ, và §3 vừa dạy bạn rằng trị riêng của ma trận tam giác nằm ngay trên đường chéo — nhưng đường chéo củaUKHÔNG PHẢI trị riêng củaA.
Đây là lỗi kinh điển, và nó hấp dẫn đúng vì §3 đã cho bạn một quy tắc thật (tam giác → đọc đường chéo) mà không nói rõ nó chỉ áp cho chính ma trận đang xét, không áp cho một ma trận khác vừa được biến đổi ra từ nó.
import numpy as np
from scipy.linalg import lu
A = np.array([[1., 2.], [3., 4.]])
w, S = np.linalg.eig(A)
P, L, U = lu(A) # A = P·L·U
print("eig(A) =", np.round(np.sort(w), 6)) # [-0.372281 5.372281]
print("diag(U) =", np.round(np.diag(U), 6)) # [3. 0.666667] ← KHÁC HẲN
print("∏diag(U) =", np.prod(np.diag(U)), # 2.0
" det(A) =", round(np.linalg.det(A), 6), # -2.0
" det(P) =", np.linalg.det(P)) # -1.0
assert not np.allclose(np.sort(np.diag(U)), np.sort(w))
assert np.isclose(np.prod(np.diag(U)) * np.linalg.det(P), np.linalg.det(A))
assert np.allclose(P @ L @ U, A)
Nhìn ba con số cạnh nhau, vì chúng dạy đúng chỗ:
| giá trị | |
|---|---|
trị riêng thật của A |
−0.372281, 5.372281 |
đường chéo của U |
3.0, 0.666667 |
tích đường chéo U |
2.0 |
det(A) |
−2.0 |
Định thức vẫn khớp — 2.0 × det(P) = 2.0 × (−1) = −2.0 ✓. Dấu lệch vì thuật toán đã đổi một
hàng (det(P) = −1). Nên khử Gauss bảo toàn định thức (sai khác dấu) nhưng không bảo toàn
trị riêng. Cùng một phép biến đổi hàng, một đại lượng sống sót, một đại lượng chết. Đó là chỗ
duy nhất cần nhớ trong mục này.
Vì sao phải bỏ LU: nó không sống sót qua phép LUỸ THỪA
Câu hỏi hay hơn "vì sao khử Gauss không dùng được" là: phân tích nào SỐNG SÓT qua Aᵏ?
Strang trả lời trong một câu (tr.278):
"The LU factorization is hopeless when squared, but SΛS⁻¹ is perfect."
A = LU → A² = LU·LU ← U và L KHÔNG triệt tiêu nhau, tắc ở đây
A = SΛS⁻¹ → A² = SΛ(S⁻¹S)ΛS⁻¹ = SΛ²S⁻¹ ← mọi S⁻¹ ăn khớp với S bên cạnh
Toàn bộ chênh lệch nằm ở chỗ S⁻¹ và S là nghịch đảo của nhau nên nuốt nhau, còn U và L
thì không có quan hệ gì. Chỉ vậy thôi.
A8 = S @ np.diag(w ** 8) @ np.linalg.inv(S)
print(np.linalg.matrix_power(A, 8))
# [[165751. 241570.]
# [362355. 528106.]]
print(f"‖SΛ⁸S⁻¹ − A⁸‖max = {np.max(np.abs(A8 - np.linalg.matrix_power(A, 8))):.2e}") # 1.16e-10
assert np.max(np.abs(A8 - np.linalg.matrix_power(A, 8))) < 1e-8
(Lệch 1.16e-10 chứ không phải 1e-16: 5.372281⁸ ≈ 6.9e5, nên sai số làm tròn tương đối 1e-16
bị nhân lên theo. Số lớn thì sai số tuyệt đối lớn — đó là chuyện bình thường, không phải bug.
Bài 6 môn 3 nói kỹ.)
Hai phân tích, hai công việc, không thay thế nhau:
dùng để vì sao A = LU(khử Gauss)GIẢI Ax = brẻ, ổn định, chỉ cần thế vào tiến rồi lùi A = SΛS⁻¹(chéo hoá)LẶP Aᵏxcác S⁻¹Striệt tiêu, chỉ còn nâng luỹ thừa trên đường chéoLU để GIẢI, SΛS⁻¹ để LẶP. Chọn nhầm thì hoặc bạn tính trị riêng cho một việc không cần (đắt vô ích), hoặc bạn khử Gauss rồi đọc nhầm đường chéo (sai kết quả).
Vì sao khó tự nghĩ ra
Hai lý do, cả hai đều là lỗi của thứ tự trình bày chứ không phải của người học:
- Ba bài liền cho thấy khử Gauss giải được mọi thứ, nên không ai nghi ngờ nó ở bài thứ tư.
- Nếu không ai hỏi "phân tích nào sống sót qua phép luỹ thừa", thì eigenvalue chỉ là một định
nghĩa lơ lửng — đẹp, nhưng không rõ dùng làm gì. Đặt câu hỏi đó trước,
SΛS⁻¹trở thành câu trả lời bắt buộc thay vì một chương phải học.
🪤 BẪY: np.linalg.det bên trong chính là khử Gauss (LU) — nên det rẻ, còn eig thì đắt
hơn hẳn. Rất dễ nghĩ "đằng nào cũng khử, chắc lấy luôn trị riêng ra được". Không: det chỉ cần
tích các trụ, mà tích thì bảo toàn; trị riêng cần từng giá trị, mà từng giá trị thì không.
Đây đúng là chuyện ở bài 3 §8b: một đại lượng tổng hợp bảo toàn
không nói gì về các thành phần bên trong.
Nối đi tiếp: bài 8 là Mᵏp — luỹ thừa. Trong
môn 7, RNN cuộn L bước cũng là luỹ thừa, và
môn 3 bài 3 §6 cho thấy chính bán kính
phổ — chứ không phải chuẩn — quyết định gradient nổ hay tắt. Cả ba chỗ đều dùng SΛᵏS⁻¹, và
không chỗ nào dùng được LU.
7. Vì sao dân AI thật sự cần cái này — luỹ thừa và phép lặp
Chéo hoá đẹp thì đẹp, nhưng nó kiếm tiền ở chỗ này: Aᵏ.
B² = P D P⁻¹ · P D P⁻¹ = P D (P⁻¹P) D P⁻¹ = P D² P⁻¹
Bᵏ = P Dᵏ P⁻¹
Các P⁻¹P ở giữa triệt tiêu hết. Mà Dᵏ của ma trận đường chéo chỉ là nâng từng số trên đường chéo lên luỹ thừa k — không có phép nhân ma trận nào cả.
Kiểm bằng số với B, k = 10:
3¹⁰ = 59049, 1¹⁰ = 1
B¹⁰ = P · diag(59049, 1) · P⁻¹ = [ 29525 29524 ]
[ 29524 29525 ]
np.linalg.matrix_power(B, 10) cho đúng ma trận đó. Và cấu trúc lộ ra ngay:
29525 = (3¹⁰ + 1)/2 29524 = (3¹⁰ − 1)/2
Cả dãy đều theo công thức đó:
k |
Bᵏ |
3ᵏ |
|---|---|---|
| 1 | [[2,1],[1,2]] |
3 |
| 2 | [[5,4],[4,5]] |
9 |
| 3 | [[14,13],[13,14]] |
27 |
| 5 | [[122,121],[121,122]] |
243 |
| 10 | [[29525,29524],[29524,29525]] |
59049 |
Big-O: nhân ma trận n×n lặp k lần tốn O(k·n³). Qua chéo hoá, chỉ tốn O(n³) một lần cho việc chéo hoá, rồi O(n) cho Dᵏ. Với k lớn, chênh lệch là toàn bộ vấn đề.
Hệ quả sâu hơn: eigenvalue lớn nhất nuốt tất cả
Áp B lặp lại lên x₀ = (1, 0) — một vector không phải eigenvector:
k |
Bᵏ x₀ |
cos với trục (1,1) |
|---|---|---|
| 1 | (2, 1) |
0.9486832981 |
| 2 | (5, 4) |
0.9938837347 |
| 3 | (14, 13) |
0.9993148338 |
| 5 | (122, 121) |
0.9999915326 |
| 10 | (29525, 29524) |
0.9999999999 |
Vector tự chạy về trục eigenvector có |λ| lớn nhất. Lý do đọc thẳng ra từ §6: viết x₀ = c₁v₁ + c₂v₂, thì
Bᵏ x₀ = c₁·3ᵏ·v₁ + c₂·1ᵏ·v₂
3ᵏ bùng nổ, 1ᵏ đứng im. Thành phần theo v₂ bị nhấn chìm về mặt tỉ lệ. Sau vài chục vòng, hướng của kết quả là hướng của v₁, bất kể xuất phát từ đâu.
Đây là toàn bộ cơ chế của PageRank — và của mọi thuật toán "lặp cho tới khi hội tụ" trên đồ thị.
|λ₁|quyết định hội tụ về đâu. Tỉ số|λ₂|/|λ₁|quyết định hội tụ nhanh cỡ nào.
Xem trước bài 8 bằng số
Ma trận chuyển tiếp của một walk trên 2 nút (mỗi cột cộng lại bằng 1 — phân phối xác suất bảo toàn):
M = [ 0.9 0.5 ] cột 1: 0.9 + 0.1 = 1 ✓
[ 0.1 0.5 ] cột 2: 0.5 + 0.5 = 1 ✓
trace = 1.4, det = 0.9·0.5 − 0.5·0.1 = 0.4:
λ² − 1.4λ + 0.4 = 0 → λ₁ = 1, λ₂ = 0.4
λ₁ bằng đúng 1 — không phải trùng hợp. Mọi ma trận có tổng mỗi cột bằng 1 đều có eigenvalue 1. Eigenvector của nó:
M − 1·I = [ −0.1 0.5 ] → −0.1x + 0.5y = 0 → y = 0.2x
[ 0.1 −0.5 ]
→ v = (1, 0.2), chuẩn hoá cho tổng bằng 1: π = (5/6, 1/6) = (0.833333, 0.166667)
Kiểm: M·π = (0.9·0.833333 + 0.5·0.166667, 0.1·0.833333 + 0.5·0.166667) = (0.833333, 0.166667) = π ✓
Lặp từ p₀ = (1, 0):
| bước | p |
sai lệch so với π |
|---|---|---|
| 1 | (0.9, 0.1) |
0.06666667 |
| 2 | (0.86, 0.14) |
0.02666667 |
| 3 | (0.844, 0.156) |
0.01066667 |
| 4 | (0.8376, 0.1624) |
0.00426667 |
| 6 | (0.834016, 0.165984) |
0.00068267 |
| 10 | (0.833351, 0.166649) |
0.00001748 |
| 12 | (0.833336, 0.166664) |
0.00000280 |
Mỗi bước sai lệch nhân với đúng 0.4 — chính là λ₂. (0.06666667 × 0.4 = 0.02666667 ✓)
Phân phối dừng của một chuỗi Markov = eigenvector ứng với
λ = 1. Nó là vector duy nhất mà "chạy thêm một bước lan truyền nữa" cũng không làm nó đổi. Đó là lý do PageRank hội tụ, vàλ₂là con số cho bạn biết cần bao nhiêu vòng lặp. Bài 8 sẽ làm kỹ trên đồ thị thật, kèm hệ số damping.
Code — tự cài, KHÔNG dùng hàm có sẵn
import numpy as np
def eig2x2(M):
"""Eigenvalue của ma trận 2×2 — giải trực tiếp đa thức đặc trưng
λ² − trace·λ + det = 0. Trả về None nếu KHÔNG có nghiệm thực."""
(a, b), (c, d) = M
trace, det = a + d, a * d - b * c
disc = trace * trace - 4 * det # biệt thức
if disc < 0:
return None # không có eigenvector thực
s = np.sqrt(disc)
return ((trace + s) / 2, (trace - s) / 2)
def eigvec2x2(M, lam, tol=1e-12):
"""Một eigenvector ứng với lam: nghiệm của (M − lam·I) v = 0.
Với ma trận 2×2 hạng 1, dòng [p q] cho ngay nghiệm v = (−q, p)."""
C = M - lam * np.eye(2)
for row in C:
v = np.array([-row[1], row[0]])
if np.linalg.norm(v) > tol:
return v / np.linalg.norm(v)
return np.array([1.0, 0.0]) # C = 0: mọi vector đều là eigenvector
A = np.array([[3., 1.], [0., 2.]])
B = np.array([[2., 1.], [1., 2.]])
R = np.array([[0., -1.], [1., 0.]])
# (a) eigenvalue tự cài khớp numpy
for name, M in [("A", A), ("B", B)]:
mine = sorted(float(x) for x in eig2x2(M))
theirs = sorted(float(x) for x in np.linalg.eigvals(M).real)
print(f"{name}: tự cài {mine} numpy {theirs}")
assert np.allclose(mine, theirs)
# (b) R không có eigenvalue thực — numpy âm thầm trả về số phức
assert eig2x2(R) is None
assert np.all(np.abs(np.linalg.eigvals(R).imag) > 1e-9)
print("R: không có eigenvalue thực, numpy trả về", np.linalg.eigvals(R))
# (c) định nghĩa gốc: A v = λ v
for name, M in [("A", A), ("B", B)]:
for lam in eig2x2(M):
v = eigvec2x2(M, lam)
assert np.allclose(M @ v, lam * v), (name, lam)
print("A v = λ v ✓ với mọi cặp tìm được")
# (d) CHÉO HOÁ: P⁻¹ B P = diag(3, 1)
P = np.array([[1., 1.], [1., -1.]]) # hai eigenvector xếp thành CỘT
D = np.linalg.inv(P) @ B @ P
print("P⁻¹ B P =\n", D)
assert np.allclose(D, np.diag([3., 1.]))
# (e) vì sao dạng chéo đáng giá: luỹ thừa chỉ là luỹ thừa trên đường chéo
B10 = P @ np.diag([3. ** 10, 1. ** 10]) @ np.linalg.inv(P)
assert np.allclose(B10, np.linalg.matrix_power(B, 10))
print("B¹⁰ =\n", B10.astype(int))
# (f) A cũng chéo hoá được, với P khác
PA = np.array([[1., 1.], [0., -1.]])
assert np.allclose(np.linalg.inv(PA) @ A @ PA, np.diag([3., 2.]))
print("P_A⁻¹ A P_A = diag(3, 2) ✓")
print("tất cả khớp numpy ✓")
Output phải ra:
A: tự cài [2.0, 3.0] numpy [2.0, 3.0]
B: tự cài [1.0, 3.0] numpy [1.0, 3.0]
R: không có eigenvalue thực, numpy trả về [0.+1.j 0.-1.j]
A v = λ v ✓ với mọi cặp tìm được
P⁻¹ B P =
[[3. 0.]
[0. 1.]]
B¹⁰ =
[[29525 29524]
[29524 29525]]
P_A⁻¹ A P_A = diag(3, 2) ✓
tất cả khớp numpy ✓
Ba bug bạn sẽ gặp, ghi sẵn vào nhật ký lỗi:
| Triệu chứng | Nguyên nhân gần như chắc chắn |
|---|---|
P⁻¹AP ra đường chéo nhưng sai thứ tự (diag(1,3)) |
Thứ tự cột của P phải khớp thứ tự eigenvalue của D. Đảo cột P thì đảo luôn D. |
assert M @ v == lam * v lệch ~1e-8 chứ không ~1e-16 |
Bạn đang so bằng == hoặc tol quá chặt. Dùng np.allclose. |
np.linalg.eigvals(...).real cho số "hợp lý" nhưng kết quả tầng trên sai |
Ma trận có eigenvalue phức và bạn đã vứt phần ảo đi. Kiểm .imag trước, đừng gọi .real phản xạ. |
Bài mở rộng (không bắt buộc, 20 phút): cài power_iteration(M, x0, k) — chỉ nhân M @ x rồi chuẩn hoá, lặp k lần — và kiểm nó hội tụ về (1,1)/√2 với B. Đối chiếu với bảng ở §7. Đó là phiên bản 5 dòng của thuật toán mà Google chạy trên vài tỉ nút.
Nối vào AI
| Khái niệm bài này | Chỗ dùng trong AI |
|---|---|
Eigenvector ứng với λ = 1 của ma trận chuyển tiếp |
PageRank của Google và Personalized PageRank: điểm xếp hạng chính là eigenvector chính. Toàn bộ web xếp hạng bằng một phép nhân ma trận lặp — bài 8 |
Tỉ số |λ₂|/|λ₁| |
Quyết định số vòng lặp cần để hội tụ. Đây là con số bạn nhìn khi tuning một thuật toán lan truyền trên đồ thị |
| Eigenvector của ma trận hiệp phương sai | PCA: các trục chính = eigenvector; phương sai giữ lại trên mỗi trục = eigenvalue. Vì hiệp phương sai đối xứng (§4), các trục luôn trực giao — bài 9 §9 dựng PCA bằng SVD, môn 6 đào sâu |
Aᵏ = P Dᵏ P⁻¹ |
Xếp nhiều lớp cùng một phép biến đổi. Trong Transformer sâu, phổ eigenvalue của các ma trận trộn token là thứ giải thích hiện tượng biểu diễn các token bị kéo về cùng một hướng khi tăng độ sâu |
|λ_max| của ma trận trọng số |
> 1 → gradient bùng nổ; < 1 → gradient tiêu biến. Đây là lý do gốc của việc phải chuẩn hoá/clip gradient trong mạng hồi quy |
Chéo hoá A = P D P⁻¹ |
Trường hợp riêng (ma trận vuông) của SVD A = UΣVᵀ — nền của low-rank, nén mô hình, và LoRA. SVD làm được cả với ma trận không vuông, thứ mà chéo hoá không làm được |
Điểm nối cần thấy rõ: bài 1 dạy tích vô hướng đo góc. Bài 2 dạy ma trận biến đổi không gian. Bài 6 dạy đổi cơ sở. Bài 7 nói: có một cơ sở đặc biệt trong đó phép biến đổi chỉ còn là phép nhân số — và §4 nói thêm rằng khi ma trận đối xứng, cơ sở đó còn trực giao theo đúng nghĩa "vuông góc" của bài 1. Bốn bài chỉ là bốn góc nhìn vào cùng một vật.
Tự kiểm — trả lời được mới sang bài 8
-
Viết lại
A v = λ vthành(A − λI) v = 0. Vì sao bắt buộc phải chènI, và vì sao yêu cầuv ≠ 0lại dẫn thẳng tớidet(A − λI) = 0? Trả lời bằng hình học (bóp dẹp / null space), không bằng công thức. -
Với
A = [[3,1],[0,2]]bạn đọc ra eigenvalue3, 2chỉ bằng cách nhìn. Quy tắc đó áp dụng cho lớp ma trận nào, và vì sao nó đúng? -
R = [[0,−1],[1,0]]không có eigenvector thực. Giải thích bằng hình trong một câu, rồi chỉ ra chỗ nào trong đa thức đặc trưng phản ánh đúng câu đó. -
S = [[1,1],[0,1]]có eigenvalue thực (1, kép) nhưng vẫn không chéo hoá được. Vậy điều kiện thật sự để chéo hoá được là gì — về eigenvalue hay về số trục độc lập? -
Nếu
vlà eigenvector vớiλ = 3thì−7vlà gì? Câu trả lời nói lên điều gì về việc "eigenvector" thực chất là một vector hay một tập hợp? -
B = [[2,1],[1,2]]có eigenvector trực giao,A = [[3,1],[0,2]]thì không (45°). Tính chất nào củaBgây ra khác biệt đó? Nêu tên định lý, và nối nó với việc PCA cho ra các thành phần không tương quan. -
Chứng minh
Bᵏ = P Dᵏ P⁻¹trong hai dòng. Rồi dùng nó giải thích vì sao lặpBlên một vector bất kỳ thì vector đó tự chạy về trục có|λ|lớn nhất. -
Ma trận
M = [[0.9,0.5],[0.1,0.5]]cóλ₁ = 1,λ₂ = 0.4. Sai lệch giảm 0.4 lần mỗi bước. Nếu một ma trận khác cóλ₂ = 0.95, cần nhiều hơn hay ít hơn bao nhiêu vòng lặp để đạt cùng độ chính xác? (Ước lượng bằng logarit, không cần chạy code.)
Câu 4 và câu 7 là hai câu đáng nhất. Câu 4 là chỗ tách người biết công thức khỏi người hiểu điều kiện: đủ eigenvalue không đồng nghĩa với đủ eigenvector. Câu 7 là cơ chế trần trụi của PageRank, PPR, power iteration, và mọi thứ bài 8 sẽ dựng lên. Nếu chỉ trả lời được một câu, chọn câu 7.
Bài tập về nhà trước bài 8
3Blue1Brown — Essence of Linear Algebra:
| Tập | Vì sao |
|---|---|
| 14 — Eigenvectors and eigenvalues | 🔴🔴 Đây là bài này, bằng hình động. Xem sau khi đã tự giải tay §3-§4 — bạn sẽ nhận ra từng ý. Đoạn về det(A − λI) = 0 và đoạn phép quay không có eigenvector thực là hai đoạn đáng tua lại |
| 15 — A quick trick for computing eigenvalues | 🟡 Tuỳ chọn. Mẹo 2×2: m = trace/2, p = det, rồi λ = m ± √(m² − p) |
Tự kiểm mẹo tập 15 trên đúng bốn ma trận của bài này (làm nhẩm, không cần code):
| Ma trận | m = trace/2 |
p = det |
m² − p |
λ = m ± √(m²−p) |
|---|---|---|---|---|
A |
2.5 | 6 | 0.25 | 2.5 ± 0.5 → 3, 2 ✓ |
B |
2 | 3 | 1 | 2 ± 1 → 3, 1 ✓ |
R |
0 | 1 | −1 | căn số âm → không có nghiệm thực ✓ |
M |
0.7 | 0.4 | 0.09 | 0.7 ± 0.3 → 1, 0.4 ✓ |
Dấu của m² − p chính là biệt thức — nó báo trước ma trận có eigenvector thực hay không, trước cả khi bạn giải.
Việc tay bắt buộc — làm trên giấy, không mở máy:
- Chéo hoá
C = [[4,1],[2,3]].trace = 7,det = 10→ tự tìmλ, tự tìmP, tự nhân raP⁻¹CP. Chỉ mởnumpyđể chấm bài, không phải để làm bài. - Chứng minh: nếu
λlà eigenvalue củaAthìλᵏlà eigenvalue củaAᵏ, với cùng eigenvector. Ba dòng. - Ma trận
[[2,0],[0,2]]có eigenvalue gì, và eigenvector là những vector nào? (Đáp án làm nhiều người bất ngờ — và nó nối thẳng vào câu 5 phần Tự kiểm.)
Bài 8 sẽ là: ma trận chuyển tiếp, phân phối dừng và PageRank/PPR — dựng thẳng lên λ = 1 và bảng hội tụ ở §7, lần này trên đồ thị thật thay vì ma trận 2×2.