基本行列

基本行列

 

線形代数の基本行列

 次回記事でガウス・ジョルダンの消去法による連立方程式の解き方を学びますが、その準備として線形代数における 基本行列(elementary matrices) について説明しておきます。

置換行列

 任意の行列 $A$ に単位行列 $I$ を掛けると、$A$ が元の形を保つことはすでに学んでいます:
 
\[IA=AI=A\]
 すなわち単位行列 $I$ は恒等変換行列です。
 いま、$3\times 3$ 単位行列と、$3\times 3$ 正方行列
 
\[I=\begin{bmatrix}1&0&0\\0&1&0\\0&0&1\end{bmatrix},\quad A=\begin{bmatrix}a&b&c\\d&e&f\\g&h&i\end{bmatrix}\]
を考えて、$I$ の 2 行目と 3 行目を入れ替えた行列
 
\[P_{2,3}=\begin{bmatrix}1&0&0\\0&0&1\\0&1&0\end{bmatrix}\]
を定義して行列 $A$ の左側に掛けてみると、
 
\[P_{2,3\ }A=\begin{bmatrix}1&0&0\\0&0&1\\0&1&0\end{bmatrix}
\begin{bmatrix}a&b&c\\d&e&f\\g&h&i\end{bmatrix}=\begin{bmatrix}a&b&c\\g&h&i\\d&e&f\end{bmatrix}\]
となって、$A$ の 2 行目と 3 行目を入れ替えることがわかります。
 
 $n\times n$ の単位行列の $i$ 行目と $j$ 行目を入れ替えた行列 $P_{i,j}$ を任意の行列 $A$ に左側から掛けると、$A$ の $i$ 行目と $j$ 行目を入れ替えます。$A$ の右側から掛けると列を交換します。
 
 一般に行列に作用して行または列の位置を変える行列を 置換行列 とよびます。複数回の交換をほどこす行列、すなわち一回交換の置換行列同士の積も置換行列に含まれます。
 
 $3\times 3$ の正方行列の置換行列は $3!=6$ 種類です。
 そのうち行を $0$ 回交換する (すなわち交換しない) 置換行列は単位行列です:
 
\[I=\begin{bmatrix}1&0&0\\0&1&0\\0&0&1\end{bmatrix}\]
 行を $1$ 回交換する置換行列は $3$ 種類あります:
 
\[P_{12}=\begin{bmatrix}0&1&0\\1&0&0\\0&0&1\end{bmatrix},\quad P_{13}=\begin{bmatrix}0&0&1\\0&1&0\\1&0&0\end{bmatrix},\quad P_{23}=\begin{bmatrix}1&0&0\\0&0&1\\0&1&0\end{bmatrix}\]
 行を $2$ 回交換する置換行列は $2$ 種類です:
 
\[P_{23}P_{12}=\begin{bmatrix}0&1&0\\0&0&1\\1&0&0\end{bmatrix},\quad P_{12}P_{23}=\begin{bmatrix}0&0&1\\1&0&0\\0&1&0\end{bmatrix}\]

行を定数倍する

 正方単位行列の $(i,i)$ 成分を $k$ で置き換えた行列 $C_{i,k}$ は $A$ の $i$ 行目を $k$ 倍します:
 
\[C_{1,k\ }A=\begin{bmatrix}k&0&0\\0&1&0\\0&0&1\end{bmatrix}\begin{bmatrix}a&b&c\\d&e&f\\g&h&i\end{bmatrix}=\begin{bmatrix}ka&kb&kc\\d&e&f\\g&h&i\end{bmatrix}\]
 $k$ 倍した行を $1/k$ 倍すれば、もとの行列に戻るので、$C_{i,k}$ の逆行列は $C_{i,1/k}$ です。

別の行の定数倍を加える

 正方単位行列の $(i,j)$ 成分を $k$ で置き換えた行列 $E_{i,j,k}$ は $A$ の $i$ 行目に $j$ 行目の $k$ 倍を加えます:
 
\[E_{2,1,k\ }A=\begin{bmatrix}1&0&0\\k&1&0\\0&0&1\end{bmatrix}\begin{bmatrix}a&b&c\\d&e&f\\g&h&i\end{bmatrix}=\begin{bmatrix}a&b&c\\d+ka&e+kb&f+kc\\g&h&i\end{bmatrix}\]
 $i$ 行目に $j$ 行目の $k$ 倍を加えたあとで $i$ 行目から $j$ 行目の $k$ 倍を引けば、もとの行列に戻ります。したがって、$E_{i,j,k}$ の逆行列は $E_{i,j,-k}$ となります。
 
\[\begin{bmatrix}1&0&0\\-k&1&0\\0&0&1\end{bmatrix}\begin{bmatrix}a&b&c\\d+ka&e+kb&f+kc\\g&h&i\end{bmatrix}=\begin{bmatrix}a&b&c\\d&e&f\\g&h&i\end{bmatrix}\]

