『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

単位行列と逆行列

【Pythonで学ぶ線形代数学講座(11)】単位行列と逆行列
次回記事では連立一次方程式を解くための一般的な手順を考えます。その準備として、今回は単位行列と逆行列を定義して、その基本的な性質と Python で実装する方法を学びます。

単位行列の定義と性質

行列 $A$ に左右どちらから掛けても $A$ となるような行列を単位行列(identity matrix)とよびます。$2\times 2$ の単位行列は
 \[I=\begin{bmatrix}1&0\\0&1\end{bmatrix}\tag{1}\]
で表されます。たとえば、$A$ に $I$ を左から掛けると
 \[IA=\begin{bmatrix}1&0\\0&1\end{bmatrix}\begin{bmatrix}a&b\\c&d\end{bmatrix}=\begin{bmatrix}a&b\\c&d\end{bmatrix}\]
となって $A$ は変化しません。同様にして $AI=A$ も確かめられます。もちろん任意のベクトルも単位行列によって変化しません。
 \[\begin{bmatrix}1&0\\0&1\end{bmatrix}\begin{bmatrix}a\\b\end{bmatrix}=\begin{bmatrix}a\\b\end{bmatrix}\]
このように、行列やベクトルに対して恒等変換を施すことから、単位行列はスカラーの $1$ に相当します。一般に $n\times n$ の単位行列は、主対角線上の成分 (左上から右下に並ぶ要素) が $1$、他はすべて $0$ の行列として表されます:
 \[I=\begin{bmatrix}1&0 &\cdots &0\\0&1 &\cdots &0\\\vdots & \vdots &\ddots &\vdots\\0&0 &\cdots &1\end{bmatrix}\tag{2}\]
Python で単位行列の性質を確認してみましょう。NumPy をインポートして、 numpy.identity() 関数を使って $3\times{3}$ の単位行列を生成します。行列の各要素を整数型にしたいので、データ型 (dtype) に np.int8 を指定しておきます。

# python_identity_matrix

# In[1]

import numpy as np

# 3×3の単位行列を生成
I = np.identity(3, dtype = np.int8)

print(I)
# [[1 0 0]
#  [0 1 0]
#  [0 0 1]]

次に同サイズの行列 $A$ を作っておきます。

# In[2]

# 行列Aを定義
A = np.arange(9).reshape(3, 3)

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

単位行列 $I$ を $A$ の左側から掛けてみます。

# In[3]

# 行列積IAを計算
print(I@A)

# [[0 1 2]
#  [3 4 5]
#  [6 7 8]]

行列 $A$ と一致していますね。今度は単位行列 $I$ を $A$ の右側から掛けます。

# In[4]

# 行列積AIを計算
print(A@I)

# [[0 1 2]
#  [3 4 5]
#  [6 7 8]]

やはり、恒等変換となっていることが確認できました。
 
関連記事:numpy.identity()

逆行列と正則行列

行列 $A$ に左右どちらから掛けても単位行列となるような行列、すなわち
 \[A^{-1}A=AA^{-1}=I\tag{3}\]
を満たすような行列 $A^{-1}$ が存在すれば、$A^{-1}$ を $A$ の 逆行列 (invertible matrix) とよびます。また、自身の逆行列をもつ行列を 正則行列 (regular matrix) とよびます。
 
ある行列について、逆行列は存在したとしても 1 個だけです。実際、$A$ に互いに相異なる逆行列 $B$ と $C$ が存在したとすると、
 \[BA=AC=I\tag{4}\]
が成り立ちます。一方で結合法則により
 \[B(AC)=(BA)C\tag{5}\]
が成り立ちますが、(4) より
 \[BI=IC\tag{6}\]
となります。すなわち $B=C$ であり、$A$ の逆行列は唯一つであることがわかります。
 
行列 $A=\begin{bmatrix}a&b\\c&d\end{bmatrix}$ の逆行列は
 \[A^{-1}=\frac{1}{ad-bc}\begin{bmatrix}d&-b\\-c&a\end{bmatrix}\tag{7}\]
で与えられます(実際に $A^{-1}A$ と $AA^{-1}$ を計算して $I$ になることが確かめられます)。ただし、$ad-bc=0$ のときは分母が $0$ になってしまうので逆行列は存在しません。
 
$ad-bc$ は $A$ の行列式 $\det A$ なので、(4) は
 \[A^{-1}=\frac{1}{\det A}\begin{bmatrix}d&-b\\-c&a\end{bmatrix}\tag{8}\]
と表すこともできます。
 
行列積 $AB$ の逆行列は
 \[(AB)^{-1}=B^{-1}A^{-1}\tag{9}\]
で与えられます。実際に $AB$ に左から掛けてみると
 \[B^{-1}A^{-1}AB=B^{-1}IB=B^{-1}B=I\]
