Gauss-Jordanの消去法#
ガウスの消去法 (Gauss elimination)、 ガウス=ジョルダンの消去法 (Gauss-Jordan elimination)、あるいは 掃き出し法 と呼ばれる方法。
\(n\) 個の未知数 \(x_j(j=1, \cdots, n)\) と \(n\) 個の方程式からなる連立 1 次方程式
について考える。
連立1次方程式を
の形に書く。
Gaussの消去法では、前進消去と後退代入という操作によって方程式を解く。
前進消去#
(1) 1番目の未知数\(x_1\)に着目し、2~\(n\)番目の方程式から消去する。
\(i=2,\cdots,n\)について、\(i\)番目の式から\(1\)番目の式の\(m_{i1} = a_{i1}^{(1)} / a_{11}^{(1)}\)倍を引けばよい(\(a_{11}^{(1)}\neq 0\)と仮定しておく)
この操作のあとの連立1次方程式は、
とおくと、
となる。
この消去操作において着目した行列要素の位置 \((1,1)\)を 枢軸 (pivot)、その要素\(a_{11}^{(1)}\)を 枢軸要素 、 \(m_{i1}\)を 乗数 と呼ぶ。
(2) 2番目の未知数\(x_2\)に着目し、3~\(n\)番目の方程式から消去する。
以下、このような操作を\(n-1\)回まで繰り返す。そして以下を得る
ただし、\(a_{11}^{(1)} \neq 0, \quad a_{22}^{(2)} \neq 0, \quad \cdots, \quad a_{n n}^{(n)} \neq 0\)と仮定している。
後退代入#
\(x_n\)については、
なので、
として解が求まる。
\(x_{n-1}\)については
となっているので
として解が求まる。
これを一般化すると、解は
によって求まる。これを 後退代入 と呼ぶ。
# データの作成
import numpy as np
A = np.array([
[1,2,3],
[4,5,6],
[7,8,0],
])
x_true = np.array([1, 2, 3])
b = A @ x_true
def forward_elimination(A, b):
# 前進消去
n = len(b)
for k in range(n):
for i in range(k + 1, n):
m = A[i,k] / A[k,k]
# numpyのベクトル演算に頼っているが、本来はここもA[i,]の各j要素にわたってfor j in range(n)が必要 → 計算量はO(n^3)
A[i,k:] = A[i,k:] - m * A[k,k:]
b[i] = b[i] - m * b[k]
# print(f"{i=}, {j=}, {m=}")
# print(A)
return A, b
A, b = forward_elimination(A, b)
print(f"{A=}")
print(f"{b=}")
A=array([[ 1, 2, 3],
[ 0, -3, -6],
[ 0, 0, -9]])
b=array([ 14, -24, -27])
def backward_substitution(A, b):
# 後退代入
n = len(b)
x = np.zeros(shape=(n,))
for i in range(n-1, -1, -1):
x[i] = (1 / A[i,i]) * (b[i] - A[i,i+1:] @ x[i+1:])
return x
x = backward_substitution(A, b)
print(f"{x=}")
x=array([1., 2., 3.])
消去法が使える行列#
以下の条件のいずれかを満たす行列\(A\)は、\(a_{11}^{(1)} \neq 0, \quad a_{22}^{(2)} \neq 0, \quad \cdots, \quad a_{n n}^{(n)} \neq 0\)を満たし、消去法が適用可能
行方向に一般化狭義優対角.
列方向に一般化狭義優対角.
(正則な) \(\mathrm{M}\) 行列.
対称部分が正定値: \(\boldsymbol{x}^{\top} A \boldsymbol{x}=\frac{1}{2} \boldsymbol{x}^{\top}\left(A+A^{\top}\right) \boldsymbol{x}>0 \quad(\forall \boldsymbol{x} \neq \mathbf{0})\).
一般化狭義優対角行列
各行において, 対角要素の絶対値が非対角要素の絶対値の和に比べて大きい 行列, すなわち,
を満たす行列 \(A\) を(行方向の) 狭義優対角行列 と呼ぶ.
また、正の実数\(d_1, \cdots, d_n\) に対して
が成り立つとき, \(A\) を(行方向の) 一般化狭義優対角行列 と呼ぶ
M行列
正則な実行列\(A=(a_{ij})\)について、非対角要素がすべて非正\(a_{ij} \leq 0 (i \neq j)\)であり、逆行列\(A^{-1}\)が非負行列(各要素が非負の実数である行列)であるとき、\(A\)を M行列 と呼ぶ。
枢軸選択#
枢軸要素\(a_{kk}^{(k)}\)が0になると次の段に進めなくなるし、0に近い値だと誤差の拡大が起きて精度の良い解が得られない。
第\(k\)段の消去の前に、\(k\)番目以降の方程式を入れ替えて、枢軸要素の絶対値\(|a_{kk}^{(k)}|\)がそれより下の位置にある要素の絶対値\(|a_{ik}^{(k)}| (k\leq i \leq n)\)の中で最大になるようにすることができる。この操作を 枢軸選択 (pivoting) あるいは 部分枢軸選択 (partial pivoting) と呼ぶ。
import numpy as np
A = np.array([
[-0.001, 6],
[3, 5],
])
x_true = np.array([-1, 1])
b = A @ x_true
print(f"{b=}")
b=array([6.001, 2. ])
A, b = forward_elimination(A, b)
print(f"{A=}")
print(f"{b=}")
x = backward_substitution(A, b)
print(f"{x=}")
A=array([[-1.0000e-03, 6.0000e+00],
[ 0.0000e+00, 1.8005e+04]])
b=array([6.0010e+00, 1.8005e+04])
x=array([-1., 1.])
スケーリング#
各方程式にゼロでない定数を掛けることをスケーリングと呼ぶ。
数学的には解は不変のはずだが、枢軸選択の結果はスケーリングに依存してしまう問題がある。
参考文献#
杉原正顯, & 室田一雄. (2009). 線形計算の数理. 岩波書店.