数値解析と線形代数では、下三角行列と上三角行列の積として行列を因数分解する下上三角行列分解(LU分解)が行われます(行列の乗算と行列の分解を参照)。この積には置換行列が含まれる場合もあります。LU分解はガウス消去法の行列形式と見なすことができます。コンピュータは通常、LU分解を使用して正方線形方程式系を解き、行列の逆行列を計算したり、行列式を計算したりする際の重要なステップでもあります。また、 LR分解(左三角行列と右三角行列に因数分解する)と呼ばれることもあります。一般行列に対するLU分解アルゴリズムは、1938年にポーランドの天文学者タデウシュ・バナヒエヴィチによって導入されました。 [ 1 ]

A を正方行列とする。LU分解とは、 Aを下三角行列Lと上三角行列Uの 2 つの因子の積として表現し、 A = LUとすることである。ゼロ除算や丸め誤差の無制限な増加を防ぐために、Aを事前に並べ替えなければ分解が不可能な場合がある。そのため、代替表現はPAQ = LUとなり、形式表記では、置換行列因子PとQ はAの行 (または列) の置換を示す。理論的には、P (またはQ ) は単位行列の行 (または列) の置換によって得られるが、実際には対応する置換がAの行 (または列) に直接適用される。
n辺の行列Aは2 つの三角行列を組み合わせた場合、係数はn ( n + 1)個となり、したがって行列LUのn個の係数は独立ではありません。慣例として、 L は単三角行列、つまりn個の主対角要素すべてが 1 となるように設定されています。ただし、代わりにU行列を単三角行列に設定すると、行列積の転置後の手順は同じになります(行列転置の性質を参照)。 転置後、UTはBの 下三角行列、LTは上三角行列の単位三角因子となります。これはまた、転置行列の行に対する操作(ピボット操作など)が列に対する操作と等価であることを示しており、一般に行アルゴリズムと列アルゴリズムの選択に利点はありません。
下三角行列では、主対角線より上の要素はすべてゼロであり、上三角行列では、主対角線より下の要素はすべてゼロです。例えば、3×3行列Aの場合、そのLU分解は次のようになります。
行列に適切な順序や順列がない場合、因数分解が実現しない可能性があります。たとえば、(行列の乗算を展開することで)簡単に確認できます。。 もしそうすれば、少なくとも以下のいずれかそしてはゼロでなければならず、これはLまたはUが特異行列であることを意味します。Aが非特異行列(可逆行列)である場合、これは不可能です。演算の観点から、 Aの最初の列の残りの要素をゼロにする/消去するには、次の除算が必要です。と0 の場合は不可能です。これは手続き上の問題です。行列Aの行を並べ替えて、置換行列の最初の要素がゼロ以外になるようにすることで、この問題は解消できます。後続の因数分解ステップにおける同じ問題も同様の方法で解消できます。丸め誤差や小さな数による除算に対する数値的安定性を確保するには、選択することが重要です。絶対値が大きい(ピボット操作を参照)。
上記の3 × 3行列の例では、関連する行列の最上行と最左端の列の行列積がLUの成功に特別な役割を果たすことが示されています。行列の連続バージョンを次のようにマークします。そして、行列積を書いてみましょうこれらの行と列が他の行と列から分離されるようにします。その際、ブロック行列表記を使用します。例えば、は普通の数です。は行ベクトルであり、は列ベクトルであり、は行列のサブ行列です一番上の行と一番左の列を除いて。そうすれば置き換えることができます ブロック行列積を用いる。つまり、行列ブロックは、通常の数値のように、つまり行×列のように乗算できることがわかる。ただし、この場合、ブロックの成分は部分行列であり、場合によってはスカラーまたはベクトルに縮小される。したがっては、から得られたベクトルを表します。各成分に数値を乗じた後、ベクトルの外積つまり、最初の列が次はそして、すべてのコンポーネントについて同様です。 そしては、部分行列の積です。
最初と最後の行列の等価性から、最終的な、、行列更新/置き換えられる ここで重要な観察が続きます。我々がしたのと同じように繰り返し。寸法がn × nであり、n − 1 回のステップの後、すべての列が三角形行列の副対角部分を形成するそしてすべてのピボット行と組み合わせる上三角行列を形成する必要に応じて。上記の例ではn = 3なので、 2 つのステップだけで十分です。
上記の手順は、どのステップにおいても上部の対角ピボット要素が連続する部分行列の要素はゼロになることがあります。これを避けるために、列または行を入れ替えることで、はゼロ以外になります。このような順列を含む手順は、 LUP(ピボット分解)と呼ばれます。
列の順列は行列の積に対応するどこは置換行列、すなわち単位行列である。同じ列の順列の後。すべてのステップの後、このような LUP 分解が適用されます。現在の計算スキームとCormenら[ 2 ]の類似のスキームは、再帰アルゴリズムの例である。これらはLU分解の2つの一般的な特性を示している。
再帰アルゴリズムは代数演算の面ではそれほどコストがかからないものの、各ステップでAのほとんどの要素を更新して保存する必要があるため、実用上の欠点がある。計算順序を組み替えることで、中間値の保存を不要にできることがわかるだろう。
既知の異常なケースを除き、行(または列)の適切な順列によって絶対最大ピボットa 11を選択することで、数値的に安定した LU 分解が可能になることがわかります。これは「部分ピボット付き LU 分解」(LUP) と呼ばれます。 ここで、LとUは再び下三角行列と上三角行列であり、PとQ は対応する置換行列であり、これらをAにそれぞれ左から右に掛けると、 Aの行と列が入れ替わります。すべての正方行列はこの形式で因数分解できることがわかっています[ 3 ]。また、因数分解は実際には数値的に安定しています[ 4 ] 。このため、LUP 分解は実際には有用な手法となります。
「ルークピボット」と呼ばれる変種では、各ステップでチェス盤上のルークの動きにならい、列、行、列と順に最大要素を探し、行と列の両方でピボットとなる最大要素に到達するまで探索を続けます。ランダムな要素からなる大きな行列の場合、各ステップでの演算コストは、完全ピボットの場合の行列の正方形とは異なり、部分ピボットと同様に行列の辺の長さに比例することが証明できます。
「フルピボット付きLU分解」では、行と列の両方の順列を用いて、部分行列全体における絶対最大要素を見つけます。 ここで、 L、U、Pは前述のように定義され、QはAの列を並べ替える置換行列である。[ 5 ]
「下対角線上分解」(LDU)とは、次の形式の分解である。 ここで、Dは対角行列であり、LとUは単位三角行列である。つまり、LとUの対角成分はすべて1である。
上記ではA が正方行列であることを要求しましたが、これらの分解はすべて長方形行列にも一般化できます。 [ 6 ]この場合、LとDはどちらもAと同じ行数を持つ正方行列であり、U はAとまったく同じ次元を持ちます。「上三角」とは、左上隅から始まる主対角線より下の要素がすべてゼロであることを意味します。同様に、 Uのより正確な用語は、行列Aの行階段形であるということです。
次の2×2行列を因数分解します。
この単純な行列のLU分解を求める一つの方法は、線形方程式を目視で解くことです。行列の乗算を展開すると次のようになります 。
この連立方程式は不確定です。この場合、L 行列とU行列の任意の 2 つの非ゼロ要素は解のパラメータであり、任意の非ゼロ値に設定できます。したがって、一意の LU 分解を見つけるには、L 行列とU行列に何らかの制約を設ける必要があります。たとえば、下三角行列L を単位三角行列にすることで、主対角のすべての要素を 1 に設定できます。すると、連立方程式は次の解を持ちます。
これらの値を上記のLU分解に代入すると、
任意の正方行列A はLUP および PLU 分解を許容します。[ 3 ] Aが可逆行列である場合、すべての主小行列式がゼロでない場合に限り、 LU (または LDU) 分解を許容します[ 7 ] [ 8 ] (例えばLU分解やLDU分解は許容しない)。Aがランクkの特異行列である場合、最初のk個の主小行列式がゼロでないならばLU分解を許容するが、その逆は真ではない。[ 9 ]
正方行列で可逆行列が LDU 分解 ( LとUのすべての対角要素が1に等しい) を持つ場合、分解は一意です。[ 8 ]この場合、 LまたはUのいずれかの対角要素が 1 で構成されていることを要求すれば、LU 分解も一意になります。
一般に、任意の正方行列A n × n は、以下のいずれかを持つ可能性があります。
ケース3では、対角成分a ijをa ij ± εに 変更することで、主小行列式がゼロになるのを回避し、LU分解を近似することができる。 [ 10 ]
Aが対称行列(Aが複素数の場合はエルミート行列)の正定値行列である場合、 UがLの共役転置行列となるように配置することができます。つまり、Aを次のように 書くことができます。
この分解はコレスキー分解と呼ばれます。行列Aが正定値行列であれば、コレスキー分解は存在し、一意です。さらに、コレスキー分解の計算は、他のLU分解の計算よりも効率的で、数値的にも安定しています。
任意の体上の(必ずしも可逆ではない)行列に対して、LU分解を持つための正確な必要十分条件が知られています。これらの条件は、特定のサブ行列のランクで表されます。LU分解を得るためのガウス消去アルゴリズムも、この最も一般的なケースに拡張されています。[ 11 ]
LDU分解が存在し、かつ一意である場合、元の行列Aの特定のサブ行列の行列式の比を用いて、 L、D、Uの要素を表す閉じた(明示的な)式が存在する。[ 12 ]特に、D1 = A1,1であり、 i = 2, ..., nに対して、Diはi番目の主サブ行列と( i -1)番目の主サブ行列の比である。行列式の計算は計算コストが高いため、この明示的な式は実際には使用されない。
以下のアルゴリズムは、基本的にガウス消去法の修正版です。このアルゴリズムを使用してLU分解を計算するには、低次の項を無視して、2/3 n 3回の浮動小数点演算が必要です。部分ピボットでは2次項のみが追加されますが、これは完全ピボットでは当てはまりません。[ 13 ]
N × N行列が与えられた場合、定義するは、行列Aの元の未変更バージョンです。行列Aの括弧付き上付き文字 (例: (0) )は、行列のバージョンです。行列A ( n )は、最初のn列についてガウス消去法によって主対角線より下の要素が既に 0 に消去されたA行列です。
以下は、表記法を覚えるのに役立つ行列です(各∗ は行列内の任意の実数を表します)。
この過程で、行操作を用いて行列Aを徐々に変更し、主対角線より下の要素がすべてゼロになる行列Uにします。この過程で、 PA = LUとなるような2 つの別々の行列PとLを同時に作成します。
最終的な置換行列Pは、行列Aと同じ行がすべて同じ順序で入れ替わった単位行列であり、行列Uに変換されるときに同じ行が入れ替わっているものと定義します。行列A ( n −1)の場合、まず行を入れ替えて、 n番目の列に必要な条件を満たすようにします。たとえば、行を入れ替えて部分的なピボット操作を実行したり、主対角線上のピボット要素a n,n をゼロ以外の値に設定してガウス消去を完了できるようにしたりできます。
行列A ( n −1)に対して、以下のすべての要素を設定したい。ゼロに(は主対角線のn列目の要素です。以下では各要素を次のように表記します。として(ここでi = n +1, ... , N)。設定するにはゼロにするために、各行iに対して行i =行i − ( ℓ i,n )⋅行nを設定します。この操作では、最初のN − 1列に対して行操作を実行すると、上三角行列A ( N −1 )が得られ、これはUで表されます。
また、以下の式を用いて、以前に計算したℓ i,nの値を直接入力することで、Lと表記される下三角行列を作成することもできます。
行列が与えられた場合 部分ピボット操作を実行することを選択し、最初の行と2番目の行を交換することで、行列Aと行列 Pの最初の反復がそれぞれ次のようになります。 行を入れ替えたら、次の操作を実行することで、最初の列の主対角線より下の要素を削除できます。 そのため、 これらの行を差し引くと、A (1)から行列 が導出されます。
部分ピボット操作を実行するため、導出された行列の2行目と3行目をそれぞれ交換し、現在のP行列のバージョンを取得して、 次に、行3 =行3 − ( ℓ 3,2 )⋅行2 を実行することにより、2 列目の主対角線より下の要素を削除します。ここで、 ℓ 3,2 = 5 / 6 です。この行減算後の現在のAの反復では主対角線より下に非ゼロの要素が存在しないため、この行減算によって最終的なA行列 ( Uと表記) と最終的なP行列が得られます。 対応する行を入れ替えると、最終的なL行列が得られます。
これらの行列は、 PA = LUという関係を持つ。
このプロセス中に行を全く交換しなかった場合、各列nに対して行操作を同時に実行できます。どこは、 n番目の列が転置ベクトル(0 ⋯ 0 1 − ℓ n +1, n ⋯ − ℓ N , n ) Tに置き換えられたN × N単位行列です。
言い換えれば、下三角行列
最初のN − 1列のすべての行操作を実行するには、この式は、分解を求めることと同等です。 A = LA ( N −1) = LUとなるように、L = L 1 ⋯ L N −1 と表します。
それでは、L 1 ⋯ L N −1のシーケンスを計算してみましょう。L i は次の式で表されることがわかっています。
主対角線に1を持つ2つの下三角行列があり、どちらの行列も主対角線より下の同じ列に非ゼロの要素を持たない場合、同じ位置にあるすべての非ゼロの要素を2つの行列の積に含めることができます。例:
最後に、L 1を掛け合わせて、融合行列Lを生成します(前述のとおり)。行列Lを使用すると、A = LUが得られます。
このアルゴリズムが機能するためには、各ステップで( ℓ i,nの定義を参照)。この仮定が途中で破綻した場合は、続行する前にn番目の行をその下の別の行と交換する必要があります。これが、一般に LU 分解がP −1 A = LUのようになる理由です。

バナキエヴィッチ (1938) の LU 分解アルゴリズムはプログラム可能な電子計算機の登場に先立っていましたが、インデックスの交換、転置、列ごとの乗算はほとんどのプログラミング言語のネイティブな組み込み機能であり、実際の実行の遅延がほとんどなくコンパイラのみで処理されるため、コードに直接実装できる状態でした。バナキエヴィッチが使用した独特の行列表記により、行列を列ごとに乗算することができ、定規を次の行にスライドさせることで連続する因数を明らかにすることができたため、機械計算には便利な機能でした。しかし、人間の読者にとっては、彼の方程式は標準的な行列表記に変換するのが最適です。完全な行列Aから三角行列UとL の計算を取得するには、まずAの最上行と最左端の列をそれぞれ行列UとLの対応する位置にコピーします。Lの既知の単位対角要素は、プロセス全体を通して保存も使用もされません。次の計算は、 Aの右下隅まで次の行と列に対して続きます。
この図は、前の段階が既に完了していると仮定した場合の、3行目と3列目の計算を示しています。関係する行列は、その内容を示す四角の上に名前が付けられています。行列の積と減算は、太い枠で囲まれたボックス内の要素にのみ適用されます。緑色で塗りつぶされた細い枠で囲まれたボックスは、前の段階で既にわかっている値を示します。青色のボックスは、U行列とL行列で結果を格納する場所を示します。各段階で、 L行列の結果要素を、U行列の主対角線上の対応するピボット要素で割る必要があることに注意してください。これは、 L行列の左端の列にも適用されます。
第3段階の完了後、行列Aの関連要素は使用されなくなり、前の段階の要素も同様に使用されなくなることに注意してください。これにより、これらの要素をUとLの結果値で置き換えることが可能になります。つまり、LU分解をその場で実行し、 Lの単位対角線を除くA全体をUとLで置き換えます。バナキエヴィッチLUアルゴリズムは、 Uの新たに計算された行から絶対最大ピボットを選択し、その後列を交換して主対角線上に配置することで、部分ピボットに適しています。詳細は、添付のFortran90コードを参照してください。
すべての部分ピボットLUアルゴリズムは、ほぼ同じオーダーのコストがかかります。演算。ここでnはAの行数または列数です。
この手順で得られる分解はドゥーリトル分解であることに注意してください。つまり、 Lの主対角線はすべて 1 で構成されています。行の倍数を追加して対角線より上の要素を削除するのではなく、列の倍数を追加して主対角線より上の要素を削除すると、 Uの主対角線がすべて 1 であるクラウト分解が得られます。
与えられた行列Aの Crout 分解を生成するもう 1 つの (同等の) 方法は、 Aの転置行列の Doolittle 分解を取得することです。実際、このセクションで紹介したアルゴリズムによってA T = L 0 U 0が LU 分解である場合、L = U T 0およびU = L T 0とすることで、 A = LUが Crout 分解であることがわかります。
ランダム化アルゴリズムを使用して、LU分解の低ランク近似を見つけることが可能です。入力行列Aと目的の低ランクkが与えられると、ランダム化 LU は、それぞれm × kとk × nのサイズの順列行列P、Qと下/上台形行列L、Uを返します。高い確率で‖ PAQ − LU ‖ 2 ≤ Cσ k +1となります。ここで、Cはアルゴリズムのパラメータに依存する定数であり、σ k +1は入力行列Aの( k +1)番目の特異値です。[ 14 ]
n次行列 2 つを時間M ( n )で乗算できる場合(ただし、M ( n ) ≥ n a (a > 2の場合))、LU 分解は時間O( M ( n ))で計算できます。[ 15 ]これは、例えば、Coppersmith–Winograd アルゴリズムに基づくO( n 2.376 )アルゴリズムが存在することを意味します。詳細については、高速行列乗算アルゴリズムの記事も参照してください。
大規模な疎行列を因数分解するための特別なアルゴリズムが開発されている。これらのアルゴリズムは、疎な因子LとUを見つけようとする。理想的には、計算コストは行列のサイズではなく、非ゼロ要素の数によって決まる。
これらのアルゴリズムは、行と列を入れ替える自由度を利用して、フィルイン(アルゴリズムの実行中に初期値がゼロからゼロ以外の値に変化するエントリ)を最小限に抑えます。
フィルインを最小化する順序付けの一般的な扱いは、グラフ理論を用いて扱うことができる。
行列形式の線形方程式系が与えられた場合
Aとbが与えられたとき、xに関する方程式を解きたい。AのLUP分解がPA = LUとなるように既に得られていると仮定すると、LU x = P bとなる。
この場合、解決策は2つの論理的なステップで実行されます。
どちらの場合も、三角行列( LとU )を扱っており、ガウス消去法を用いなくても、前方代入と後方代入によって直接解くことができます(ただし、LU分解自体を計算するには、この方法またはそれに相当する方法が必要です)。
上記の手順を繰り返し適用することで、異なるbに対して方程式を複数回解くことができます。この場合、ガウス消去法を毎回使用するよりも、行列Aの LU 分解を一度行い、異なるbに対して三角行列を解く方が高速(かつ便利)です。行列LとU は、ガウス消去法のプロセスを「符号化」していると考えることができます。
行列A のサイズがnの場合、連立一次方程式を解くコストは約 2/3 n 3 回の浮動小数点演算です。これは、ハウスホルダー反射を使用した場合に約 4/3 n 3 回の浮動小数点演算のコストがかかるQR分解に基づくアルゴリズムの 2 倍の速さです。このため、通常は LU 分解が好まれます。[ 16 ]
連立方程式を解く場合、bは通常、行列Aの高さに等しい長さのベクトルとして扱われます。しかし、行列の逆行列を求める場合、ベクトルbの代わりに、n × p行列である行列Bを用います。つまり、行列X (これもn × p行列) を求めようとしているのです。
先に示しておいたアルゴリズムと同じものを使って、行列Xの各列を解くことができます。ここで、Bがnサイズの単位行列、I n であると仮定します。したがって、結果X はAの逆行列でなければなりません。[ 17 ]
正方行列AのLUP 分解A = P −1 LUが与えられた場合、 Aの行列式は次のように簡単に計算できます。
2 番目の式は、三角行列の行列式が単にその対角成分の積であること、および置換行列の行列式が(−1) Sに等しいこと(Sは分解における行交換の数)から導かれる。
LU分解でフルピボットを使用する場合、Sを行と列の交換の総数とすると、 det( A )も上記の式の右辺と等しくなります。
同様の方法は、Pを単位行列に設定することで、LU分解にも容易に適用できる。

LU分解は、例えばラルストンが説明したように、線形方程式系の消去に関連しています。[ 18 ] N個の未知数を持つN個の線形方程式を消去法で解く方法は、古代中国ではすでに知られていました。[ 19 ]ガウス以前には、ユーラシアの多くの数学者がこの方法を実行し、完成させていましたが、この方法が学校教育レベルに追いやられると、詳細な記述を残した人はほとんどいませんでした。したがって、ガウス消去法という名前は、複雑な歴史を簡略化したものに過ぎません。
ポーランドの天文学者タデウシュ・バナヒエヴィチは1938年にLU分解を導入した。[ 20 ]バナヒエヴィチについて、ポール・ドワイヤーは次のように述べている。[ 21 ]
ガウスとドゥーリトルは、消去法を対称方程式にのみ適用したようです。エイトケン、バナキエヴィッチ、ドワイヤー、クラウトなどの最近の著者は、 非対称問題に関連して、この方法またはその変形を使用することを強調しています 。バナキエヴィッチは、基本的な問題は実際には行列分解、または彼が「分解」と呼んだものの問題であるという 点に気づきました。
—ポール・ドワイヤー著『線形計算』(1951年)
バナキエヴィッチ[ 20 ]は、行列の観点から消去法を初めて検討し、図解で示されているようにLU分解を定式化した。彼の計算は通常の行列計算に従うが、表記法は異なり、彼は1つの因子を転置して記述することを好んだ。これは、両方の行列の連続する行に定規をスライドさせることで、列ごとに機械的に乗算できるようにするためである(算術計を使用)。添え字の順序を入れ替えた彼の式は、現代の表記法では 次のようになる。
ここで、IA → A T ; x ≡ [ x 1 , ... , x n , −1 ] ; A ′ は、最後の列を追加したAを指し、xの最後の要素は−1です。 LU 因子の行と列を再帰的に計算するための行列式は、Banachiewicz の論文の残りの部分で式 (2.3) および (2.4) として与えられています。 Banachiewicz によるこの論文には、それぞれ非対称行列と対称行列のLU 因子とR T R因子の導出の両方が含まれています。 後年の出版物では、彼の名前が Cholesky 分解の再発見のみに関連付けられる傾向があるため、これらは時々混同されます。バナキエヴィチ自身は行動を起こさなかったことを責められるべきではない。なぜなら、翌年には占領軍による迫害を受け、ザクセンハウゼン強制収容所に3ヶ月間収容され、釈放後、協力者であり同房者であったアントニ・ウィルクを列車から降ろしたが、ウィルクは1週間後に疲労困憊で亡くなったからである。
Module mlu Implicit None Integer , Parameter :: SP = Kind ( 1 d0 ) ! 入出力の実精度を設定Private Public luban , lusolve Contains Subroutine luban ( a , tol , g , h , ip , condinv , detnth ) ! Banachiewicz (1938、以下 B38) による LU 分解法は、正方形 B=A^T=G^TH=LU となるような三角形 L=G^T および U=H を計算します。! 列置換 IP(:) による部分ピボットは、現代の追加機能です。! コード内では、a、g は B38 A^T および G^T に対応するため、a=gh が成り立ちます。! ! 通常の使用は正方形 A 用ですが、RHS l については既に知られています! (A|l)^T の入力は (L|y^T)^T を生成し、L^Tx=y の x は Ax=l の解です。Real ( SP ), Intent ( In ) :: a (:, :) ! 入力行列 A(m,n)、n<=m Real ( SP ), Intent ( In ) :: tol ! ゼロに近いピボットの許容値Real ( SP ), Intent ( Out ) :: g ( size ( a , dim = 1 ), size ( a , dim = 2 )) ! L(m,n) Real ( SP ), Intent ( Out ) :: h ( size ( a , dim = 2 ), size ( a , dim = 2 )) ! U(n,n) ! U 列は順列になっていることに注意してくださいReal ( SP ), Intent ( Out ) :: condinv ! 1/cond(A)、単数形のAの場合は0 Real ( SP ) 、Intent ( Out ) :: detnth ! sign*Abs(det(A))**(1/n) Integer 、Intent ( Out ) :: ip( size ( a , dim = 2 )) ! 列の順列! Integer :: k , n , j , l , isig Real ( SP ) :: tol0 , pivmax , pivmin , piv ! n = size ( a , dim = 2 ) tol0 = Max ( tol , 3._SP * epsilon ( tol0 )) ! tol=0 の場合はデフォルト値を使用! ! 長方形 A と G は、次の条件で許可されます: If ( n > size ( a , dim = 1 ) . Or . n < 1 ) Stop 91 Forall ( k = 1 : n ) ip ( k ) = k h = 0._SP g = 0._SP isig = 1 detnth = 0._SP pivmax = Maxval ( Abs ( a ( 1 , :))) pivmin = pivmax ! k = 1 、n ! Banachiewicz (1938) 式 (2.3) h ( k 、ip ( k :) ) = a ( k 、ip ( k :)) - Matmul ( g ( k 、: k - 1 ) 、h (: k - 1 、ip ( k :))) ! ! 行ピボットj = ( Maxloc ( Abs ( h ( k 、ip ( k :)))、dim = 1 ) + k - 1を見つける) If ( j /= k ) Then ! 列 j と k を入れ替えるisig = - isig ! 置換のため Det(A) の符号を変更するl = ip ( k ) ip ( k ) = ip ( j ) ip ( j ) = l End If piv = Abs ( h ( k , ip ( k ))) pivmax = Max ( piv , pivmax ) ! 条件を調整するpivmin = Min ( piv , pivmin ) If ( piv < tol0 ) Then ! 特異行列isig = 0 pivmax = 1._SP Exit Else ! ピボットの寄与を Det(A) の符号と値に考慮するIf ( h ( k , ip ( k )) < 0._SP ) isig = - isig detnth = detnth + Log ( piv ) End If ! ! 転置された Banachiewicz (1938) 式。 (2.4) g ( k + 1 :, k ) = ( a ( k + 1 :, ip ( k )) - & Matmul ( g ( k + 1 :, : k - 1 ), h (: k - 1 , ip ( k )))) / h ( k , ip ( k )) g ( k , k ) = 1._SP End Do ! detnth = isig * Exp ( detnth / n ) condinv = Abs (isig ) * pivmin / pivmax ! 以下のコメントを解除して、A(n,n) の二乗をテストします! Print *, '|AQ-LU| ',Maxval (Abs(a(:,ip(:))-Matmul(g, h(:,ip(:))))) End Subroutine luban Subroutine lusolve ( l , u , ip , x ) ! 三角係数 LU=A を使用して Ax=b システムを解きます Real ( SP ), Intent ( In ) :: l (:, :) ! 下三角行列 L(n,n) Real ( SP ), Intent ( In ) :: u (:, :) ! 上三角行列 U(n,n) Integer , Intent ( In ) :: ip (:) !列の順列 IP(n) Real ( SP ), Intent ( InOut ) :: x (:, :) ! 入力: m 個の RHS セット B(n,m)、! 出力: 対応する未知数のセット X(n,m) Integer :: n , m , i , j n = size ( ip ) m = size ( x , dim = 2 ) If ( n < 1. Or . m < 1. Or . Any ([ n , n ] /= shape ( l )). Or . Any ( shape ( l ) /= shape ( u )). Or . & n /= size ( x , dim = 1 )) Stop 91 Do i = 1 , m Do j = 1 , n x ( j , i ) = x ( j , i ) - dot_product ( x(: j - 1 , i ), l ( j ,: j - 1 )) End Do Do j = n , 1 , - 1 x ( j , i ) = ( x ( j , i ) - dot_product ( x ( j + 1 :, i ), u ( j , ip ( j + 1 :)))) / & u ( j , ip ( j )) End Do End Do End Subroutine lusolve End Module mlu/* 入力: A - 次元 N の正方行列の行へのポインタの配列* Tol - 行列が縮退に近い場合に失敗を検出するための小さな許容値* 出力: 行列 A が変更され、行列 LE と U の両方のコピーが A=(LE)+U として含まれ、P*A=L*U となります。* 置換行列は行列としてではなく、置換行列が「1」である列インデックスを含むサイズ N+1 の整数ベクトル P に格納されます。最後の要素 P[N]=S+N、* ここで S は行列式の計算に必要な行交換の数、det(P)=(-1)^S */ int LUPDecompose ( double ** A , int N , double Tol , int * P ) {int i 、j 、k 、imax ; double maxA 、* ptr 、absA ;for ( i = 0 ; i <= N ; i ++ ) P [ i ] = i ; // 単位置換行列、P[N] は N で初期化されますfor ( i = 0 ; i < N ; i ++ ) { maxA = 0.0 ; imax = i ;for ( k = i ; k < N ; k ++ ) if (( absA = fabs ( A [ k ][ i ])) > maxA ) { maxA = absA ; imax = k ; }if ( maxA < Tol ) return 0 ; //失敗、行列が縮退しているif ( imax != i ) { //ピボット P j = P [ i ]; P [ i ] = P [ imax ]; P [ imax ] = j ;// A の行をピボットしますptr = A [ i ]; A [ i ] = A [ imax ]; A [ imax ] = ptr ;//Nから始まるピボットをカウントする(行列式用)P [ N ] ++ ; }for ( j = i + 1 ; j < N ; j ++ ) { A [ j ][ i ] /= A [ i ][ i ];for ( k = i + 1 ; k < N ; k ++ ) A [ j ][ k ] -= A [ j ][ i ] * A [ i ][ k ]; } }return 1 ; // 分解完了}/* 入力: LUPDecompose で入力された A、P; b - 右辺ベクトル; N - 次元* 出力: x - A*x=b の解ベクトル*/ void LUPSolve ( double ** A , int * P , double * b , int N , double * x ) {for ( int i = 0 ; i < N ; i ++ ) { x [ i ] = b [ P [ i ]];for ( int k = 0 ; k < i ; k ++ ) x [ i ] -= A [ i ][ k ] * x [ k ]; }for ( int i = N - 1 ; i >= 0 ; i -- ) { for ( int k = i + 1 ; k < N ; k ++ ) x [ i ] -= A [ i ][ k ] * x [ k ];x [ i ] /= A [ i ][ i ]; } }/* 入力: LUPDecompose で埋められた A、P。N - 次元* 出力: IA は初期行列の逆行列です*/ void LUPInvert ( double ** A 、int * P 、int N 、double ** IA ) { for ( int j = 0 ; j < N ; j ++ ) { for ( int i = 0 ; i < N ; i ++ ) { IA [ i ][ j ] = P [ i ] == j ? 1.0 : 0.0 ;for ( int k = 0 ; k < i ; k ++ ) IA [ i ][ j ] -= A [ i ][ k ] * IA [ k ][ j ]; }for ( int i = N - 1 ; i >= 0 ; i -- ) { for ( int k = i + 1 ; k < N ; k ++ ) IA [ i ][ j ] -= A [ i ][ k ] * IA [ k ][ j ];IA [ i ][ j ] /= A [ i ][ i ]; } } }/* 入力: LUPDecompose で入力された A、P。N は次元。* 出力: 関数は初期行列の行列式を返します*/ double LUPDeterminant ( double ** A , int * P , int N ) {double det = A [ 0 ][ 0 ];for ( int i = 1 ; i < N ; i ++ ) det *= A [ i ][ i ];return ( P [ N ] - N ) % 2 == 0 ?デット: -デット; }public class SystemOfLinearEquations { public double [] SolveUsingLU ( double [,] matrix , double [] rightPart , int n ) { // 行列の分解double [,] lu = new double [ n , n ]; double sum = 0 ; for ( int i = 0 ; i < n ; i ++ ) { for ( int j = i ; j < n ; j ++ ) { sum = 0 ; for ( int k = 0 ; k < i ; k ++ ) sum += lu [ i , k ] * lu [ k , j ]; lu [ i , j ] = matrix [ i , j ] - sum ; } for ( int j = i + 1 ; j < n ; j ++ ) { sum = 0 ; for ( int k = 0 ; k < i ; k ++ ) sum += lu [ j , k ] * lu [ k , i ]; lu [ j , i ] = ( 1 / lu [ i , i ]) * ( matrix [ j , i ] - sum ); } }// lu = L+UI // Ly = b の解を求めるdouble [] y = new double [ n ]; for ( int i = 0 ; i < n ; i ++ ) { sum = 0 ; for ( int k = 0 ; k < i ; k ++ ) sum += lu [ i , k ] * y [ k ]; y [ i ] = rightPart [ i ] - sum ; } // Ux = y の解を求めるdouble [] x = new double [ n ]; for ( int i = n - 1 ; i >= 0 ; i -- ) { sum = 0 ; for ( int k = i + 1 ; k < n ; k ++ ) sum += lu [ i , k ] * x [ k ]; x [ i ] = ( 1 / lu [ i , i ]) * ( y [ i ] - sum ); } return x ; } }関数LU = LUDecompDoolittle ( A ) n =長さ( A ); LU = A ; k = 2 の場合: n i = 1 の場合: k - 1ラムダ= LU ( k , i ) / LU ( i , i ); LU ( k , i ) =ラムダ; LU ( k , i + 1 : n ) = LU ( k , i + 1 : n ) - LU ( i , i + 1 : n ) *ラムダ;終わり、終わり、終わりfunction x = SolveLinearSystem ( LU, B ) n = length ( LU ); y = zeros ( size ( B )); % Ly = B の解を求めるfor i = 1 : n y ( i ,:) = B ( i ,:) - LU ( i , 1 : i ) * y ( 1 : i ,:); end % Ux = y の解を求めるx = zeros ( size ( B )); for i = n :( - 1 ): 1 x ( i ,:) = ( y ( i ,:) - LU ( i ,( i + 1 ): n ) * x (( i + 1 ): n ,:)) / LU ( i , i ); end endA = [ 4 3 3 ; 6 3 3 ; 3 4 3 ] LU = LUDecompDoolittle ( A ) B = [ 1 2 3 ; 4 5 6 ; 7 8 9 ; 10 11 12 ] ' x = SolveLinearSystem ( LU , B ) A * x参考文献
コンピュータコード
オンラインリソース