跳轉到

5.2 特徵值計算#

本節目標#

  • 完整手算一個 \(2 \times 2\) 矩陣的特徵值與特徵向量
  • 學會兩個速算技巧: \(\lambda_1 + \lambda_2 = \mathrm{tr}(A)\) 、 \(\lambda_1 \lambda_2 = \det(A)\)
  • 熟練使用 np.linalg.eig,並理解它的輸出格式(含單位化的特徵向量)
  • 認識重根與「特徵向量不夠用」的情況(為對角化鋪路)
  • 學會解讀複數特徵值背後的幾何訊息

2×2 手算完整流程#

以 \(A = \begin{bmatrix} 4 & 1 \\ 2 & 3 \end{bmatrix}\) 為例,走一遍標準流程。

第 1 步:寫出特徵方程

\[ \det(A - \lambda I) = \det \begin{bmatrix} 4-\lambda & 1 \\ 2 & 3-\lambda \end{bmatrix} = (4-\lambda)(3-\lambda) - 2 = \lambda^2 - 7\lambda + 10 = 0 \]

第 2 步:因式分解求根

\[ (\lambda - 5)(\lambda - 2) = 0 \quad\Longrightarrow\quad \lambda_1 = 5,\ \lambda_2 = 2 \]

**第 3 步:把每個 \(\lambda\) 代回 \((A - \lambda I)\mathbf{v} = \mathbf{0}\) **

\(\lambda = 5\) 時:

\[ \begin{bmatrix} -1 & 1 \\ 2 & -2 \end{bmatrix} \begin{bmatrix} v_1 \\ v_2 \end{bmatrix} = \mathbf{0} \quad\Longrightarrow\quad v_1 = v_2 \quad\Longrightarrow\quad \mathbf{v}_1 = \begin{bmatrix} 1 \\ 1 \end{bmatrix} \]

\(\lambda = 2\) 時:

\[ \begin{bmatrix} 2 & 1 \\ 2 & 1 \end{bmatrix} \begin{bmatrix} v_1 \\ v_2 \end{bmatrix} = \mathbf{0} \quad\Longrightarrow\quad 2v_1 + v_2 = 0 \quad\Longrightarrow\quad \mathbf{v}_2 = \begin{bmatrix} 1 \\ -2 \end{bmatrix} \]

注意特徵向量只定方向、不定長度: \(\begin{bmatrix} 1 \\ 1 \end{bmatrix}\) 、 \(\begin{bmatrix} 100 \\ 100 \end{bmatrix}\) 都是 \(\lambda = 5\) 的特徵向量。 習慣上我們把它單位化(除以自己的長度),方便比較與計算:

import numpy as np

A = np.array([[4.0, 1.0],
              [2.0, 3.0]])
vals, vecs = np.linalg.eig(A)
print("特徵值:", vals)            # [5. 2.]
print("特徵向量(單位化):")
print(vecs)                       # 每一行是 1/sqrt(2) 之類
# 驗證 A v = lambda v
for i in range(2):
    v = vecs[:, i]
    print(A @ v, vals[i] * v)     # 兩者應幾乎相同

速算技巧:跡與行列式#

把特徵多項式展開成一般式( \(2 \times 2\) 情形):

\[ \lambda^2 - (\text{對角和})\,\lambda + \det(A) = 0 \]

由根與係數關係立刻得到兩個好用一輩子的公式:

\[ \lambda_1 + \lambda_2 = \mathrm{tr}(A) = a + d, \qquad \lambda_1 \lambda_2 = \det(A) \]

驗證剛剛的例子: \(\mathrm{tr}(A) = 4 + 3 = 7 = 5 + 2\) ✓, \(\det(A) = 12 - 2 = 10 = 5 \times 2\) ✓。

速算的實戰用法——用檢驗代替死算:

def check_eigen_2x2(A, lam1, lam2):
    """用跡與行列式快速驗證兩個候選特徵值"""
    tr = A[0, 0] + A[1, 1]
    det = np.linalg.det(A)
    ok1 = np.isclose(lam1 + lam2, tr) and np.isclose(lam1 * lam2, det)
    return ok1

A = np.array([[4.0, 1.0], [2.0, 3.0]])
print(check_eigen_2x2(A, 5, 2))   # True
print(check_eigen_2x2(A, 5, 3))   # False(和對了,積錯)

另一個妙用:不用解二次方程就能判斷特徵值的性質 (判別式 \(\Delta = \mathrm{tr}^2 - 4\det\) ):

判別式 特徵值性質 幾何
\(\Delta > 0\) 兩個相異實根 兩個不動方向
\(\Delta = 0\) 重根 可能只有一個方向(見下節)
\(\Delta < 0\) 共軛複根 旋轉成分,無實不動方向

np.linalg.eig 的完整用法#

import numpy as np

A = np.array([[4.0, 1.0],
              [2.0, 3.0]])

eigvals, eigvecs = np.linalg.eig(A)

必須記住的三件事:

  1. eigvals:形狀 (n,) 的一維陣列,可能含複數(dtype 會自動變 complex)
  2. eigvecs:形狀 (n, n),第 i 行(column)是對應 eigvals[i] 的單位特徵向量 ——是「第 i 行」不是「第 i 列」,初學者最常搞錯
  3. 特徵向量已自動單位化(長度為 1),且 NumPy 只保證方向正確, 正負號與順序可能與手算不同——這完全合法,因為特徵向量本來就能差一個常數倍
