跳轉到

6.3 特徵值與對角化:找到變換的「天然座標軸」#

本節目標#

  • 理解特徵值與特徵向量的定義與幾何意義(伸縮方向)
  • 學會用 eigenvals()、eigenvects()、charpoly() 做特徵分析
  • 學會用 diagonalize() 對角化矩陣,並用 \(A = PDP^{-1}\) 快速計算 \(A^n\)
  • 實驗:畫出矩陣對單位圓的變換,親眼看到特徵方向

特徵值與特徵向量:不轉彎的方向#

一個線性變換 \(A\) 作用在向量上,通常會同時旋轉和伸縮。但總有某些特別的方向, \(A\) 作用上去只伸縮、不轉彎:

\[ A\mathbf{v} = \lambda \mathbf{v} \]

滿足此式的非零向量 \(\mathbf{v}\) 稱為特徵向量(eigenvector),對應的倍率 \(\lambda\) 稱為特徵值(eigenvalue)。

幾何意義: \(\mathbf{v}\) 指出「變換下的不動方向」, \(\lambda\) 說明該方向被拉長( \(|\lambda| > 1\) )、壓縮( \(|\lambda| < 1\) )或翻轉( \(\lambda < 0\) )。例如 \(\lambda = 2\) 表示沿這個方向一切長度加倍。

如何求特徵值?把定義改寫成 \((A - \lambda I)\mathbf{v} = \mathbf{0}\) 。要存在非零解 \(\mathbf{v}\) ,係數矩陣必須不可逆(否則只有零解),也就是:

\[ \det(A - \lambda I) = 0 \]

這條以 \(\lambda\) 為未知數的方程稱為特徵多項式。

用 eigenvals() 求特徵值#

from sympy import Matrix

A = Matrix([[4, 1],
            [2, 3]])

print(A.eigenvals())
# {5: 1, 2: 1}

輸出是字典:鍵是特徵值,值是它的代數重數(重根次數)。這裡特徵值是 \(\lambda = 5\) 和 \(\lambda = 2\) 。

檢驗一下定義 \(A\mathbf{v} = \lambda \mathbf{v}\) :

v = Matrix([1, 1])   # 待會會看到它是 λ=5 的特徵向量
print(A * v)         # Matrix([[5], [5]]) ← 正好是 5*v!

用 eigenvects() 一次拿到全部#

eigenvects() 回傳特徵值、重數與對應特徵向量空間的一組基底:

from sympy import Matrix

A = Matrix([[4, 1],
            [2, 3]])

for val, mult, vecs in A.eigenvects():
    print(f"特徵值 {val}(重數 {mult}),特徵向量:{vecs}")
# 特徵值 5(重數 1),特徵向量:[Matrix([[1/2], [1]])]
# 特徵值 2(重數 1),特徵向量:[Matrix([[-1], [1]])]

驗證第二組: \(A \begin{pmatrix} -1 \\ 1 \end{pmatrix} = \begin{pmatrix} -2 \\ 2 \end{pmatrix} = 2 \begin{pmatrix} -1 \\ 1 \end{pmatrix}\) ,確實是 \(\lambda = 2\) 。

特徵多項式 charpoly#

特徵多項式可以用 charpoly() 取得:

from sympy import Matrix, Symbol

lam = Symbol('lambda')
A = Matrix([[4, 1],
            [2, 3]])

cp = A.charpoly(lam)
print(cp.as_expr())     # lambda**2 - 7*lambda + 10
print(cp.as_expr().factor())   # (lambda - 5)*(lambda - 2)

手算核對: \(\det(A - \lambda I) = (4-\lambda)(3-\lambda) - 2 = \lambda^2 - 7\lambda + 10\) 。特徵多項式的根就是特徵值。兩個根的和等於矩陣的蹤(trace,對角線和: \(4+3=7\) ),積等於行列式( \(10\) ),這兩個小結論常常能心算驗證答案。

對角化 diagonalize()#

如果 \(n \times n\) 矩陣有 \(n\) 個線性獨立的特徵向量,就能寫成

\[ A = P D P^{-1} \]

其中 \(D\) 是對角矩陣(對角線放特徵值), \(P\) 的行是對應的特徵向量。diagonalize() 一次回傳 \(P\) 和 \(D\) :

from sympy import Matrix

A = Matrix([[4, 1],
            [2, 3]])

P, D = A.diagonalize()
print(P)   # Matrix([[1/2, -1], [1, 1]]):行是特徵向量
print(D)   # Matrix([[5, 0], [0, 2]]):對角線是特徵值

# 驗證 A = P*D*P^{-1}
print(P * D * P.inv() == A)   # True
print(D == P.inv() * A * P)   # True

為什麼要對角化?因為對角矩陣的次方極好算——只要把對角線元素各自乘方。於是:

\[ A^n = (P D P^{-1})(P D P^{-1}) \cdots = P D^n P^{-1} \]
from sympy import Matrix

A = Matrix([[4, 1],
            [2, 3]])
P, D = A.diagonalize()