となって確かに単位行列になります。同じように右から掛けても単位行列になります。
 
Python で逆行列の性質を確認してみましょう。まず、3 行 3 列の適当な行列 A を生成します。

# python_invertible_matrix

# In[1]

import numpy as np

np.set_printoptions(precision=3)

# 乱数のseedを設定
np.random.seed(5)

# 3×3配列をランダムに生成
A = np.random.randint(0, 9, (3, 3))

print(A)
# [[3 6 6]
#  [0 8 4]
#  [7 0 0]]

念のために A の 行列式 の値が 0 ではないことを確認しておきます。

# In[2]

from scipy.linalg import det

# Aの行列式を求める
print(det(A))

# -168.0

逆行列は NumPy の linalg.inv() 関数を使って求められます。

# In[3]

# Aの逆行列
A_inv = np.linalg.inv(A)

print(A_inv)
# [[ 0.     0.     0.143]
#  [-0.167  0.25   0.071]
#  [ 0.333 -0.25  -0.143]]

A に A の逆行列を左から掛けて単位行列になることを確認してみます。

# In[4]

# AにAの逆行列を左から掛ける
print(A_inv @ A)

# [[ 1.000e+00  0.000e+00  0.000e+00]
#  [-2.776e-17  1.000e+00  0.000e+00]
#  [ 5.551e-17  0.000e+00  1.000e+00]]

同様に A に A の逆行列を右から掛けて単位行列になることを確めます。

# In[5]

# AにAの逆行列を右から掛ける
print(A @ A_inv)

# [[1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]]

numpy.linalg.inv()

numpy.linalg.inv() は配列 a を受け取って、a の逆行列を返します。

# numpy_invertible_matrix

# In[1]

import numpy as np

# 行列aを定義
a = np.array([[1, 2],
              [3, 4]])

# aの逆行列ainv
ainv = np.linalg.inv(a)

# aとainvの積bを計算
b = np.dot(a, ainv)

print("aの逆行列\n{}\n".format(ainv))
print("aとaの逆行列の積\n{}".format(b))

# aの逆行列
# [[-2.   1. ]
#  [ 1.5 -0.5]]

# aとaの逆行列の積
# [[1.00000000e+00 1.11022302e-16]
#  [0.00000000e+00 1.00000000e+00]]

scipy.linalg.inv()

scipy.linalg.inv() は配列 a を受け取って、a の逆行列を返します。

# scipy_invertible_matrix

# In[1]

import numpy as np
from scipy import linalg

# 行列aを定義
a = np.array([[7, -2],
              [5,  1]])

# aの逆行列ainv
ainv = linalg.inv(a)

# aとainvの積bを計算
b = np.dot(a, ainv)

print("aの逆行列\n{}\n".format(ainv))
print("aとaの逆行列の積\n{}".format(b))

# aの逆行列
# [[ 0.05882353  0.11764706]
#  [-0.29411765  0.41176471]]

# aとaの逆行列の積
# [[ 1.00000000e+00 -1.11022302e-16]
#  [ 0.00000000e+00  1.00000000e+00]]

 

コメント

  1. すぎ より:

    逆行列との積が単位行列にならないのはなぜでしょうか??

    • BlogCat より:

      浮動小数点の演算では、原理的に丸め誤差が避けられません。たとえば、10 進数の 0.2 は 2 進数で表すと本来は無限級数になります。しかし、コンピュータは限られたビット数で数値を扱わなければならないので、有限桁数で打ち切る必要があります。単純な引き算でさえ誤差が生じ得ます。次の例を見てください。

      x = 0.2 - (0.9 - 0.7)
      print(x)
      
      #実行結果
      #-5.551115123125783e-17


      計算結果が 0 になるべきところで、僅かな誤差が生じています。これは 0.2 と 0.9 – 0.7 が、コンピュータ内部では異なる値であることを示しています。0.2 と 0.9 – 0.7 をそれぞれ 40 桁まで正確に表示させて、比較すると、0.2 のほうが僅かに小さいことがわかります。

      a = 0.2
      b = 0.9 - 0.7
      
      print("a: {:.40f}".format(a))
      print("b: {:.40f}".format(b))
      
      #実行結果
      #a: 0.2000000000000000111022302462515654042363
      #b: 0.2000000000000000666133814775093924254179


      上の例のように、同程度の数の引き算で有効桁数が減少することを「桁落ち」といいます。特に行列と逆行列の積の計算では、対角成分の要素を計算するときに (原理的には) 結果が 0 になるような引き算が内部で実行されるので、この「桁落ち」が生じやすいといえます。

  2. HNaito より:

    下記は誤植と思われますので、ご確認ください。
    #In[5]の上の文章で、左から → 右から

    • あとりえこばと より:

      細かい所までみてくださって、本当にありがとうございます。
      直しておきました。