print(np.linalg.norm(eigvecs[:, 0]))          # 1.0(已單位化)
print(A @ eigvecs[:, 0] - eigvals[0] * eigvecs[:, 0])  # ~ 0

只要特徵值、不要特徵向量時用較省記憶的:

np.linalg.eigvals(A)   # array([5., 2.])

重根與「特徵向量不夠用」的陷阱#

看剪切矩陣 \(S = \begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix}\) :

\[ \det(S - \lambda I) = (1-\lambda)^2 = 0 \quad\Longrightarrow\quad \lambda = 1 \ (\text{重根}) \]

代回 \((S - I)\mathbf{v} = \mathbf{0}\) :

\[ \begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix} \mathbf{v} = \mathbf{0} \quad\Longrightarrow\quad v_2 = 0 \quad\Longrightarrow\quad \text{只有 } \begin{bmatrix} 1 \\ 0 \end{bmatrix} \text{ 一個方向} \]

兩次的特徵值,卻只解出一條特徵直線。 \(2 \times 2\) 的矩陣,我們希望有兩個獨立特徵向量把平面撐起來, 這裡卻缺了一個——這種矩陣不可對角化(5.3 節詳談, 需要 Jordan 標準型才能完全描述它)。

S = np.array([[1.0, 1.0], [0.0, 1.0]])
vals, vecs = np.linalg.eig(S)
print(vals)                 # [1. 1.]
print(vecs)                 # 兩行幾乎平行!獨立特徵向量只有一個
print(np.linalg.matrix_rank(vecs))   # 1 < 2,不夠用

判斷準則(先記結論,5.3 節證明): \(\lambda\) 的重數(在特徵多項式中出現的次數) 等於對應獨立特徵向量個數時,矩陣才可對角化。 實對稱矩陣永遠滿足這條件,所以永遠可對角化。

複數特徵值的觀察與解讀#

theta = np.pi / 6
R = np.array([[np.cos(theta), -np.sin(theta)],
              [np.sin(theta),  np.cos(theta)]])

vals, vecs = np.linalg.eig(R)
print(vals)
# [0.8660254+0.5j 0.8660254-0.5j]  —— 共軛成對出現
print(np.abs(vals))          # [1. 1.]  模長 = 1
print(np.angle(vals))        # [ 0.5236 -0.5236] = ±30°

複數特徵值永遠共軛成對( \(a \pm bi\) ),而且藏著兩個幾何訊息:

  • 模長 \(|\lambda| = \sqrt{a^2 + b^2}\) :這個「旋轉」附帶的伸縮倍數。 旋轉矩陣的 \(|\lambda| = 1\) ,正好對應「旋轉不改變長度」
  • 輻角 \(\arg(\lambda)\) :就是旋轉角 \(\theta\) 本身! \(2 \times 2\) 實矩陣若特徵值為 \(a \pm bi\) ,它在幾何上等價於 「伸縮 \(|\lambda|\) 倍 + 旋轉 \(\arg\lambda\) 」(這正是複數乘法的幾何意義)
# 一般化的「伸縮 + 旋轉」矩陣
r, theta = 1.5, np.pi / 3
B = r * np.array([[np.cos(theta), -np.sin(theta)],
                  [np.sin(theta),  np.cos(theta)]])
vals = np.linalg.eigvals(B)
print(np.abs(vals), np.angle(vals))   # [1.5 1.5] [1.047 -1.047],完全吻合

這也解釋了為什麼需要複數:不是數學家故意刁難, 而是「旋轉」這個幾何動作本來就住在複數世界裡。

小結與練習#

本節重點#

  1. 手算流程: \(\det(A - \lambda I) = 0\) 求特徵值 → 代回解 \((A - \lambda I)\mathbf{v} = \mathbf{0}\) 求特徵向量
  2. 速算: \(\lambda_1 + \lambda_2 = \mathrm{tr}(A)\) 、 \(\lambda_1\lambda_2 = \det(A)\) ; 判別式 \(\mathrm{tr}^2 - 4\det\) 判斷實根或複根
  3. np.linalg.eig 回傳 (eigvals, eigvecs),特徵向量按行存放、已單位化
  4. 重根可能伴隨特徵向量不足(剪切矩陣),此矩陣不可對角化
  5. 複數特徵值共軛成對:模長 = 伸縮倍數、輻角 = 旋轉角

練習#

  1. 手算 \(A = \begin{bmatrix} 1 & 2 \\ 2 & 4 \end{bmatrix}\) 的特徵值 (提示: \(\det(A) = 0\) ,所以有一個 \(\lambda = 0\) ),並解出兩個特徵向量。
  2. 用跡與行列式速算驗證練習 1 的答案,再用 np.linalg.eig 對照。
  3. 對 \(B = \begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix}\) ( \(90°\) 旋轉) 計算特徵值,解讀模長與輻角。
  4. 寫一個函式 is_diagonalizable_2x2(A):用 np.linalg.eig 判斷 獨立特徵向量是否為 2 個(提示:np.linalg.matrix_rank(vecs) == 2), 分別用對角矩陣與剪切矩陣測試。
  5. 挑戰:證明實矩陣的複數特徵值必共軛成對 (提示:對 \(\det(A - \lambda I) = 0\) 取共軛,利用 \(A\) 是實矩陣)。