|
Serwis Edukacyjny w I-LO w Tarnowie
Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2026 mgr Jerzy Wałaszek
|
ProblemDaną macierz kwadratową An×n należy rozłożyć na dwie macierze: trójkątną dolną Ln×n o elementach głównej przekątnej równych 1 i trójkątną górną Un×n, takich że:
|
Rozważmy macierz kwadratową A o wymiarze n×n. Załóżmy, iż jest ona iloczynem dwóch macierzy trójkątnych L i U:
An×n =
|
|
= Ln×n×Un×n = |
= |
|
× |
|
Wykorzystując wzory na mnożenie macierzy otrzymujemy dla każdego elementu ai, j macierzy A następujące równanie:
An×n = Ln×n×Un×n |
ai,j =
|
n Σ k=1 |
li,k×uk,j |
Ponieważ macierze L i U są macierzami trójkątnymi, to część ich elementów jest równa zero. Otrzymujemy stąd dwa przypadki w zależności od indeksów i oraz j:
1) i ≤ j, czyli element macierzy A ai,j leży na głównej przekątnej lub ponad nią. W takim przypadku ostatnim, niezerowym elementem w wierszu i-tym macierzy L jest element li,i. Skoro tak, to podany wzór możemy zredukować do postaci:
ai,j =
|
i Σ k=1 |
li,k×uk,j |
Dodatkowo z postaci macierzy L wiemy, iż element li,i, leżący na głównej przekątnej, jest zawsze równy 1. Stąd:
ai,j = (
|
i-1 Σ k=1 |
li,k×uk,j)+ui,j |
Wzór ten pozwala nam wyliczyć element ui,j o ile są znane poprzednie elementy macierzy L i U:
ui,j = ai,j- |
i-1 Σ k=1 |
li,k×uk,j |
2) i > j, czyli element macierzy A: ai,j leży pod główną przekątną. W tym przypadku ostatnim, niezerowym elementem w j-tej kolumnie macierzy U jest uj,j. Zatem wzór wyjściowy redukuje się do postaci:
ai,j =
|
j Σ k=1 |
li,k×uk,j |
ai,j = (
|
j-1 Σ k=1 |
li,k×uk,j)+li,j×uj, j |
Otrzymany wzór pozwala wyliczyć element li, j o ile znane są poprzednie elementy macierzy L i U:
li,j =
|
1 |
×(ai,j-
|
i-1 Σ k=1 |
li,k×uk,j) |
uj,j
|
W ten sposób doszliśmy do algorytmu Doolitle'a wyznaczania rozkładu macierzy A na iloczyn dwóch macierzy L i U (ang. LU matrix decomposition). Zasada działania tego algorytmu jest następująca:
Tworzymy dwie macierze L i U o stopniu macierzy A. Wypełniamy je zerami. W macierzy L główną przekątną wypełniamy elementami o wartości 1.
Dla j = 1, 2, …, n: wykonujemy kolejno dwie poniższe operacje:
Dla i = 1, 2, …, j: wykorzystujemy wzór z punktu 1) do wyznaczenia elementów ui, j.
Dla i = j+1, j+2, …, n: wykorzystujemy wzór z punktu 2) do wyznaczenia elementów li, j.
Przykład:
W celu zobrazowania pracy algorytmu Doolitle'a wyznaczmy wzory na rozkład LU macierzy A3×3:
A3×3 =
|
|
= L3×3×U3×3 = |
= |
|
× |
|
i = 1, i = j, zatem wyznaczamy u1,1:
u1,1 = a1,1- |
0 Σ k=1 |
l1,k×uk,1 = a1,1 |
i = 2, i > j, wyznaczamy l2,1:
l2,1 = 1/u1,1×(a2,1- |
0 Σ k=1 |
l2,k×uk,1) = 1/u1,1×a2,1 |
i = 3, i > j, wyznaczamy l3,1:
l3,1 = 1/u1,1×(a3,1- |
0 |
l3,k×uk,1) = 1/u1,1×a3,1 |
|
Σ |
||
|
k=1 |
i = 1, i < j, wyznaczamy u1,2:
u1,2 = a1,2- |
0 Σ k=1 |
l1,k×uk,2 = a1,2 |
i = 2, i = j, wyznaczamy u2,2:
u2,2 = a2,2- |
1 Σ k=1 |
l2,k×uk,2 = a2,2-l2,1×u1,2 |
i = 3, i > j, wyznaczamy l3,2:
l3,2 = 1/u2,2×(a3,2- |
1 Σ k=1 |
l3,k×uk,2) = 1/u2,2×(a3,2-l3,1×u1,2) |
i = 1, i < j, wyznaczamy u1,3:
u1,3 = a1,3- |
0 Σ k=1 |
l1,k×uk,3 = a1,3 |
i = 2, i < j, wyznaczamy u2, 3:
u2,3 = a2,3- |
1 Σ k=1 |
l2,k×uk,3 = a2,3-l2,1×u1,3 |
i = 3, i = j, wyznaczamy u3,3:
u3,3 = a3,3- |
2 Σ k=1 |
l3,k×uk,3 = a3,3-l3,1×u1,3-l3,2×u2,3 |
Dla przykładowych danych liczbowych otrzymujemy ze wzorów:
|
= |
|
× |
|
W powyższym przykładzie zwróć uwagę na następujące fakty:
Prowadzi to do wniosku, iż macierze L i U mogą zostać umieszczone w miejscu macierzy A, czyli operacja będzie wykonana in situ (łac. w miejscu) i zaoszczędzimy pamięć. W macierzy L istotna jest tylko część leżąca pod główną przekątną. Elementy przekątnej są zawsze równe 1, a elementy leżące nad przekątną są równe zero i nie musimy ich wcale zapamiętywać. Z drugiej strony w macierzy U istotna staje się tylko główna przekątna oraz elementy leżące nad nią – pozostałe elementy są równe zero i również nie musimy ich zapamiętywać. Zatem algorytm Doolitle'a może przekształcać macierz A na połączone macierze L i U:
|
→ |
→ |
|
K01: r ← false ; zakładamy porażkę K02: Dla j = 1, 2, …, n: wykonuj kroki K03…K11 K03: Jeśli |A[j,j]| < ε, ; rozkład niemożliwy, to zakończ ; gdyż dzielnik równy zero K04: Dla i = 1, 2, …, j: ; najpierw wyznaczamy w kolumnie wyrazy u wykonuj kroki K05…K07 K05: s ← 0 K06: Dla k = 1, 2, …, i-1: ; wyznaczamy sumę iloczynów wyrazów z L i U wykonuj s ← s+A[i,k]×A[k,j] K07: A[i,j] ← A[i,j]-s ; obliczamy U[i,j] K08: Dla i = j+1, j+2, …, n: wykonuj kroki K09…K11 K09: s ← 0 K10: Dla k = 1, 2, …, j-1: ; wyznaczamy sumę iloczynów wyrazów z L i U wykonuj s ← s+A[i,k]×A[k,j] K11: A[i,j] ← (A[i,j]-s):A[j,j] ; obliczamy L[i,j] K12: r ← true ; zaznaczamy sukces K13: Zakończ
|
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
| Program odczytuje ze standardowego wejścia macierz A (najpierw liczba n określająca stopień macierzy, a następnie n2 liczb dla kolejnych elementów), wyznacza jej rozkład LU za pomocą algorytmu Doolitle'a i wyświetla wynikową macierz A. |
|
Dane przykładowe (przekopiuj do schowka i
wklej do okna konsoli):
3 1 4 7 2 5 8 3 6 9 |
Pascal// Rozkład LU algorytmem Doolitle'a
// Data: 21.03.2010
// (C)2020 mgr Jerzy Wałaszek
//-----------------------------
program ludoolitle;
type
NReal = array of double; // typ tablicy wierszy
MReal = array of NReal; // typ tablicy wskaźników
const
eps = 1e-12;
// Funkcja realizuje algorytm Doolitle'a rozkładu LU
//--------------------------------------------------
function Doolitle(n : integer; A : MReal) : boolean;
var
i, j, k : integer;
s : double;
begin
for j := 0 to n-1 do
begin
if abs(A[j][j]) < eps then Exit(false);
for i := 0 to j do
begin
s := 0;
for k := 0 to i-1 do
s := s+A[i][k]*A[k][j];
A[i][j] := A[i][j]-s;
end;
for i := j+1 to n-1 do
begin
s := 0;
for k := 0 to j-1 do
s := s+A[i][k]*A[k][j];
A[i][j] := (A[i][j]-s)/A[j][j];
end;
end;
Doolitle := true;
end;
// Program główny
var
A : MReal;
n, i, j : integer;
begin
// odczytujemy stopień macierzy A
read(n);
// tworzymy macierz A o odpowiednich rozmiarach
SetLength(A, n);
for i := 0 to n-1 do SetLength(A[i], n);
// odczytujemy dane dla macierzy A
for i := 0 to n-1 do
for j := 0 to n-1 do read(A[i][j]);
// wyznaczamy rozkład LU macierzy A
if Doolitle(n, A) then
for i := 0 to n-1 do
begin
for j := 0 to n-1 do
write(A[i][j]:8:3, ' ');
writeln;
end
else
writeln('DZIELNIK ZERO');
// usuwamy macierz z pamięci
for i := 0 to n-1 do
SetLength(A[i], 0);
SetLength(A, 0);
end. |
// Rozkład LU algorytmem Doolitle'a
// Data: 21.03.2010
// (C)2020 mgr Jerzy Wałaszek
//-----------------------------
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
const double eps = 1e-12;
// Funkcja realizuje algorytm
// Doolitle'a rozkładu LU
//---------------------------
bool Doolitle(int n,
double ** A)
{
int i, j, k;
double s;
for(j = 0; j < n; j++)
{
if(fabs(A[j][j]) < eps)
return false;
for(i = 0; i <= j; i++)
{
s = 0;
for(k = 0; k < i; k++)
s += A[i][k]*A[k][j];
A[i][j] -= s;
}
for(i = j+1; i < n; i++)
{
s = 0;
for(k = 0; k < j; k++)
s += A[i][k]*A[k][j];
A[i][j] = (A[i][j]-s)/A[j][j];
}
}
return true;
}
// Program główny
int main()
{
double **A;
int n, i, j;
cout << setprecision(3) << fixed;
// odczytujemy stopień macierzy A
cin >> n;
// tworzymy macierz A o odpowiednich rozmiarach
A = new double * [n];
for(i = 0; i < n; i++)
A[i] = new double[n];
// odczytujemy dane dla macierzy A
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
cin >> A[i][j];
// wyznaczamy rozkład LU macierzy A
if(Doolitle(n, A))
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
cout << setw(8) << A[i][j] << " ";
cout << endl;
}
else
cout << "DZIELNIK ZERO\n";
// usuwamy macierz z pamięci
for(i = 0; i < n; i++)
delete [] A[i];
delete [] A;
return 0;
} |
Basic' Rozkład LU algorytmem Doolitle'a
' Data: 21.03.2010
' (C)2020 mgr Jerzy Wałaszek
'-----------------------------
Const eps As Double = 1e-12
' Funkcja realizuje algorytm
' Doolitle'a rozkładu LU
'---------------------------
Function doolitle(n As Integer, _
A As Double Ptr Ptr) _
As Integer
Dim As Integer i, j, k
Dim As Double s
For j = 0 To n-1
If Abs(A[j][j]) < eps Then
Return 0
For i = 0 To j
s = 0
For k = 0 To i-1
s += A[i][k]*A[k][j]
Next
A[i][j] -= s
Next
For i = j+1 To n-1
s = 0
For k = 0 To j-1
s += A[i][k]*A[k][j]
Next
A[i][j] = (A[i][j]-s)/A[j][j]
Next
Next
doolitle = 1
End Function
Dim As Integer i, j, n
Dim A As Double Ptr Ptr
Open Cons For Input As #1
' odczytujemy stopień macierzy A
Input #1, n
' tworzymy macierz A o odpowiednich rozmiarach
A = New Double Ptr [n]
For i = 0 To n-1
A[i] = New Double [n]
Next
' odczytujemy dane dla macierzy A
For i = 0 To n-1
For j = 0 To n-1
Input #1, A[i][j]
Next
Next
Close #1
' wyznaczamy rozkład LU macierzy A
If doolitle(n, A) = 1 Then
For i = 0 To n-1
For j = 0 To n-1
Print Using "####.### ";A[i][j];
Next
Print
Next
Else
Print "DZIELNIK ZERO"
End If
' usuwamy macierz z pamięci
For i = 0 To n-1
Delete [] A[i]
Next
Delete [] A
End |
Python
(dodatek)# Rozkład LU algorytmem Doolitle'a
# Data: 23.04.2024
# (C)2024 mgr Jerzy Wałaszek
#-----------------------------
EPS = 1e-12
# Funkcja realizuje algorytm
# Doolitle'a rozkładu LU
#---------------------------
def doolitle(a):
n = len(a)
for j in range(n):
if abs(a[j][j]) < EPS:
return False
for i in range(j+1):
s = 0
for k in range(i):
s += a[i][k]*a[k][j]
a[i][j] -= s
for i in range(j+1, n):
s = 0
for k in range(j):
s += a[i][k]*a[k][j]
a[i][j] = (a[i][j]-s)/a[j][j]
return True
# Program główny
# odczytujemy stopień macierzy A
n = int(input())
# odczytujemy macierz A
a = []
for i in range(n):
arr = input().split()
arr = [float(arr[j]) for j in range(n)]
a.append(arr)
# wyznaczamy rozkład LU macierzy A
if doolitle(a):
for i in range(n):
for j in range(n):
print("%8.3f " % a[i][j], end="")
print()
else:
print("DZIELNIK ZERO")
# usuwamy macierz z pamięci
a = None
|
| Wynik: |
3 1 4 7 2 5 8 3 6 9 1.000 4.000 7.000 2.000 -3.000 -6.000 3.000 2.000 0.000 |
W literaturze informatycznej można znaleźć prostsze rozwiązanie wyznaczania rozkładu LU macierzy. Polega ono na przechodzeniu przez kolejne elementy głównej przekątnej macierzy A od pierwszego do przedostatniego i dla każdego elementu A[k, k] wykonywaniu kolejno dwóch operacji:

