可逆行列のLU分解
📂行列代数可逆行列のLU分解
ビルドアップ
行列 A∈Rm×m が左から掛けられた時 (i,j) の成分を 0 にする行列 Eij を A の ij-消去オペレーターと定義しよう。
具体的に 正方行列 (aij)∈Rm×m に対する Eij は、対角成分が 1 であり、(i,j)-成分が −mij=−ajjaij、残りの成分が 0 で得られる。これは連立方程式の解法で同じ変数同士の係数を合わせて消去する操作を行列で表したものである。このような消去は連立方程式の解がただ一つだけ存在する時に意味があるので、我々が扱う A は可逆行列とする。
例
簡単な例として、
B:=210121012
とすると、E21B=200121012 が得られる。そして B=210121012 なので B に対する (2,1)-消去オペレーターは
E21=1−mij0010001=1−210010001
で求めることができる。一方でこのような操作を何度も行った
UB:=E32E31E21B=200120012
は上三角行列であり、E21,E31,E32 は全て下三角行列である。下三角行列同士の積は下三角行列なので LB:=E21E31E32 も下三角行列であり、L−1B=U で表せる。再び B について整理すると B=LBUB すなわち下lower三角行列と上upper三角行列の積に分解された形になる。
このような分解方法はあまりにも単純なため、A∈Rn×n の場合についても特に問題なく一般化が可能だ。連立方程式 Ax=b の解法を考えると、A が A=LU に分解されるので
LUx=y:=bUx
と置くことができる。ここで U が上三角行列なので、y は後退代入法を通じて容易に求めることができ、
L(Ux)=y=Lb
では L が下三角行列なので、b は前進代入法を通じて容易に求めることができる。従って、LU分解を実際に求めることができるということは、非常に多くの問題を非常に簡単かつ迅速に解決することができるという意味になる。
導出
今、直接的に L と U を探し出すために、A:=LU を満たす下三角行列 L と上三角行列 U を次のように定義しよう。
L:=U:=1l21l31⋮ln101l32⋮ln2001⋮ln3⋯⋯⋯⋱⋯000⋮1u1100⋮0u12u220⋮0u13u23u33⋮0⋯⋯⋯⋱⋯u1nu2nu3n⋮unn
ここで、L は消去オペレーターたちの積なので、対角成分が全て 1 であることは容易に確認できる。この定義に従って、直接 A=(aij) の成分 aij を計算してみると
aij=s=1∑nlisusj=s=1∑min(i,j)lisusj
となる。今、aij を対角線上にある場合、対角線の上にある場合、対角線の下にある場合に分けて考えてみよう。
i=j=k ならば、
akk====s=1∑min(i,j)lksusks=1∑klksusks=1∑k−1lksusk+lkkukks=1∑k−1lksusk+ukk
これを ukk に対して整理すると、
ukk=akk−s=1∑k−1lksusk
が得られる。同様に、k<j なら
akj=s=1∑k−1lksusj+ukj
を、k<i なら
aik=s=1∑k−1lisusk+likukk
を得ることができる。これをそれぞれ ukj と lik に対して整理すると次のようになる。
ukj=lik=akj−s=1∑k−1lksusjukk1{aik−s=1∑k−1lisusk}
■
アルゴリズム
(aij)∈Rn×n が可逆行列だとしよう。
ステップ 1. k=1
u1j=a1j を代入して、li1=u111ai1 を計算する。
ステップ 2. k=2,3,⋯,n−1
- ステップ 2-1. 次を計算する。
ukk=akk−s=1∑k−1lksusk
- ステップ 2-2. j=k+1,k+2,⋯,n−1 について次を計算する。
ukj=akj−s=1∑k−1lksusj
- ステップ 2-3. i=k+1,k+2,⋯,n−1 について次を計算する。
lik=ukk1{aik−s=1∑k−1lisusk}
ステップ 3. k=n について次を計算する。
unn=ann−s=1∑n−1lnsusn
- アルゴリズムをよく見ると、k が増加するにつれて、L は左から右へ、U は上から下へ計算されることが分かる。各ステップで必要な値は少なくともその前のステップで全て計算されているため、各ステップでの計算単位は、一行または一列になる。
コード
さて、上で例として挙げた行列 B を実際に分解してみよう。

以下のコードは、前述のアルゴリズムを実際にRで実装したものである。
B <- matrix(c(2,1,0,1,2,1,0,1,2), ncol= 3); B
LU<-function(A)
{
n=length(A[,1])
L<-diag(1,n)
U<-diag(0,n)
k=1
j=1
i=1
U[1,]<-A[1,]
L[,1]<-A[,1]/A[1,1]
for(k in 2:(n-1))
{
U[k,k] = A[k,k] - sum(L[k,1:(k-1)]*U[1:(k-1),k])
for(j in (k+1):n)
{
U[k,j] = A[k,j] - sum(L[k,1:(k-1)]*U[1:(k-1),j])
}
for(i in (k+1):n)
{
L[i,k] = ( A[i,k] - sum(L[i,1:(k-1)]*U[1:(k-1),k]) ) / U[k,k]
}
}
U[n,n] = A[n,n] - sum(L[n,1:(n-1)]*U[1:(n-1),n])
return(list(L=L,U=U))
}
LU(B)
require(Matrix)
expand(lu(B))
実行結果は以下の通りである。

R自体で実装されたLU分解と正確に一致することが確認できる。
一緒に見る