対称行列のLDU分解
📂行列代数対称行列のLDU分解
概要
LT は上三角行列なので、A=LU における U を U:=DLT に置き換えると考えればよい。一般的な LU 分解より条件が厳しくなる分、計算量は大幅に減る。
定理
可逆対称行列 A について A=LDLT を満たす下三角行列 L と 対角行列 D が存在する。
証明
A は可逆行列なので、A=LU を満たす下三角行列 L と 上三角行列 U が存在する。一方 A=AT なので
LU=A=AT=UTLT
LU 分解で L は対角成分がすべて 1 である下三角行列として求められるので、detL=1、すなわち逆行列 L−1 が存在する。L−1 は下三角行列であり
LU=UTLT
を U について整理すると
U=L−1UTLT
両辺の右側に (LT)−1 を掛けると
U(LT)−1=L−1UT
を得る。U(LT)−1=L−1UT の左辺は上三角行列、右辺は下三角行列なので
D:=U(LT)−1=L−1UT
は 対角行列とわかる。これを U について整理すると U=DLT となり、次を得る。
A=LU=LDLT
■
計算
さて、具体的に L と D を計算するために次のように定義してみよう。
L:=1l21l31⋮ln101l32⋮ln2001⋮ln3⋯⋯⋯⋱⋯000⋮1,D:=d1100⋮00d220⋮000d33⋮0⋯⋯⋯⋱⋯000⋮dnn
A=(aij)=LDLT を直接計算しよう。
lij>0 であり lii=1 なので
aij====ν=1∑nliνμ=1∑ndνμlμjTν=1∑nliνμ=1∑nδνμdνμljμν=1∑nliνdννljνν=1∑min(i,j)liνdννljν
まず j≤i の場合で仮定してみよう。
aij=ν=1∑jliνdννljν=ν=1∑j−1liνdννljν+lijdjjljj=ν=1∑j−1liνdννljν+lijdjj
ここで i=j=k の場合
akk=ν=1∑k−1lkνdννlkν+dkkdkk
について整理すると
dkk=akk−ν=1∑k−1dννlkν2
逆に j=k であり i=k+1,⋯,n とすると
aik=ν=1∑k−1liνdννlkν+likdkk
lik について整理すると
lik=dkk1{aik−ν=1∑k−1liνdννlkν}
アルゴリズム
(aij)∈Rn×n が可逆対称行列としよう。
**Step 1. k=1
d11=a11 を代入して li1=d111ai1 を計算する。
**Step 2. k=2,3,⋯,n−1
Step 2-1. 次を計算する。
dkk=akk−ν=1∑k−1dννlkν2
**Step 2-2. i=k+1,k+2,⋯,n−1 について次を計算する。
lik=dkk1{aik−ν=1∑k−1liνdννlkν}
**Step 3. k=n について次を計算する。
dnn=ann−ν=1∑n−1dννlnν2
- 手計算では複雑に見えたが、アルゴリズムとして整理すれば計算量は大幅に減少したことが分かる。常に D の対角成分を先に求め、次に L の値を列単位で求めていくことを覚えておこう。
コード
さて、LU 分解で行ったのと同じ例を使って LDU 分解を実際に試してみよう。

以下のコードは上記のアルゴリズムを R で実際に実装したものだ。
B <- matrix(c(2,1,0,1,2,1,0,1,2), ncol= 3); B
LDU<-function(A)
{
n=length(A[,1])
L<-diag(1,n)
D<-diag(0,n)
k=1
D[1,1] = A[1,1]
L[,1] = A[,1]/D[1,1]
for(k in 2:(n-1))
{
D[k,k] = A[k,k] - sum(diag(D)[1:(k-1)]*L[k,1:(k-1)]^2)
for(i in (k+1):n)
{
L[i,k] = ( A[i,k] - sum(L[i,1:(k-1)]*diag(D)[1:(k-1)]*L[k,1:(k-1)]) )/D[k,k]
}
}
D[n,n] = A[n,n] - sum(diag(D)[1:(n-1)]*L[n,1:(n-1)]^2)
return(list(L=L, D=D))
}
LDU(B)
LDU(B)$L%*%LDU(B)$D%*%t(LDU(B)$L)
実行結果は次の通りだ。

R には LDU 分解が実装されていないため、結果を確認しただけだが、正確に分解されていることが分かる。
B のサイズがそれほど大きくなく、計算もそれなりにできるので、疑わしい場合は直接手計算しても良い。