Die Faktorisierung
Die -Zerlegung speichert die Gauß-Elimination in zwei Dreiecksmatrizen. Mit partieller Pivotisierung wird eine invertierbare quadratische Matrix faktorisiert als
Die drei Faktoren haben folgende Aufgaben:
- eine Permutationsmatrix ist, welche die Zeilenvertauschungen erfasst
- eine untere Dreiecksmatrix mit Einsen auf der Diagonalen ist
- eine obere Dreiecksmatrix ist
Im Eliminationsschritt wählt die partielle Pivotisierung in Spalte den betragsmäßig größten Eintrag auf oder unterhalb der Diagonalen. Dadurch wird eine Division durch einen Nullpivot vermieden und das Wachstum numerischer Fehler gewöhnlich begrenzt.
Für eine Permutationsmatrix gilt . Die Gleichheit gilt nur für spezielle Permutationen, etwa eine einzelne Zeilenvertauschung. Bei einer zusammengesetzten Permutation kann sie falsch sein.
Wie die Elimination die Faktoren erzeugt
Nach der Pivotwahl in Spalte wird jeder Eintrag darunter mit dem Multiplikator
Anschließend wenden wir die folgende Zeilenaktualisierung an.
Diese Aktualisierung eliminiert den Eintrag unterhalb des Pivots. Die Multiplikatoren werden unterhalb der Diagonalen von gespeichert. Werden nach früheren Eliminationsschritten zwei Zeilen vertauscht, müssen auch die bereits gefüllten Spalten von vertauscht werden. Andernfalls gilt nicht mehr.
Eine Matrix mit partieller Pivotisierung faktorisieren
Wir verfolgen beide Zeilenvertauschungen, damit die endgültige Permutation zu den Faktoren passt.
Erster Pivot
Der größte Betrag in der ersten Spalte ist . Vertausche die erste und dritte Zeile und eliminiere dann den Eintrag der zweiten Zeile mit . Die dritte Zeile beginnt bereits mit null.
Zweiter Pivot
Der nächste Pivot hat den Betrag und steht in Zeile drei. Vertausche deshalb die zweite und dritte Zeile, einschließlich ihrer bereits gespeicherten Multiplikatoren in der ersten Spalte von . Für die letzte Zeile gilt danach .
Damit ergeben sich die Faktoren
Die Multiplikation der Faktoren bestätigt die Zerlegung:
Ein lineares System lösen
Aus wird die Gleichung zu
Wir lösen dieses System in zwei Schritten mit Dreiecksmatrizen.
Vorwärtssubstitution
Zuerst lösen wir . Hier gilt
Für eine allgemeine untere Dreiecksmatrix lautet die Rekursion
Die Diagonaleinträge dieses sind alle eins.
Rückwärtssubstitution
Anschließend lösen wir von der letzten Zeile nach oben:
Für das Beispiel erhalten wir
Direktes Einsetzen bestätigt .
Aufwand und Wiederverwendung
Eine dichte -Faktorisierung benötigt ungefähr
Gleitkommaoperationen, wenn Multiplikationen und Subtraktionen getrennt gezählt werden. Sobald die Faktoren vorliegen, benötigt jede neue rechte Seite nur Arbeit für die beiden Substitutionen. Die Wiederverwendung derselben -Faktoren vermeidet daher eine erneute Faktorisierung mit Aufwand für jede rechte Seite.
Auch die Determinante folgt aus den Faktoren:
Implementierung mit NumPy und Pivotprüfung
import numpy as np
def plu_factor(matrix, tolerance=1e-12):
"""Return P, L, U such that P @ matrix == L @ U."""
A = np.asarray(matrix)
if np.iscomplexobj(A):
raise ValueError("this example requires real entries")
A = A.astype(float)
if A.ndim != 2 or A.shape[0] != A.shape[1] or A.shape[0] == 0:
raise ValueError("PLU factorization requires a nonempty square matrix")
if not np.all(np.isfinite(A)):
raise ValueError("matrix entries must be finite")
if not np.isfinite(tolerance) or tolerance < 0:
raise ValueError("relative tolerance must be finite and nonnegative")
threshold = tolerance * np.linalg.norm(A, ord=np.inf)
n = A.shape[0]
U = A.copy()
L = np.eye(n)
P = np.eye(n)
for k in range(n - 1):
pivot = k + np.argmax(np.abs(U[k:, k]))
if abs(U[pivot, k]) <= threshold:
raise np.linalg.LinAlgError("matrix is singular to working precision")
if pivot != k:
U[[k, pivot]] = U[[pivot, k]]
P[[k, pivot]] = P[[pivot, k]]
L[[k, pivot], :k] = L[[pivot, k], :k]
for i in range(k + 1, n):
L[i, k] = U[i, k] / U[k, k]
U[i, k:] -= L[i, k] * U[k, k:]
if abs(U[-1, -1]) <= threshold:
raise np.linalg.LinAlgError("matrix is singular to working precision")
return P, L, U
def forward_substitution(L, right_hand_side):
b = np.asarray(right_hand_side, dtype=float)
y = np.empty_like(b)
for i in range(len(b)):
y[i] = (b[i] - L[i, :i] @ y[:i]) / L[i, i]
return y
def backward_substitution(U, right_hand_side, tolerance=1e-12):
y = np.asarray(right_hand_side, dtype=float)
x = np.empty_like(y)
threshold = tolerance * np.linalg.norm(U, ord=np.inf)
for i in range(len(y) - 1, -1, -1):
if abs(U[i, i]) <= threshold:
raise np.linalg.LinAlgError("singular triangular system")
x[i] = (y[i] - U[i, i + 1:] @ x[i + 1:]) / U[i, i]
return x
def plu_solve(matrix, right_hand_side):
P, L, U = plu_factor(matrix)
b = np.asarray(right_hand_side)
if np.iscomplexobj(b):
raise ValueError("this example requires a real right-hand side")
b = b.astype(float)
if b.shape != (len(P),) or not np.all(np.isfinite(b)):
raise ValueError("right-hand side must be a finite vector of matching size")
y = forward_substitution(L, P @ b)
x = backward_substitution(U, y)
return x, P, L, U
A = np.array([[0, 2, 1], [1, 1, 0], [2, 1, 1]], dtype=float)
b = np.array([7, 3, 7], dtype=float)
x, P, L, U = plu_solve(A, b)
assert np.allclose(P @ A, L @ U)
assert np.allclose(A @ x, b)
assert np.allclose(x, np.array([1, 2, 3], dtype=float))Die Prüfungen gehören zum Beispiel, weil eine falsche Faktorordnung und fehlerhaftes Verbuchen von Zeilenvertauschungen häufige Implementierungsfehler sind.
Die Pivottoleranz bezieht sich auf die Unendlichnorm der Matrix. Eine gleichmäßige Skalierung eines gut konditionierten Systems löst deshalb nicht allein wegen kleiner Einträge einen Fehler aus. Der Schwellenwert ist eine numerische Entscheidung und kein Beweis exakter Singularität.
Gültigkeitsbereich der Methode
Diese Implementierung löst nur Gleichungssysteme mit invertierbaren quadratischen Matrizen und behandelt einen numerischen Nullpivot als Fehler. Für rechteckige Ausgleichsprobleme eignet sich eine -Zerlegung. Bei rangdefizienten Problemen sind eine -Zerlegung mit Spaltenpivotisierung oder eine erforderlich. Auch für eine singuläre quadratische Matrix kann eine -Faktorisierung existieren, doch dieses Lösungsverfahren kann damit keine eindeutige Lösung bestimmen.