固有値と固有ベクトルは線形代数の中心的な概念であり、主成分分析(PCA)、振動解析、量子力学など幅広い分野で応用される。その意味を理解し、Python での計算方法を学ぼう。
固有値・固有ベクトルとは
正方行列 に対して、次の関係を満たすスカラー とベクトル ()を考える。
このとき を固有値(eigenvalue)、 を固有ベクトル(eigenvector)と呼ぶ。
直感的には、行列 による変換で方向が変わらず、長さだけが 倍になるベクトルが固有ベクトルだ。
幾何学的な意味
2×2 行列で固有値の意味を視覚化してみよう。
import numpy as np A = np.array([[2, 1], [1, 2]]) # 固有値と固有ベクトル eigenvalues, eigenvectors = np.linalg.eig(A) print(f'固有値: {eigenvalues}') # [3. 1.] print(f'固有ベクトル:\n{eigenvectors}')
この行列 は固有値 と を持つ。固有値 に対応する方向は 3 倍に伸び、固有値 に対応する方向は長さが変わらない。
その方向に伸びる
その方向に縮む
固有方程式
固有値は特性方程式(固有方程式)から求まる。
2×2 行列の場合、これは二次方程式になる。
# A = [[a, b], [c, d]] の固有値を求める公式 # λ² - (a+d)λ + (ad - bc) = 0 def eigenvalues_2x2(A): """2×2 行列の固有値を求める""" a, b = A[0] c, d = A[1] trace = a + d # トレース(対角成分の和) det = a * d - b * c # 行列式 # 二次方程式の解の公式 discriminant = trace**2 - 4 * det if discriminant < 0: # 複素固有値 real = trace / 2 imag = (-discriminant)**0.5 / 2 return (complex(real, imag), complex(real, -imag)) else: sqrt_d = discriminant**0.5 return ((trace + sqrt_d) / 2, (trace - sqrt_d) / 2) A = [[2, 1], [1, 2]] print(eigenvalues_2x2(A)) # (3.0, 1.0)
べき乗法(Power Method)
一般の行列に対して最大固有値を求める反復法がべき乗法だ。任意のベクトルに行列を繰り返し掛けると、最大固有値に対応する固有ベクトルの方向に収束する。
def power_method(A, max_iter=100, tol=1e-10): """べき乗法で最大固有値と固有ベクトルを求める""" n = len(A) # 初期ベクトル(ランダムでもよい) v = [1.0] * n for _ in range(max_iter): # A @ v を計算 Av = [sum(A[i][j] * v[j] for j in range(n)) for i in range(n)] # 最大成分で正規化 norm = max(abs(x) for x in Av) v_new = [x / norm for x in Av] # 収束判定 if all(abs(v_new[i] - v[i]) < tol for i in range(n)): break v = v_new # 固有値はレイリー商で計算 Av = [sum(A[i][j] * v[j] for j in range(n)) for i in range(n)] eigenvalue = sum(Av[i] * v[i] for i in range(n)) / sum(v[i]**2 for i in range(n)) return eigenvalue, v A = [[2, 1], [1, 2]] eigenvalue, eigenvector = power_method(A) print(f'最大固有値: {eigenvalue:.6f}') # 3.000000 print(f'固有ベクトル: {eigenvector}')
べき乗法は最大固有値しか求められないが、シンプルで大規模疎行列にも適用できる。
NumPy による計算
実務では NumPy の linalg.eig を使う。
import numpy as np A = np.array([[4, -2], [1, 1]]) eigenvalues, eigenvectors = np.linalg.eig(A) print(f'固有値: {eigenvalues}') # [3. 2.] print(f'固有ベクトル:\n{eigenvectors}')
固有ベクトルは列ベクトルとして返される。eigenvectors[:, i] が eigenvalues[i] に対応する固有ベクトルだ。
import numpy as np A = np.array([[4, -2], [1, 1]]) eigenvalues, eigenvectors = np.linalg.eig(A) # 検証: A @ v = λ * v for i in range(len(eigenvalues)): lam = eigenvalues[i] v = eigenvectors[:, i] Av = A @ v lam_v = lam * v print(f'λ={lam}: A@v = {Av}, λ*v = {lam_v}')
対称行列の固有値
対称行列()は特別な性質を持つ。固有値はすべて実数で、固有ベクトルは互いに直交する。
import numpy as np # 対称行列 A = np.array([[4, 2, 1], [2, 5, 3], [1, 3, 6]]) eigenvalues, eigenvectors = np.linalg.eigh(A) # eigh は対称行列専用 print(f'固有値: {eigenvalues}') # すべて実数 # 固有ベクトルの直交性を確認 print(f'直交性: {eigenvectors.T @ eigenvectors}') # 単位行列に近い
linalg.eigh は対称行列専用の関数で、linalg.eig より高速で数値的に安定だ。
応用例:主成分分析(PCA)
PCA はデータの共分散行列の固有値問題を解くことに帰着される。
import numpy as np # サンプルデータ(3 次元、5 サンプル) X = np.array([[2.5, 2.4, 1.0], [0.5, 0.7, 0.3], [2.2, 2.9, 1.5], [1.9, 2.2, 1.2], [3.1, 3.0, 1.8]]) # 中心化 X_centered = X - X.mean(axis=0) # 共分散行列 cov_matrix = np.cov(X_centered.T) # 固有値分解 eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix) # 固有値を降順にソート idx = np.argsort(eigenvalues)[::-1] eigenvalues = eigenvalues[idx] eigenvectors = eigenvectors[:, idx] print(f'固有値(寄与度): {eigenvalues}') print(f'第 1 主成分: {eigenvectors[:, 0]}')
固有値の大きい順に主成分を取ることで、次元削減が行える。
固有値の応用分野
PCA、特異値分解(SVD)、スペクトラルクラスタリング
振動解析、量子力学のシュレディンガー方程式
PageRank アルゴリズム、グラフのラプラシアン
システムの安定性解析
固有値・固有ベクトルは抽象的に見えるが、「行列が何をしているか」を本質的に理解するための鍵だ。線形代数を深く学ぶほど、その重要性が実感できるだろう。