n = 10
print(A**10)              # 直接算
print(P * D**10 * P.inv())   # 對角化算,結果相同

不是所有矩陣都能對角化(例如 \(A = \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix}\) 特徵值 1 重根,但特徵向量只有一個方向)。可以先用 is_diagonalizable() 檢查:

print(A.is_diagonalizable())                 # True
print(Matrix([[1, 1], [0, 1]]).is_diagonalizable())  # False

應用預告:Fibonacci 數列(7.3 節)#

Fibonacci 數列 \(F_{n+1} = F_n + F_{n-1}\) 可以寫成矩陣遞迴:

\[ \begin{pmatrix} F_{n+1} \\ F_n \end{pmatrix} = \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} F_n \\ F_{n-1} \end{pmatrix} \]

對這個矩陣做特徵分析,就能得到 Fibonacci 數列的封閉公式(Binet 公式):

from sympy import Matrix

F = Matrix([[1, 1],
            [1, 0]])
print(F.eigenvals())   # {(1 + sqrt(5))/2: 1, (1 - sqrt(5))/2: 1}

特徵值正是黃金比例 \(\varphi = \frac{1+\sqrt{5}}{2}\) 與其共軛!詳細推導與 \(A^n\) 的應用留待 7.3 節。

動手實驗:畫出矩陣對單位圓的變換#

線性變換把單位圓變成橢圓。橢圓的主軸方向正是特徵向量方向,主軸半長正是特徵值的絕對值。讓我們畫圖驗證:

import matplotlib.pyplot as plt
import numpy as np
from sympy import Matrix

A = Matrix([[3, 1],
            [1, 3]])

# 單位圓上的點
theta = np.linspace(0, 2 * np.pi, 200)
circle = np.vstack([np.cos(theta), np.sin(theta)])   # 2 x 200

# 作用變換
Anp = np.array(A, dtype=float)
ellipse = Anp @ circle

# 特徵向量(畫成直線)
for val, mult, vecs in A.eigenvects():
    v = np.array(vecs[0], dtype=float).flatten()
    v = v / np.linalg.norm(v)
    plt.plot([-5 * v[0], 5 * v[0]], [-5 * v[1], 5 * v[1]],
             '--', label=f"特徵方向 λ={val}")

plt.plot(circle[0], circle[1], 'gray', label='單位圓')
plt.plot(ellipse[0], ellipse[1], 'b', label='變換後的橢圓')
plt.axis('equal')
plt.legend()
plt.title("矩陣 $A = [[3,1],[1,3]]$ 對單位圓的變換")
plt.show()

執行後你會看到:圓被拉成一個橢圓,而虛線畫的兩條特徵方向恰好是橢圓的長短軸。特徵值 \(\lambda = 4, 2\) 分別對應長軸半長 \(4\) 、短軸半長 \(2\) 。

換一個會「翻轉」的矩陣玩玩:

B = Matrix([[0, 1],
            [1, 0]])   # 沿直線 y=x 鏡射
print(B.eigenvals())   # {1: 1, -1: 1}

\(\lambda = 1\) 的特徵向量在鏡射軸上(不變), \(\lambda = -1\) 的特徵向量垂直於鏡射軸(方向反轉)——負特徵值就代表「翻轉」。

小結與練習#

本節重點:

  • 特徵向量是變換下「不轉彎」的方向,特徵值是該方向的伸縮倍率
  • 特徵值來自特徵多項式 \(\det(A - \lambda I) = 0\) ;eigenvals()、eigenvects()、charpoly() 是三件套
  • 對角化 \(A = PDP^{-1}\) 把 \(A^n\) 變成 \(PD^nP^{-1}\) ;diagonalize() 回傳 \(P, D\)
  • 不是所有矩陣都可對角化(特徵向量不夠時不行)
  • Fibonacci 矩陣的特徵值是黃金比例——伏筆在 7.3 節

練習:

  1. 求 \(A = \begin{pmatrix} 0 & -1 \\ 1 & 0 \end{pmatrix}\) (旋轉 \(90°\) )的特徵值,並解釋為什麼實數特徵值不存在(提示:旋轉不改變長度,哪來的伸縮方向?)。
  2. 對 \(A = \begin{pmatrix} 2 & 0 \\ 0 & 5 \end{pmatrix}\) 求特徵值與特徵向量,你能不經計算就說出答案嗎?(提示:對角矩陣的特徵結構一目了然)
  3. 用 diagonalize() 求 \(A = \begin{pmatrix} 4 & 1 \\ 2 & 3 \end{pmatrix}\) 的 \(A^5\) ,並與 A**5 對照。
  4. 驗證:特徵值總和等於 A.trace(),特徵值乘積等於 A.det()(任取一個 \(3 \times 3\) 矩陣)。
  5. 修改實驗程式,用 \(A = \begin{pmatrix} 1 & 2 \\ 0 & 1 \end{pmatrix}\) (剪切變換)畫單位圓的變換。觀察:它可對角化嗎?橢圓的主軸和特徵向量還重合嗎?