JuliaでLU分解するときの注意点
JuliaでLU分解を利用して連立一次方程式を数値計算するとき,枢軸選択 (pivoting) に注意する必要があります.みるべき情報はここですが,備忘録も兼ねてまとめておきます.
julia> using LinearAlgebra
julia> A = [0 0 2; 1 0 1 ; 2 -1 1]
3×3 Array{Int64,2}:
0 0 2
1 0 1
2 -1 1
julia> L, U = lu(A) # 素直にLU分解
LU{Float64,Array{Float64,2}}
L factor:
3×3 Array{Float64,2}:
1.0 0.0 0.0
0.5 1.0 0.0
0.0 0.0 1.0
U factor:
3×3 Array{Float64,2}:
2.0 -1.0 1.0
0.0 0.5 0.5
0.0 0.0 2.0
julia> L*U
3×3 Array{Float64,2}:
2.0 -1.0 1.0
1.0 0.0 1.0
0.0 0.0 2.0
L*U
がA
と一致しません.そもそもA
の (1,1) 成分が 0 ですから,通常のLU分解は初手から破綻してしまいます.では何が行われているかというと枢軸選択 (pivoting) が行われているわけです.要するに行の入れ替えが行われています.
julia> L, U, p = lu(A)
LU{Float64,Array{Float64,2}}
L factor:
3×3 Array{Float64,2}:
1.0 0.0 0.0
0.5 1.0 0.0
0.0 0.0 1.0
U factor:
3×3 Array{Float64,2}:
2.0 -1.0 1.0
0.0 0.5 0.5
0.0 0.0 2.0
julia> p # 置換を表すベクトル
3-element Array{Int64,1}:
3
2
1
julia> b = [4;3;5] # 連立一次方程式の右辺ベクトル
3-element Array{Int64,1}:
4
3
5
julia> A\b # 連立一次方程式の解
3-element Array{Float64,1}:
1.0
-1.0
2.0
julia> x1 = U\(L\b) # pivotingを無視して計算すると...
3-element Array{Float64,1}:
0.5
-0.5
2.5
julia> x2 = U\(L\b[p]) # pivotingを考慮して計算するにはこのようにすればよい
3-element Array{Float64,1}:
1.0
-1.0
2.0
置換を表す行列も簡単に取り出せます.
julia> F = lu(A);
julia> F.P
3×3 Array{Float64,2}:
0.0 0.0 1.0
0.0 1.0 0.0
1.0 0.0 0.0
julia> (F.P)*A - L*U
3×3 Array{Float64,2}:
0.0 0.0 0.0
0.0 0.0 0.0
0.0 0.0 0.0
なお,枢軸選択を無視してLU分解することも可能です.
julia> lu(A,Val(false)) # A は上の3x3行列
ERROR: ZeroPivotException: factorization encountered one or more zero pivots. Consider switching to a pivoted LU factorization.
怒られます.(1,1) 成分が 0 なのですから当然です.しかし,枢軸選択を無視してもLU分解が動く例もあります.
julia> A = [1 1; 2 1] # この行列に対して
2×2 Array{Int64,2}:
1 1
2 1
julia> L,U = lu(A) # LU分解すると
LU{Float64,Array{Float64,2}}
L factor:
2×2 Array{Float64,2}:
1.0 0.0
0.5 1.0
U factor:
2×2 Array{Float64,2}:
2.0 1.0
0.0 0.5
julia> L*U # 枢軸選択が適用されますが
2×2 Array{Float64,2}:
2.0 1.0
1.0 1.0
julia> L,U = lu(A,Val(false)) # このようにすれば,枢軸選択は適用されません
LU{Float64,Array{Float64,2}}
L factor:
2×2 Array{Float64,2}:
1.0 0.0
2.0 1.0
U factor:
2×2 Array{Float64,2}:
1.0 1.0
0.0 -1.0
julia> L*U # 実際に L*U が A に一致します
2×2 Array{Float64,2}:
1.0 1.0
2.0 1.0
あえて枢軸選択を行わないメリットはあまりないとは思いますが,例えば,matlabの素朴なLU分解と比較するときなどには lu(A,Val(false))
は役立つかもしれません.