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

下三角行列と上三角行列

≪【前の記事】ガウス・ジョルダンの消去法

下三角行列

主対角成分の上にある成分がすべてゼロとなるような正方行列を下三角行列または左三角行列とよびます。たとえば、$4\times 4$ の下三角行列は
 \[L=\begin{bmatrix}a_{11}&0&0&0\\a_{21}&a_{22}&0&0\\
a_{31}&a_{32}&a_{33}&0\\a_{41}&a_{42}&a_{43}&a_{44}\end{bmatrix}\]
のように表されます。

numpy.tril()

numpy.tril() は行列 m の指定対角成分の上にある要素をすべて 0 に置き換えた配列を返します。

numpy.tril(m, k=0)

k を省略、または 0 を指定すると、主対角成分の上にある要素を 0 にします。m に正方行列を渡せば一般的な下三角行列が戻ります。

# lower_triangular__matrix_01

# In[1]

import numpy as np

# 4×4下三角行列を生成
L = np.tril([[ 1,  2,  3,  4],
             [ 5,  6,  7,  8],
             [ 9, 10, 11, 12],
             [13, 14, 15, 16]])

print(L)
# [[ 1  0  0  0]
#  [ 5  6  0  0]
#  [ 9 10 11  0]
#  [13 14 15 16]]

k は基準対角成分の位置を決めます。k に正の値を指定すると、主対角成分 (k=0) の位置から右に k 要素だけずらした位置にある対角成分の上側の要素を 0 に置き換えます。

# In[2]

# 4×4下三角行列
# 基準対角成分を右にスライド
L = np.tril([[ 1,  2,  3,  4],
             [ 5,  6,  7,  8],
             [ 9, 10, 11, 12],
             [13, 14, 15, 16]], k=1)

print(L)
# [[ 1  2  0  0]
#  [ 5  6  7  0]
#  [ 9 10 11 12]
#  [13 14 15 16]]

k に負の値を指定すると、基準対角成分が左にスライドします。

# In[3]

# 4×4下三角行列
# 基準対角成分を左にスライド
L = np.tril([[ 1,  2,  3,  4],
             [ 5,  6,  7,  8],
             [ 9, 10, 11, 12],
             [13, 14, 15, 16]], k=-1)

print(L)
# [[ 0  0  0  0]
#  [ 5  0  0  0]
#  [ 9 10  0  0]
#  [13 14 15  0]]

numpy.tri()

numpy.tri() は指定対角成分の上にある要素を 0, 残りの要素をすべて 1 とする下三角行列を生成します。

numpy.tri(N, M=None, k=0, dtype=)

N, M はそれぞれ行数と列数、k は基準対角成分の位置です。
M を省略すると N × N の正方行列を返します。k = 0 を指定すると、主対角成分の右上を 0 とする下三角行列を返します。

# lower_triangular__matrix_02

# In[1]

import numpy as np

# 5×5の下三角行列を生成
L = np.tri(5)

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

k に整数を渡すと、主対角線から右に k 要素だけスライドさせた位置を基準対角成分として下三角行列を生成します。

# In[2]

# 3×6の下三角行列を生成
# 基準対角成分を右に2要素スライド
L = np.tri(3, 6, k=2)

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

上三角行列

主対角成分の下にある成分すべてが 0 であるような正方行列を 上三角行列 または右三角行列とよびます。$4\times 4$ の上三角行列は
 \[U=\begin{bmatrix}a_{11}&a_{12}&a_{13}&a_{14}\\0&a_{22}&a_{23}&a_{24}\\0&0&a_{33}&a_{34}\\0&0&0&a_{44}\end{bmatrix}\]
のように表されます。

numpy.triu()

numpy.triu() は受け取った行列の指定対角成分の下にある要素をすべて 0 に置き換えます。

numpy.triu(m, k=0)

k を省略、または 0 を指定すると、主対角成分の下にある要素を 0 にします。m に正方行列を渡せば一般的な上三角行列が戻ります。

# upper_triangular_matrix

# In[1]

import numpy as np

# 4×4上三角行列を生成
U = np.triu([[ 1,  2,  3,  4],
             [ 5,  6,  7,  8],
             [ 9, 10, 11, 12],
             [13, 14, 15, 16]])

print(U)
# [[ 1  2  3  4]
#  [ 0  6  7  8]
#  [ 0  0 11 12]
#  [ 0  0  0 16]]

k に整数を指定すると、主対角線から右に k 要素だけスライドさせた位置を基準対角成分として上三角行列を生成します (k が負の値ならば左に |k| だけスライドします)。

# In[2]

# 4×4上三角行列
# 基準対角成分を右にスライド
U = np.triu([[ 1,  2,  3,  4],
             [ 5,  6,  7,  8],
             [ 9, 10, 11, 12],
             [13, 14, 15, 16]], k=1)

print(U)
# [[ 0  2  3  4]
#  [ 0  0  7  8]
#  [ 0  0  0 12]
#  [ 0  0  0  0]]

三角行列の性質

三角行列同士の演算

定義より明らかに、下(上)三角行列同士の和と差は下(上)三角行列となります。また、下(上)三角行列同士の積も下(上)三角行列です。対角成分はそれぞれの三角行列の対角成分同士の積となります。計算機代数システム SymPy を使って確認してみましょう。最初に下三角行列と上三角行列を生成する関数を定義します。

# triangular_matrix_operation

# In[1]

import sympy

# 結果を LaTeX で表示する
sympy.init_printing()