Dzięki takiemu podejściu algorytm rozkładu LU znacznie się upraszcza.
K01: r ← false ; zakładamy porażkę K02: Dla k = 1, 2, …, n-1: wykonuj kroki K03…K06 K03: Jeśli |A[k, k]| < ε, ; element zerowy nie pozwoli to zakończ ; wykonać dzielenia K04: Dla i = k+1, k+2, …, n: ; normalizujemy kolumnę wykonuj: A[i, j] ← A[i, k]:A[k, k] K05: Dla i = k+1, k+2, …, n: wykonuj krok K06 K06: Dla j = k+1, k+2, …, n: ; modyfikujemy podmacierz wykonuj: A[i, j] ← A[i, j]-A[i, k]×A[k, j] K07: r ← true ; sukces K08: Zakończ
|
Uwaga: Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
| Program odczytuje ze standardowego wejścia macierz A (najpierw liczba n określająca stopień macierzy, a następnie n2 liczb dla kolejnych elementów), wyznacza jej rozkład LU i wyświetla wynikową macierz A. |
Pascal// Rozkład LU
// Data: 30.04.2010
// (C)2020 mgr Jerzy Wałaszek
//-----------------------------
program ludecomp;
type
NReal = array of double; // typ tablicy wierszy
MReal = array of NReal; // typ tablicy wskaźników
const
eps = 1e-12;
// Funkcja realizuje algorytm rozkładu LU
//---------------------------------------
function lu(n : integer;
A : MReal)
: boolean;
var
i, j, k : integer;
begin
for k := 0 to n-2 do
begin
if abs(A[k][k]) < eps then
Exit(false);
for i := k+1 to n-1 do
A[i][k] := A[i][k]/A[k][k];
for i := k+1 to n-1 do
for j := k+1 to n-1 do
A[i][j] := A[i][j]-A[i][k]*A[k][j];
end;
lu := true;
end;
// Program główny
var
A : MReal;
n, i, j : integer;
begin
// odczytujemy stopień macierzy A
read(n);
// tworzymy macierz A o odpowiednich rozmiarach
SetLength(A, n);
for i := 0 to n-1 do
SetLength(A[i], n);
// odczytujemy dane dla macierzy A
for i := 0 to n-1 do
for j := 0 to n-1 do
read(A[i][j]);
// wyznaczamy rozkład LU macierzy A
if lu(n, A) then
for i := 0 to n-1 do
begin
for j := 0 to n-1 do
write(A[i][j]:8:3, ' ');
writeln;
end
else
writeln('DZIELNIK ZERO');
// usuwamy macierz z pamięci
for i := 0 to n-1 do
SetLength(A[i], 0);
SetLength(A, 0);
end. |
// Rozkład LU
// Data: 30.04.2010
// (C)2020 mgr Jerzy Wałaszek
//-----------------------------
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
const double eps = 1e-12;
// Funkcja realizuje algorytm rozkładu LU
//---------------------------------------
bool lu(int n, double ** A)
{
int i, j, k;
for(k = 0; k < n-1; k++)
{
if(fabs(A[k][k]) < eps)
return false;
for(i = k+1; i < n; i++)
A[i][k] /= A[k][k];
for(i = k+1; i < n; i++)
for(j = k+1; j < n; j++)
A[i][j] -= A[i][k]*A[k][j];
}
return true;
}
// Program główny
int main()
{
double **A;
int n, i, j;
cout << setprecision(3) << fixed;
// odczytujemy stopień macierzy A
cin >> n;
// tworzymy macierz A
// o odpowiednich rozmiarach
A = new double * [n];
for(i = 0; i < n; i++)
A[i] = new double[n];
// odczytujemy dane dla macierzy A
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
cin >> A[i][j];
// wyznaczamy rozkład LU macierzy A
if(lu(n, A))
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
cout << setw(8) << A[i][j] << " ";
cout << endl;
}
else
cout << "DZIELNIK ZERO\n";
// usuwamy macierz z pamięci
for(i = 0; i < n; i++)
delete [] A[i];
delete [] A;
return 0;
} |
Basic' Rozkład LU
' Data: 30.04.2010
' (C)2020 mgr Jerzy Wałaszek
'-----------------------------
Const eps As Double = 1e-12
' Funkcja realizuje algorytm rozkładu LU
'---------------------------------------
Function lu(n As Integer, _
A As Double Ptr Ptr) _
As Integer
Dim As Integer i, j, k
For k = 0 To n-2
If Abs(A[k][k]) < eps Then _
Return 0
For i = k+1 To n-1
A[i][k] /= A[k][k]
Next
For i = k+1 To n-1
For j = k+1 To n-1
A[i][j] -= A[i][k]*A[k][j]
Next
Next
Next
lu = 1
End Function
Dim A As Double Ptr Ptr
Dim As Integer i, j, n
Open Cons For Input As #1
' odczytujemy stopień macierzy A
Input #1, n
' tworzymy macierz A
' o odpowiednich rozmiarach
A = New Double Ptr[n]
For i = 0 To n-1
A[i] = New Double[n]
Next
' odczytujemy dane dla macierzy A
For i = 0 To n-1
For j = 0 To n-1
Input #1, A[i][j]
Next
Next
Close #1
' wyznaczamy rozkład LU macierzy A
If lu(n, A) = 1 Then
For i = 0 To n-1
For j = 0 To n-1
Print Using "####.### ";A[i][j];
Next
Print
Next
Else
Print "DZIELNIK ZERO"
End If
' Usuwamy macierz A
For i = 0 To n-1
Delete [] A[i]
Next
Delete [] A
End |
Python
(dodatek)# Rozkład LU
# Data: 23.04.2024
# (C)2024 mgr Jerzy Wałaszek
#---------------------------
EPS = 1e-12
# Funkcja realizuje algorytm rozkładu LU
#---------------------------------------
def lu(a):
n = len(a)
for k in range(n-1):
if abs(a[k][k]) < EPS:
return False
for i in range(k+1, n):
a[i][k] /= a[k][k]
for i in range(k+1, n):
for j in range(k+1, n):
a[i][j] -= a[i][k]*a[k][j]
return True
# Program główny
#---------------
# odczytujemy stopień macierzy A
n = int(input())
# odczytujemy macierz A
a = []
for i in range(n):
arr = input().split()
arr = [float(arr[j]) for j in range(n)]
a.append(arr)
# wyznaczamy rozkład LU macierzy A
if lu(a):
for i in range(n):
for j in range(n):
print("%8.3f" % a[i][j], end=" ")
print()
else:
print("DZIELNIK ZERO")
# usuwamy macierz z pamięci
a = None
|
![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2026 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.