余因子展開(cofactor expansion)は、行列式を計算するための基本的な方法の1つです。大きな行列式をより小さな行列式に分解して再帰的に計算するこの手法は、行列式の理論的な性質を理解する上で非常に重要です。
また、余因子は逆行列の公式やクラメルの公式にも登場するため、線形代数を学ぶ上で避けて通れない概念です。
本記事の内容
- 小行列式と余因子の定義
- 余因子展開の公式と証明のアイデア
- クラメルの公式
- Pythonでの実装
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
小行列式と余因子
小行列式
$n$ 次正方行列 $\bm{A}$ の $(i, j)$ 小行列式(minor)$M_{ij}$ は、$\bm{A}$ から第 $i$ 行と第 $j$ 列を取り除いて得られる $(n-1)$ 次行列式です。
例えば、$\bm{A} = \begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix}$ のとき、
$$ M_{11} = \begin{vmatrix} 5 & 6 \\ 8 & 9 \end{vmatrix} = 45 – 48 = -3 $$
$$ M_{12} = \begin{vmatrix} 4 & 6 \\ 7 & 9 \end{vmatrix} = 36 – 42 = -6 $$
余因子
$(i, j)$ 余因子(cofactor)$C_{ij}$ は、小行列式に符号を付けたものです。
$$ C_{ij} = (-1)^{i+j} M_{ij} $$
符号のパターンは以下のようになります(チェッカーボードパターン)。
$$ \begin{pmatrix} + & – & + & \cdots \\ – & + & – & \cdots \\ + & – & + & \cdots \\ \vdots & \vdots & \vdots & \ddots \end{pmatrix} $$
余因子展開の公式
第i行に沿った展開
$$ \det(\bm{A}) = \sum_{j=1}^{n} a_{ij} C_{ij} = \sum_{j=1}^{n} (-1)^{i+j} a_{ij} M_{ij} $$
これは任意の行 $i$ について成り立ちます。
第j列に沿った展開
$$ \det(\bm{A}) = \sum_{i=1}^{n} a_{ij} C_{ij} = \sum_{i=1}^{n} (-1)^{i+j} a_{ij} M_{ij} $$
これは任意の列 $j$ について成り立ちます。
なぜ成り立つのか
行列式の定義 $\det(\bm{A}) = \sum_{\sigma} \mathrm{sgn}(\sigma) \prod_i a_{i,\sigma(i)}$ を第1行に関してまとめると、各項は $a_{1,j}$ を含むかどうかで分類できます。$a_{1,j}$ を含む項をまとめると、$a_{1,j}$ の係数がちょうど $(-1)^{1+j} M_{1j}$(余因子 $C_{1j}$)になることが示せます。
具体例: 3次行列式の余因子展開
$$ \bm{A} = \begin{pmatrix} 2 & 1 & 3 \\ 0 & -1 & 2 \\ 1 & 4 & -1 \end{pmatrix} $$
第1行に沿って展開します。
$$ \begin{align} \det(\bm{A}) &= a_{11} C_{11} + a_{12} C_{12} + a_{13} C_{13} \\ &= 2 \cdot (-1)^{1+1} \begin{vmatrix} -1 & 2 \\ 4 & -1 \end{vmatrix} + 1 \cdot (-1)^{1+2} \begin{vmatrix} 0 & 2 \\ 1 & -1 \end{vmatrix} + 3 \cdot (-1)^{1+3} \begin{vmatrix} 0 & -1 \\ 1 & 4 \end{vmatrix} \\ &= 2 \cdot (1 – 8) + (-1) \cdot (0 – 2) + 3 \cdot (0 + 1) \\ &= 2 \cdot (-7) + (-1)(-2) + 3 \cdot 1 \\ &= -14 + 2 + 3 \\ &= -9 \end{align} $$
コツ: 0が多い行や列に沿って展開すると、計算量が減ります。上の例では第2行($a_{21} = 0$)に沿って展開すると、3項のうち1項が0になります。
余因子行列と逆行列
余因子行列
$\bm{A}$ の余因子行列(adjugate matrix)$\mathrm{adj}(\bm{A})$ は、余因子 $C_{ij}$ を $(j, i)$ 成分に持つ行列です(転置に注意)。
$$ \mathrm{adj}(\bm{A}) = (C_{ji}) = \begin{pmatrix} C_{11} & C_{21} & \cdots & C_{n1} \\ C_{12} & C_{22} & \cdots & C_{n2} \\ \vdots & \vdots & \ddots & \vdots \\ C_{1n} & C_{2n} & \cdots & C_{nn} \end{pmatrix} $$
逆行列の公式
$\det(\bm{A}) \neq 0$ のとき、
$$ \bm{A}^{-1} = \frac{1}{\det(\bm{A})} \mathrm{adj}(\bm{A}) $$
この公式は理論的には美しいですが、大きな行列に対しては計算効率が悪いため、実用上はガウスの消去法やLU分解が使われます。
クラメルの公式
連立一次方程式 $\bm{A}\bm{x} = \bm{b}$ の解は、$\det(\bm{A}) \neq 0$ のとき、
$$ x_j = \frac{\det(\bm{A}_j)}{\det(\bm{A})} $$
で求まります。ここで $\bm{A}_j$ は $\bm{A}$ の第 $j$ 列を $\bm{b}$ で置き換えた行列です。
例
$$ \begin{cases} 2x + y = 5 \\ 3x – 2y = 4 \end{cases} $$
$$ \bm{A} = \begin{pmatrix} 2 & 1 \\ 3 & -2 \end{pmatrix}, \quad \bm{b} = \begin{pmatrix} 5 \\ 4 \end{pmatrix} $$
$$ \det(\bm{A}) = -4 – 3 = -7 $$
$$ x = \frac{\begin{vmatrix} 5 & 1 \\ 4 & -2 \end{vmatrix}}{-7} = \frac{-10 – 4}{-7} = \frac{-14}{-7} = 2 $$
$$ y = \frac{\begin{vmatrix} 2 & 5 \\ 3 & 4 \end{vmatrix}}{-7} = \frac{8 – 15}{-7} = \frac{-7}{-7} = 1 $$
Pythonでの実装
余因子展開の実装
import numpy as np
def cofactor(A, i, j):
"""(i,j)余因子を計算する"""
# 第i行と第j列を取り除いた小行列
minor = np.delete(np.delete(A, i, axis=0), j, axis=1)
return (-1)**(i + j) * np.linalg.det(minor)
def det_cofactor_expansion(A, row=0):
"""第row行に沿った余因子展開で行列式を計算する"""
n = A.shape[0]
if n == 1:
return A[0, 0]
result = 0
for j in range(n):
result += A[row, j] * cofactor(A, row, j)
return result
def adjugate(A):
"""余因子行列を計算する"""
n = A.shape[0]
adj = np.zeros((n, n))
for i in range(n):
for j in range(n):
adj[j, i] = cofactor(A, i, j) # 転置に注意
return adj
# テスト
A = np.array([[2, 1, 3],
[0, -1, 2],
[1, 4, -1]], dtype=float)
print("行列 A:")
print(A)
print(f"\nNumPyによる行列式: {np.linalg.det(A):.1f}")
print(f"余因子展開による行列式: {det_cofactor_expansion(A):.1f}")
# 逆行列の検証
adj_A = adjugate(A)
det_A = np.linalg.det(A)
A_inv_cofactor = adj_A / det_A
A_inv_numpy = np.linalg.inv(A)
print(f"\n余因子行列による逆行列:")
print(np.round(A_inv_cofactor, 4))
print(f"\nNumPyによる逆行列:")
print(np.round(A_inv_numpy, 4))
クラメルの公式の実装
import numpy as np
def cramers_rule(A, b):
"""クラメルの公式で連立方程式 Ax = b を解く"""
n = A.shape[0]
det_A = np.linalg.det(A)
if abs(det_A) < 1e-10:
raise ValueError("行列式が0です。一意解は存在しません。")
x = np.zeros(n)
for j in range(n):
A_j = A.copy()
A_j[:, j] = b
x[j] = np.linalg.det(A_j) / det_A
return x
# テスト: 2x + y = 5, 3x - 2y = 4
A = np.array([[2, 1],
[3, -2]], dtype=float)
b = np.array([5, 4], dtype=float)
x_cramer = cramers_rule(A, b)
x_numpy = np.linalg.solve(A, b)
print(f"クラメルの公式: x = {x_cramer}")
print(f"NumPy solve: x = {x_numpy}")
まとめ
本記事では、行列式の余因子展開について解説しました。
- 小行列式 $M_{ij}$: 第 $i$ 行と第 $j$ 列を取り除いた行列式
- 余因子 $C_{ij}$: $(-1)^{i+j} M_{ij}$
- 余因子展開: $\det(\bm{A}) = \sum_j a_{ij} C_{ij}$(任意の行 $i$ について成立)
- 逆行列の公式: $\bm{A}^{-1} = \mathrm{adj}(\bm{A}) / \det(\bm{A})$
- クラメルの公式: $x_j = \det(\bm{A}_j) / \det(\bm{A})$
次のステップとして、連立方程式の実用的な解法について学びましょう。