# 下三角行列
def tri_lower(m, n, s):
    sympy.Symbol(s)
    elements = lambda i,j : sympy.var('{}{}{}'.format(s, i+1, j+1))
    mtx = sympy.Matrix(m, n, elements)
    for i in range(m):
        for j in range(n):
            if j > i:
                mtx[i, j] = 0
    return mtx

# 上三角行列
def tri_upper(m, n, s):
    sympy.Symbol(s)
    elements = lambda i,j : sympy.var('{}{}{}'.format(s, i+1, j+1))
    mtx = sympy.Matrix(m, n, elements)
    for i in range(m):
        for j in range(n):
            if i > j:
                mtx[i, j] = 0
    return mtx

m, n には行列の行数と列数を指定します。s には要素を表す記号を渡します。各要素の右下には自動的に添字が割り当てられます。3 × 3 の下三角行列を 2 つ生成してみます。

# In[2]

# 下三角行列を生成
L1 = tri_lower(3, 3, "a")
L2 = tri_lower(3, 3, "b")

display(L1, L2)
\[\left[\begin{matrix}a_{11} & 0 & 0\\a_{21} & a_{22} & 0\\a_{31} & a_{32} & a_{33}\end{matrix}\right]\]

\[\left[\begin{matrix}b_{11} & 0 & 0\\b_{21} & b_{22} & 0\\b_{31} & b_{32} & b_{33}\end{matrix}\right]\]

L1 と L2 の行列積を計算してみます。

# In[3]

# 下三角行列同士の積
L1*L2
\[\left[\begin{matrix}a_{11} b_{11} & 0 & 0\\a_{21} b_{11} + a_{22} b_{21} & a_{22} b_{22} & 0\\a_{31} b_{11} + a_{32} b_{21} + a_{33} b_{31} & a_{32} b_{22} + a_{33} b_{32} & a_{33} b_{33}\end{matrix}\right]\]

三角行列の行列式

行列式の定義から明らかなように (サラスの公式 を思い出してください)、三角行列の行列式は対角成分の積で表されます。たとえば上三角行列
 \[U=\left[\begin{matrix}a_{11} & a_{12} & a_{13}\\0 & a_{22} & a_{23}\\0 & 0 & a_{33}\end{matrix}\right]\]
の行列式 $\mathrm{det}\ U$ は
 \[\mathrm{det}\ U=a_{11}a_{22}a_{33}\]
となります。

三角行列の逆行列

逆行列が存在するためには行列式が非ゼロでなければなりません。上の節で見たように、三角行列の行列式は主対角成分の積なので、逆行列が存在するためには対角成分の中に $0$ が $1$ つも含まれていないことが条件となります。
 
下(上)三角行列の逆行列は下(上)三角行列であり、対角成分はもとの行列の対角成分の逆数となります。

# In[4]

# L1の逆行列
L1.inv()
\[\left[\begin{matrix}\frac{1}{a_{11}} & 0 & 0\\- \frac{a_{21}}{a_{11} a_{22}} & \frac{1}{a_{22}} & 0\\\frac{a_{11} a_{21} a_{32} – a_{11} a_{22} a_{31}}{a_{11}^{2} a_{22} a_{33}} & – \frac{a_{32}}{a_{22} a_{33}} & \frac{1}{a_{33}}\end{matrix}\right]\]

三角行列の固有値

上三角行列
 \[U=\left[\begin{matrix}a_{11} & a_{12} & a_{13}\\0 & a_{22} & a_{23}\\0 & 0 & a_{33}\end{matrix}\right]\]
の固有値を求めてみましょう。$\det (A-\lambda I)$ を計算すると
 \[\det (A-\lambda I)=(a_{11}-\lambda)(a_{22}-\lambda)(a_{33}-\lambda)\]
なので、固有方程式は
 \[(a_{11}-\lambda)(a_{22}-\lambda)(a_{33}-\lambda)=0\]
となり、これを解いて固有値 $a_{11},\ a_{22},\ a_{23}$ を得ます。すなわち、三角行列の固有値は対角成分の各要素に一致します。下三角行列についても同様です。
 
eigenvals()メソッドで L1 の固有値を求めてみます。

# In[5]

# L1の固有値
L1.eigenvals()
\[\left \{ a_{11} : 1, \quad a_{22} : 1, \quad a_{33} : 1\right \}\]

結果はディクショナリで出力されています。
key が固有値、value が対応する固有値の重解の個数です。
≫【次の記事】LU分解
≫ Pythonで学ぶ線形代数

コメント

  1. HNaito より:

    下記は誤植と思われますので、ご確認ください。
    ———————————————————
    ## numpy.tril( )
    m の → 行列 m の
    誤植ではありませんが、いきなり 「m の」と出てきたときに「mって何?」と思ってしまいました。

    ## 上三角行列
    主対角成分の上→主対角成分の下
     
    ## 三角行列の固有値
    det|A-λI| → det(A-λI)  …2か所
    これをを解いて → これを解いて
    ———————————————————
    SymPyの行列生成では、Matrix()にリストで要素を与える方法しか知りませんでしたが、下記の方法であれば大きな行列も簡単に作れるので感心いたしました。

    def tri_upper(m, n, s):
    sympy.Symbol(s)
    elements = lambda i,j : sympy.var(‘{}{}{}’.format(s, i+1, j+1))
    mtx = sympy.Matrix(m, n, elements)
    ・・・・・

    • あとりえこばと より:

      ご指摘の箇所を訂正しておきました。
      いつも本当にありがとうございます。
      行列生成については、SymPy の公式ドキュメント (sympy.org) を見て、Matrix() 関数の第3引数に2変数関数を渡せることを知ったので、それを応用してみました。時々公式ドキュメントをググってみると意外な使い方を発掘できることがあります。