基本行列の実装

 今後の記事で基本行列の積を計算することがあるので、3 種の基本行列を Python で実装しておきます。

# python_elementary_matrices

# In[1]

import numpy as np

# i行とj行の交換
def matrix_p(n, i=1, j=1):
    arr = np.eye(n)
    arr[[i-1, j-1]] = arr[[j-1, i-1]]
    return arr

# i行の定数倍
def matrix_c(n, i=1, k=1):
    arr = np.eye(n)
    arr[i-1, i-1] = k
    return arr

# i行にj行の定数倍を加える
def matrix_e(n, i=1, j=1, k=1):
    arr = np.eye(n)
    arr[i-1, j-1] = k
    return arr

 numpy.eye() で指定した大きさの単位行列を生成して、必要な成分を書き換えます。
 matrix_p() の内部に記述されている

arr[[i-1, j-1]] = arr[[j-1, i-1]]

の意味については記事の後半を参照してください。
 
 定義した関数を使って 4 × 4 の基本行列を生成してみましょう。
 
2行目と3行目を入れ替える行列:

# In[2]

p = matrix_p(4, 2, 3)

print(p)
# [[1. 0. 0. 0.]
#  [0. 0. 1. 0.]
#  [0. 1. 0. 0.]
#  [0. 0. 0. 1.]]

3行目を5倍する行列:

# In[3]

c = matrix_c(4, 3, 5)

print(c)
# [[1. 0. 0. 0.]
#  [0. 1. 0. 0.]
#  [0. 0. 5. 0.]
#  [0. 0. 0. 1.]]

3行目に1行目の2倍を加える行列:

# In[4]

e = matrix_e(4, 3, 1, 2)

print(e)
# [[1. 0. 0. 0.]
#  [0. 1. 0. 0.]
#  [2. 0. 1. 0.]
#  [0. 0. 0. 1.]]

 一般に、NumPy で行を操作する場合、配列の行にアクセスして書き換えます。たとえば「 3 行目に 1 行目の 5 倍を加える」という操作は次のように記述します。

# In[5]

# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]
x = np.arange(1, 10).reshape(3,3)

# xの3行目に1行目の5倍を加える
x[2, :] = x[2, :] + 5 * x[0, :]

print(x)
# [[ 1  2  3]
#  [ 4  5  6]
#  [12 18 24]]

 行の交換については以下の節を参照してください。
 

配列の行を入れ替える

 NumPy で 配列の行を入れ替える方法 を解説します。
 最初に 3×3 の 2 次元配列を作成しておきます。

# python_swap_rows

# In[1]

import numpy as np

x = np.arange(1, 10).reshape(3, 3)

print(x)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]]

 NumPy ではファンシーインデックス (インデックスリストを渡して複数要素に同時アクセスする手法) を使って特定の行にアクセスできます。たとえば、x[[1, 2]] は 2 行目と 3 行目を縦積みした配列を取得します。

# In[2]

print(x[[1, 2]])
# [[4 5 6]
#  [7 8 9]]

 x[[2, 1]] は 3 行目と 2 行目を取得します。

# In[3]

print(x[[2, 1]])
# [[7 8 9]
#  [4 5 6]]

 ファンシーインデックスはメモリへの直接アクセスなので、x[[1, 2]] に x[[2, 1]] を代入すると、結果として 2 行目と 3 行目が交換されることになります (もとの配列が変更されます)。

# In[4]

x[[1, 2]] = x[[2, 1]]

print(x)
# [[1 2 3]
#  [7 8 9]
#  [4 5 6]]

 一般に、x[[i-1, j-1]] = x[[j-1, i-1]] という記述で i 行と j 行を入れ替えられます。

def swap_rows(x, i, j):
    x[[i-1, j-1]] = x[[j-1, i-1]]

 とはいえ、場合によっては元の配列を変更したくないと思うかもしれません。以下に定義する swap_rows()関数 は、受け取った配列のコピーを作成して行を入れ替えます。

# In[5]

def swap_rows(x, i, j):
    a = x.copy()
    a[[i-1, j-1]] = a[[j-1, i-1]]
    return a

 swap_rows() の動作を確認するために、新しい 2 次元配列 y を作成しておきます。

# In[6]

# 乱数生成器のシードを0に固定
np.random.seed(0)

# 要素をランダムに決定して配列を生成
y = np.random.randint(0, 10, (5, 5))

print(y)
# [[5 0 3 3 7]
#  [9 3 5 2 4]
#  [7 6 8 8 1]
#  [6 7 7 8 1]
#  [5 9 8 9 4]]

 y を swap_rows() に渡して 2 行目と 4 行目を入れ替えます。

# In[7]

# yの2行目と4行目を交換
sy = swap_rows(y, 2, 4)

print(sy)
# [[5 0 3 3 7]
#  [6 7 7 8 1]
#  [7 6 8 8 1]
#  [9 3 5 2 4]
#  [5 9 8 9 4]]