Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

obrazek

Autor artykułu: mgr Jerzy Wałaszek

©2026 mgr Jerzy Wałaszek

obrazek

Równania

Rozkład LU

SPIS TREŚCI REMANENT
Podrozdziały
 

Definicje

Macierz trójkątna (ang. triangular matrix) jest specjalnym rodzajem macierzy kwadratowej. Występuje ona w dwóch odmianach:

Macierz trójkątna dolna (ang. lower triangular matrix, L) posiada wszystkie wyrazy leżące ponad główną przekątna równe zero:

Macierz trójkątna górna (ang. upper triangular matrix, U) posiada wszystkie wyrazy leżące poniżej przekątnej głównej równe zero:

Rozkład LU (ang. LU decomposition) polega na rozłożeniu macierzy kwadratowej A na iloczyn dwóch macierzy trójkątnych, dolnej L o wyrazach przekątnej głównej równych 1 oraz górnej U:





Zgodnie z własnościami wyznaczników, wyznacznik iloczynu macierzy jest równy iloczynowi ich wyznaczników:

Z kolei wyznacznik macierzy trójkątnej jest równy iloczynowi wyrazów na przekątnej głównej, co wynika bezpośrednio z rozwinięcia Laplace'a. Zatem:

Wyznacznik macierzy A jest równy wyznacznikowi macierzy U. Z kolei wyznacznik macierzy U, która jest macierzą trójkątną, jest równy iloczynowi wyrazów na głównej przekątnej macierzy U:

I ostatecznie:

Wynika z tego, iż jeśli uda nam się znaleźć macierz U dla macierzy A, to wyznacznik macierzy A policzymy w prosty sposób przez wymnożenie przez siebie wyrazów głównej przekątnej macierzy U. Zajmijmy się zatem sposobem wyznaczenia macierzy U.


do podrozdziału  do strony 

Rozkład LU Doolitle'a

Ponieważ iloczyn macierzy L i Umusi dać w wyniku macierz A, to każdy wyraz macierzy A jest sumą iloczynów wyrazów z odpowiedniego wiersza macierzy L i odpowiedniej kolumny macierzy U:

Na przykład wyraz a11 jest sumą iloczynów kolejnych wyrazów z wiersza nr 1 macierzy L przez kolejne wyrazy z kolumny nr 1 macierzy U:

gdzie n jest stopniem macierzy.

Ponieważ obie macierze L i U są macierzami trójkątnymi, to część ich elementów ma wartość zero. Np. w macierzy L w wierszu nr 1 tylko pierwszy wyraz jest niezerowy i równy 1, pozostałe wyrazy tego wiersza są równe 0, zatem:

Pozwala to wyznaczyć pierwszy wyraz macierzy U, który jest taki sam jak wyraz macierzy A.

Rozważmy teraz dwa możliwe przypadki dla wyrazu aij:

Indeksy: i ≤ j

Wyraz aij leży na głównej przekątnej macierzy A lub ponad nią. Ostatnim niezerowym elementem w macierzy L w wierszu i-tym jest lii. Wzór iloczynowy możemy więc zredukować:

Co więcej, element lii jest elementem przekątnej głównej macierzy L i ma wartość 1, zatem:

Wzór ten pozwala wyznaczyć element uij, jeśli znamy poprzednie elementy macierzy L i U:


indeksy: i > j

Wyraz aij leży pod główną przekątną macierzy A. Ostatnim potencjalnie niezerowym elementem w kolumnie j-tej w macierzy U jest element ujj. Wszystkie pozostałe elementy macierzy U w tej kolumnie mają wartość zero. Wzór iloczynowy redukuje się do:

Wyprowadźmy poza sumę jej ostatni wyraz:

Wzór ten pozwala wyliczyć wyraz lij macierzy L, jeśli znane są pozostałe wyrazy macierzy L i U:

Otrzymaliśmy wzory na elementy obu macierzy L i U rozkładu LU macierzy A. Wzory te wykorzystuje znany algorytm Doolitle'a, który wygląda następująco:

Tworzymy dwie macierze L i U o stopniu n macierzy A. Wypełniamy je zerami. W macierzy L główną przekątną wypełniamy elementami o wartości 1.

Dla j od 1 do n wykonujemy dwie poniższe operacje:

Dla i od 1 do j wyznaczamy uij wg wzoru:

Dla i od j = 1 do n wyznaczamy lij wg wzoru:

Uwaga: obliczenia powinny być wykonywane na liczbach zmiennoprzecinkowych.

Przyjrzyj się drugiemu ze wzorów w algorytmie Doolitle'a. Występuje tutaj dzielenie przez element ujj. Jeśli ujj = 0, dzielenie jest niewykonalne i nie można wyznaczyć rozkładu LU macierzy A. Problemem tym zajmiemy się dalej w rozdziale.

Metoda Doolitle'a ma złożoność obliczeniową klasy O(n3), co sprawia iż nadaje się ona do obliczeń wyznaczników nawet dużych macierzy.

Poniższy program wyznacza rozkład LU macierzy wejściowej A, a następnie oblicza jej wyznacznik mnożąc przez siebie elementy głównej przekątnej macierzy U. Macierz A przekazywana jest przez standardowe wejście w następującej postaci:

pierwsza liczba określa stopień macierzy, następne n × n liczb to zawartość kolejnych wierszy macierzy A. Program wyświetla odczytaną macierz oraz wartość jej wyznacznika.

Przykładowe dane (przekopiuj je do schowka i wklej w programie):

Macierz:

Dane wejściowe:

5
5 3 7 4 2
9 2 2 1 1
3 6 2 8 9
9 4 -2 -1 -3
0 5 3 -6 -11
C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne 0061
//---------------------------

#include <iostream>
#include <windows.h>
#include <iomanip>

using namespace std;

// Definicja klasy,
// może być w pliku
// nagłówkowym
//-----------------

#ifndef _matrix_class
  #define _matrix_class

template <class T>
          class matrix
{
  private:
    // Wyrazy macierzy
    T * A;
  public:
    // Stopień macierzy
    int n;
    // Konstruktor
    matrix();
    // Konstruktor
    matrix(int nn);
    // Destruktor
    ~matrix();
    T  getv(int r, int c);
    void setv(int r, int c, T v);
    // ustawia elementy
    // przekątnej na 1
    void set1();
    // Oblicza wyznacznik
    T det();
};

// Konstruktor #1 tworzy
// macierz ze strumienia cin
//--------------------------
template <class T>
         matrix<T>::matrix()
{
  // Stopień macierzy
  cin >> n;
  // wielkość macierzy
  int nn = n * n;
  // Rezerwujemy pamięć
  // na macierz n x n
  A = new T [nn];

  // Wczytujemy wyrazy macierzy
  for(int i = 0; i < nn; i++)
    cin >> A[i];
}

// Konstruktor #2 tworzy macierz
// stopnia n i wypełnia ją zerami
//-------------------------------
template <class T>
         matrix<T>::matrix(int nn)
{
  // Stopień macierzy
  n = nn;
  nn = n * n;
  // Rezerwujemy pamięć
  // na macierz n x n
  A = new T [nn];

  for(int i = 0; i < nn; i++)
    A[i] = (T)0;
}

// Destruktor usuwa macierz
// i zwraca zajętą przez
// nią pamięć do systemu
//-------------------------
template <class T>
         matrix<T>::~matrix()
{
  delete [] A;
}

// Zwraca wartość elementu
// r - numer wiersza
// c - numer kolumny
//-------------------------
template <class T>
         T matrix<T>
         ::getv(int r, int c)
{
  return A[r * n + c];
}

// Ustawia wartość elementu
// r - numer wiersza
// c - numer kolumny
// v - wartość elementu
//-------------------------
template <class T>
         void matrix<T>
         ::setv(int r, int c, T v)
{
  A[r * n + c] = v;
}

// Ustawia przekatną na 1
//-----------------------
template <class T>
         void matrix<T>::set1()
{
  for(int i = 0; i < n; i++)
    A[i * n + i] = 1;
}

// Oblicza wyznacznik
// macierzy
template <class T>
         T matrix<T>::det()
{
  matrix<T> L(n);
  matrix<T> U(n);
  L.set1();
  int i,j,k;
  T sc;
  for(j = 0; j < n; j++)
  {
    for(i = 0; i <= j; i++)
    {
      sc = (T)0;
      for(k = 0; k < i; k++)
        sc += L.getv(i,k)
              * U.getv(k,j);
      U.setv(i,j,getv(i,j) - sc);
    }
    for(i = j + 1; i < n; i++)
    {
      sc = (T)0;
      for(k = 0; k < j; k++)
        sc += L.getv(i,k)
              * U.getv(k,j);
      if(U.getv(j,j))
        L.setv(i,j,(getv(i,j) - sc)
               / U.getv(j,j));
    }
  }
  sc = (T)1;
  for(i = 0; i < n; i++)
    sc *= U.getv(i,i);
  return sc;
}

#endif // _matrix_class

// Program główny
//---------------
int main()
{

  // Wczytujemy dane
  // dla macierzy

  SetConsoleOutputCP(CP_UTF8);
  SetConsoleCP(CP_UTF8);

  cout << setprecision(2)
       << fixed;

  cout
  << "Wpisz dane dla macierzy:\n\n";

  matrix<double> A;

  cout
  << "\nWyznacznik macierzy metodą"
     "rozkładu LU Doolitle'a\n"
     "--------------------------"
     "----------------------\n\n";
  for(int i = 0; i < A.n; i++)
  {
    for(int j = 0; j < A.n; j++)
      cout << setw(8)
           << A.getv(i,j);
    cout << endl;
  }
  cout << "\n\nWyznacznik = "
       << A.det();

  cout << endl << endl;
  system("pause");
  return 0;
}
Wynik
Wpisz dane dla macierzy:

5
5 3 7 4 2
9 2 2 1 1
3 6 2 8 9
9 4 -2 -1 -3
0 5 3 -6 -11

Wyznacznik macierzy metodą rozkładu LU Doolitle'a
-------------------------------------------------

    5.00    3.00    7.00    4.00    2.00
    9.00    2.00    2.00    1.00    1.00
    3.00    6.00    2.00    8.00    9.00
    9.00    4.00   -2.00   -1.00   -3.00
    0.00    5.00    3.00   -6.00  -11.00

Wyznacznik = -9368.00
Python (dodatek)
# Macierze
# (C)2026 mgr Jerzy Wałaszek
# Metody numeryczne 0061
#---------------------------

# Definicja klasy

class Matrix:

    # Konstruktor tworzy
    # macierz ze strumienia
    # wejściowego lub z wymiaru
    # i wypełnia ją zerami
    #--------------------------
    def __init__(self, nn=None):
        if nn is None:
            # Czytamy stopień
            # macierzy
            self.n = int(input())
            
            # Wczytujemy kolejne
            # wiersze i zmieniamy je
            # w listy liczb float
            self.a = [[float(x)
             for x in input().split()]
             for _ in range(self.n)]
        else:
            # Tworzymy macierz  
            # stopnia nn
            # i wypełniamy ją zerami
            #-----------------------
            self.n = nn
            self.a = [[0.0 
             for _ in range(self.n)]
             for _ in range(self.n)]

    # Ustawia przekatną na 1
    #-----------------------
    def set1(self):
        for i in range(self.n):
            self.a[i][i] = 1

    # Oblicza wyznacznik
    # macierzy
    def det(self):
        m_l = Matrix(self.n)
        m_u = Matrix(self.n)
        m_l.set1()
        for j in range(self.n):
            for i in range(j + 1):
                sc = 0
                for k in range(i):
                    sc += (m_l.a[i][k]
                      * m_u.a[k][j])
                m_u.a[i][j] = \
                  self.a[i][j] - sc
            for i in range(j + 1, self.n):
                sc = 0
                for k in range(j):
                    sc += (m_l.a[i][k]
                    * m_u.a[k][j])
                if m_u.a[j][j]:
                    m_l.a[i][j] = (
                    (self.a[i][j] - sc)
                    / m_u.a[j][j])
        sc = 1
        for i in range(self.n):
            sc *= m_u.a[i][i]
        return sc

# Program główny
#---------------

# Wczytujemy dane
# dla macierzy
print("Wpisz dane dla macierzy:\n")

t = Matrix()

print("Wyznacznik macierzy metodą "
      "rozkładu LU Doolitle'a\n"
      "--------------------------"
      "----------------------\n")
for i in range(t.n):
    for j in range(t.n):
        print(f"{t.a[i][j]:8.2f}",
              end="")
    print()

print(f"\nWyznacznik = {t.det():.2f}")

print()
input("Naciśnij Enter...")

Jeśli przyjrzysz się dokładnie wzorom Doolitle'a, to powinieneś zauważyć, że wyrazy macierzy A są wykorzystywane tylko jeden raz w każdym obiegu wyznaczania wyrazów macierzy L i U. Pozwala to umieścić macierze L i U w macierzy A, przez co algorytm działa w miejscu, tzn. nie zużywa dodatkowej pamięci. Jest to możliwe, ponieważ przekątna główna macierzy dolnej L nie jest używana przez algorytm. Zatem obie macierze L i U wypełnią dokładnie macierz A.

Poniższy program wykorzystuje tę koncepcję do wyliczenia wyznacznika. W przypadku próby dzielenia przez zero ustawiany jest znacznik error na true w macierzy A. Pozwala to programowi sprawdzić, czy obliczony wynik jest poprawny.

Przykładowe dane (przekopiuj je do schowka i wklej w programie) :

Macierz:

Dane wejściowe:

5
5 3 7 4 2
9 2 2 1 1
3 6 2 8 9
9 4 -2 -1 -3
0 5 3 -6 -11
Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne 0062
//---------------------------

#include <iostream>
#include <windows.h>
#include <iomanip>

using namespace std;

// Definicja klasy,
// może być w pliku
// nagłówkowym
//-----------------

#ifndef _matrix_class
  #define _matrix_class

template <class T>
         class matrix
{
  private:
    // Wyrazy macierzy
    T * A;
    T EPS;
  public:
    // Stopień macierzy
    int n;
    // Wielkość macierzy
    int s;
    // Błąd obliczenia wyznacznika
    bool error;
    matrix();  // Konstruktor
    ~matrix(); // Destruktor
    T  getv(int r, int c);
    void setv(int r, int c, T v);
    // Oblicza wyznacznik
    T det();
};

// Konstruktor tworzy macierz
// ze strumienia cin
//---------------------------
template <class T>
         matrix<T>::matrix()
{
  error = false;
  EPS = (T)0.000000001;

  // Stopień macierzy
  cin >> n;

  // Wielkość macierzy
  s = n * n;

  // Rezerwujemy pamięć
  // na macierz n x n
  A = new T [s];

  // Wczytujemy wyrazy
  // macierzy
  for(int i = 0; i < s; i++)
    cin >> A[i];
}

// Destruktor usuwa macierz
// i zwraca zajętą przez
// nią pamięć
//-------------------------
template <class T>
         matrix<T>::~matrix()
{
  delete [] A;
}

// Zwraca wartość elementu
// r - numer wiersza
// c - numer kolumny
//-------------------------
template <class T>
         T matrix<T>
         ::getv(int r, int c)
{
  return A[r * n + c];
}

// Ustawia wartość elementu
// r - numer wiersza
// c - numer kolumny
// v - wartość elementu
//-------------------------
template <class T>
         void matrix<T>
         ::setv(int r, int c, T v)
{
  A[r * n + c] = v;
}

// Oblicza wyznacznik
// macierzy
template <class T>
         T matrix<T>::det()
{
  int i,j,k;
  T sc;
  error = false;
  for(j = 0; j < n; j++)
  {
    if((getv(j,j) > - EPS) &&
       (getv(j,j) < EPS))
    {
      // Błąd w obliczaniu
      // wyznacznika
      error = true;
      return 0;
    }
    for(i = 0; i <= j; i++)
    {
      sc = (T)0;
      for(k = 0; k < i; k++)
        sc += getv(i,k)* getv(k,j);
      setv(i,j,getv(i,j) - sc);
    }
    for(i = j + 1; i < n; i++)
    {
      sc = (T)0;
      for(k = 0; k < j; k++)
        sc += getv(i,k) * getv(k,j);
      setv(i,j,(getv(i,j) - sc) /
           getv(j,j));
    }
  }
  sc = (T)1;
  for(i = 0; i < n; i++)
    sc *= getv(i,i);
  return sc;
}

#endif // _matrix_class

// Program główny
//---------------
int main()
{
  // Wczytujemy dane
  // dla macierzy

  SetConsoleOutputCP(CP_UTF8);
  SetConsoleCP(CP_UTF8);

  cout << setprecision(2)
       << fixed;

  cout << "Wpisz dane dla macierzy:"
       << endl << endl;

  matrix<double> A;

  cout << endl
       << "Wyznacznik macierzy metodą "
          "rozkładu LU Doolitle'a "
          "w miejscu\n"
          "---------------------------"
          "-----------------------"
          "---------\n\n";
  for(int i = 0; i < A.n; i++)
  {
    for(int j = 0; j < A.n; j++)
      cout << setw(8) << A.getv(i,j);
    cout << endl;
  }

  cout << "\nWyznacznik = " << A.det();

  if(A.error)
    cout << " - BŁĄD W OBLICZENIACH !!!";

  cout << endl << endl;
  system("pause");
  return 0;
}
Wynik
Wpisz dane dla macierzy:

5
5 3 7 4 2
9 2 2 1 1
3 6 2 8 9
9 4 -2 -1 -3
0 5 3 -6 -11

Wyznacznik macierzy metodą rozkładu LU Doolitle'a w miejscu
-----------------------------------------------------------

    5.00    3.00    7.00    4.00    2.00
    9.00    2.00    2.00    1.00    1.00
    3.00    6.00    2.00    8.00    9.00
    9.00    4.00   -2.00   -1.00   -3.00
    0.00    5.00    3.00   -6.00  -11.00

Wyznacznik = -9368.00
Python (dodatek)
# Macierze
# (C)2026 mgr Jerzy Wałaszek
# Metody numeryczne 0062
#---------------------------

# Definicja klasy,
#-----------------
class Matrix:
        
    # Konstruktor tworzy
    # macierz ze strumienia
    # wejścia
    #----------------------
    def __init__(self):
        # Zerujemy błąd
        self.error = False
    
        # Dokładność
        self.EPS = 0.000000001

        # czytamy stopień macierzy
        self.n = int(input())

        # Wczytujemy kolejne
        # wiersze i zmieniamy je
        # w listy liczb float
        self.a = [[float(x)
         for x in input().split()]
         for _ in range(self.n)]

    # Oblicza wyznacznik macierzy
    def det(self):
        # Zerujemy flagę błędu
        self.error = False

        for j in range(self.n):
            if (abs(self.a[j][j]) <
                self.EPS):
                # Błąd w obliczaniu
                # wyznacznika
                self.error = True
                return 0
            for i in range(j+1):
                sc = sum(self.a[i][k]
                 * self.a[k][j]
                 for k in range(i))
                self.a[i][j] = \
                 self.a[i][j] - sc
            for i in range(j+1,self.n):
                sc = sum(self.a[i][k]
                 * self.a[k][j]
                 for k in range(j))
                self.a[i][j] = (
                 (self.a[i][j] - sc) 
                 / self.a[j][j])
        sc = 1
        for i in range(self.n):
            sc *= self.a[i][i]
        return sc

# Program główny
#---------------

# Wczytujemy dane
# dla macierzy

print("Wpisz dane dla macierzy:")
print()

t = Matrix()

print()
print("Wyznacznik macierzy metodą "
      "rozkładu LU Doolitle'a "
      "w miejscu\n"
      "---------------------------"
      "-----------------------"
      "---------\n")

# Wyświetlamy macierz
for i in range(t.n):
    for j in range(t.n):
        print(f"{t.a[i][j]:8.2f}",
              end=" ")
    print()

print(f"\nWyznacznik = {t.det():.2f}",
      end="")

if t.error:
    print(" - BŁĄD W OBLICZENIACH!!!")

print()
input("Naciśnij Enter...")

Istnieje prostsze rozwiązanie problemu 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:

  1. Normalizacji kolumny – elementy w kolumnie pod elementem akk są dzielone przez ten element.
  2. Modyfikacji podmacierzy, która zawiera wszystkie elementy macierzy A leżące poniżej i na prawo elementu przekątnej z pominięciem wiersza i kolumny zawierającego element przekątnej. Modyfikacja polega na odjęciu od każdego elementu aij tej podmacierzy iloczynu elementów aik × akj.

Dzięki takiemu podejściu algorytm rozkładu LU znacznie się upraszcza.

Poniższy program wykorzystuje ten algorytm do wyznaczenia rozkładu LU macierzy. Macierz jest wprowadzana tak samo, jak w programie poprzednim. Po dokonaniu rozkładu LU program oblicza wyznacznik macierzy i wyświetla wynik. Jeśli w trakcie pracy program napotka zerowy element na głównej przekątnej, to zakończy działanie z informacją o błędzie.

Przykładowe dane (przekopiuj je do schowka i wklej w programie) :

Macierz:

Dane wejściowe:

5
5 3 7 4 2
9 2 2 1 1
3 6 2 8 9
9 4 -2 -1 -3
0 5 3 -6 -11
Przykładowy program w języku C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne 0063
//---------------------------

#include <iostream>
#include <windows.h>
#include <iomanip>

using namespace std;

// Definicja klasy,
// może być w pliku
// nagłówkowym
//-----------------

#ifndef _matrix_class
  #define _matrix_class

template <class T>
         class matrix
{
  private:
    // Wyrazy macierzy
    T * A;
    T EPS;
  public:
    // Stopień macierzy
    int n;
    // Błąd obliczenia
    // wyznacznika
    bool error;
    // Konstruktor
    matrix();
    // Destruktor
    ~matrix();
    T getv(int r, int c);
    void setv(int r, int c, T v);
    // Oblicza wyznacznik
    T det();
};

// Konstruktor tworzy
// macierz ze strumienia
// wejścia cin
//----------------------
template <class T>
         matrix<T>::matrix()
{
  // Zerujemy błąd
  error = false;

  // Dokładność
  EPS = (T)0.000000001;

  // Stopień macierzy
  cin >> n;

  // Rozmiar macierzy
  int nn = n * n;

  // Rezerwujemy pamięć
  // na macierz n x n
  A = new T [nn];

  // Wczytujemy kolejne
  // wyrazy macierzy
  for(int i = 0; i < nn; i++)
    cin >> A[i];
}

// Destruktor usuwa macierz
// i zwraca zajętą przez
// nią pamięć
//-------------------------
template <class T>
         matrix<T>::~matrix()
{
  delete [] A;
}

// Zwraca wartość elementu
// r - numer wiersza
// c - numer kolumny
//-------------------------
template <class T>
         T matrix<T>
         ::getv(int r, int c)
{
  return A[r * n + c];
}

// Ustawia wartość elementu
// r - numer wiersza
// c - numer kolumny
// v - wartość elementu
//-------------------------
template <class T>
         void matrix<T>
         ::setv(int r, int c,
                T v)
{
  A[r * n + c] = v;
}

// Oblicza wyznacznik
// macierzy
template <class T>
         T matrix<T>::det()
{
  int i,j,k;
  T sc, akk;

  for(k = 0; k < n - 1; k++)
  {
    // Dzielnik
    akk = getv(k,k);
    if((akk > -EPS) && (akk < EPS))
    {
      error = true;
      return 0;
    }

    // Normalizujemy kolumnę
    for(i = k + 1; i < n; i++)
      setv(i,k,getv(i,k) / akk);

    // Modyfikujemy podmacierz
    for(i = k + 1; i < n; i++)
      for(j = k + 1; j < n; j++)
        setv(i,j,
             getv(i,j) - getv(i,k)
             * getv(k,j));
  }

  // Obliczamy wyznacznik;
  sc = getv(0,0);
  for(k = 1; k < n; k++)
    sc *= getv(k,k);
  return sc;
}

#endif // _matrix_class

// Program główny
//---------------
int main()
{
  SetConsoleOutputCP(CP_UTF8);
  SetConsoleCP(CP_UTF8);

  // Wczytujemy dane
  // dla macierzy
  cout << setprecision(2)
       << fixed;
  cout << "Wpisz dane dla macierzy:"
       << endl << endl;

  matrix<double> A;

  cout << endl
       << "Wyznacznik macierzy "
          "metodą normalizacji "
          "kolumn\n"
          "--------------------"
          "--------------------"
          "------\n\n";
  for(int i = 0; i < A.n; i++)
  {
    for(int j = 0; j < A.n; j++)
      cout << setw(8)<< A.getv(i,j);
    cout << endl;
  }

  cout << endl
       << "Wyznacznik = "
       << A.det();

  if(A.error)
    cout << " - BŁĄD OBLICZENIA !!!";

  cout << endl << endl;
  system("pause");
  return 0;
}
Wynik
Wpisz dane dla macierzy:

5
5 3 7 4 2
9 2 2 1 1
3 6 2 8 9
9 4 -2 -1 -3
0 5 3 -6 -11

Wyznacznik macierzy metodą normalizacji kolumn
----------------------------------------------

    5.00    3.00    7.00    4.00    2.00
    9.00    2.00    2.00    1.00    1.00
    3.00    6.00    2.00    8.00    9.00
    9.00    4.00   -2.00   -1.00   -3.00
    0.00    5.00    3.00   -6.00  -11.00

Wyznacznik = -9368.00
Python (dodatek)
# Macierze
# (C)2026 mgr Jerzy Wałaszek
# Metody numeryczne 0063
#---------------------------

# Definicja klasy
#----------------
class Matrix:

    # Konstruktor tworzy
    # macierz ze strumienia
    # wejścia
    #----------------------
    def __init__(self):
        # Zerujemy błąd
        self.error = False
    
        # Dokładność
        self.EPS = 0.000000001

        # czytamy stopień macierzy
        self.n = int(input())

        # Wczytujemy kolejne
        # wiersze i zmieniamy je
        # w listy liczb float
        self.a = [[float(x)
         for x in input().split()]
         for _ in range(self.n)]

    # Oblicza wyznacznik
    # macierzy
    def det(self):
        for k in range(self.n - 1):
            # Dzielnik
            akk = self.a[k][k]
            if abs(akk) < self.EPS:
                self.error = True
                return 0

            # Normalizujemy kolumnę
            for i in range(k+1,self.n):
                self.a[i][k] = \
                 self.a[i][k] / akk

            # Modyfikujemy podmacierz
            for i in range(k+1,self.n):
                for j in range(k+1,self.n):
                    self.a[i][j] = \
                     self.a[i][j] - \
                     self.a[i][k] * \
                     self.a[k][j]

        # Obliczamy wyznacznik
        sc = self.a[0][0]
        for k in range(1, self.n):
            sc *= self.a[k][k]
        return sc

# Program główny
#---------------

# Wczytujemy dane dla macierzy
print("Wpisz dane dla macierzy:")
print()

t = Matrix()

print()
print("Wyznacznik macierzy "
      "metodą normalizacji "
      "kolumn\n"
      "--------------------"
      "--------------------"
      "------\n")

# Wyświetlamy macierz
for i in range(t.n):
    for j in range(t.n):
        print(f"{t.a[i][j]:8.2f}",
              end="")
    print()

print()
print(f"Wyznacznik = {t.det():.2f}",
      end="")

if t.error:
    print(" - BŁĄD OBLICZENIA !!!")

print()
input("Naciśnij Enter...")

do podrozdziału  do strony 

Rozkład LU z częściowym wyborem elementu głównego

Podane powyżej metody rozkładu LU zawodzą w przypadku napotkania elementu zerowego przez który należy wykonać dzielenie. W takich przypadkach programy przerywały obliczenia. Tymczasem rozwiązanie istnieje i jest dosyć proste. Wystarczy w takim przypadku tak poprzestawiać wiersze rozkładanej macierzy, aby na jej przekątnej znalazły się wyrazy o największych modułach. Wprowadźmy pojęcie macierzy permutacji P. Jest to macierz jednostkowa o stopniu macierzy A, w której zamieniono miejscami wiersze lub kolumny. Jeśli pomnożymy macierz A przez tak zmienioną macierz permutacji P, to wynikowa macierz będzie miała przestawione wiersze lub kolumny tak samo, jak zostały one przestawione w macierzy P. Obowiązuje zasada: mnożenie P × A przestawia wiersze, A × P przestawia kolumny. Zobaczmy na przykładzie, jak to działa:

Mamy macierz A czwartego stopnia:

Tworzymy dla niej macierz permutacji P:

W takiej postaci permutacja nie zamienia miejscami wierszy/kolumn macierzy A:

Zamieńmy miejscami dwa środkowe wiersze (lub dwie środkowe kolumny, co daje identyczny wynik) w macierzy permutacji P:

Teraz mnożenia (sprawdź to) dadzą wyniki:

Pierwsze mnożenie dało w wyniku macierz A z zamienionymi dwoma środkowymi wierszami, a drugie mnożenie dało w wyniku macierz A z zamienionymi dwiema środkowymi kolumnami. Zatem macierz permutacji P ma za zadanie odpowiednio pozamieniać ze sobą miejscami wiersze lub kolumny. Taka zamiana nazywa się piwotem (ang. pivoting). Termin ten pochodzi z gry w kosza, gdzie oznacza obrót zawodnika z piłką.

Po co to wszystko? Otóż permutacje pozwolą nam tak poukładać wiersze (lub kolumny) macierzy, aby na jej przekątnej głównej znalazły się wyrazy o największym module. Jeśli macierz nie jest zdegenerowana, to jest to zawsze możliwe. Permutacje wykonujemy w trakcie wyznaczania rozkładu LU. Musimy jednak zapamiętywać ich liczbę, ponieważ każda zamiana wierszy/kolumn zmienia znak wyznacznika na przeciwny.

Praktycznie nie tworzy się macierzy P. Permutacje wierszy zapamiętujemy w wektorze wierszowym W, poprzez który odwołujemy się do wierszy macierzy A przy wyznaczaniu rozkładu LU. Podobnie postępowaliśmy w trakcie wyliczania wartości wyznacznika za pomocą rozwinięcia Laplace'a. Każdorazowo w pętli rozkładu LU wyznaczamy największy element w pierwszej kolumnie bieżącej podmacierzy i zamieniamy jego wiersz z pierwszym wierszem poprzez wektor kolumnowy. Algorytm jest następujący:

Algorytm rozkładu LU z piwotowaniem

Dane wejściowe:

ε dokładność przyrównania do zera
n stopień macierzy
A macierz podlegająca rozkładowi LU

Dane wyjściowe

A macierz zawierająca połączone macierze L  i U
W wektor wierszowy
md mnożnik do korekcji wyznacznika

Zmienne pomocnicze

i,j,k indeksy
maxw numer wiersza z elementem maksymalnym
maxe moduł elementu maksymalnego
akk element macierzy A  leżący na jej przekątnej po piwotowaniu

Lista kroków

K01: md ← 1 mnożnik korekcyjny ustawiamy na 1
K02: Dla i = 0,1,...,n – 1:
wykonuj: W [ i ] ← i
ustawiamy wektor wierszowy
K03: Dla k = 0,1,...,n – 1:
wykonuj kroki K04...K13
rozpoczynamy rozkład LU
K04:     maxw ← k wiersz z elementem maksymalnym
K05:     maxe ← | A  [ W [ k ], k ] | moduł elementu maksymalnego
K06:     Dla i = k + 1, k + 2,..., n – 1:
    wykonuj krok K07
szukamy elementu większego
K07:         Jeśli | A [W [ i ], k ] | > maxe,
        to maxw ← i
            maxe ← | A [ W [ i ], k ] |
 
K08:     Jeśli maxe ≤ ε,
    to zakończ z wynikiem false
macierz jest zdegenerowana
K09:     Jeśli maxw ≠ W [ k ],
    to md ← - md
        W [ k ] ↔ W [ maxw ]
znaleziono element maksymalny
K10:     akk ← A [ W [ k ], k ] rozkład LU
K11:     Dla i = k + 1, k + 2,...,n – 1:
    wykonuj: A [ W [ i ], k ] ← A [ W [ i ], k ] / akk
normalizacja kolumny
K12:     Dla i = k + 1,k + 2,...,n – 1:
    wykonuj krok K13
modyfikujemy podmacierz
K13:     Dla j = k + 1,k + 2,...,n – 1:
    wykonuj:
        A [ W [ i ], j ] ← A [ W [ i ], j ] - A [ W [ i ], k ] × A [ W [ k ], j ]
 
K14: Zakończ z wynikiem true  

Przy obliczaniu wyznacznika do wierszy macierzy A  należy odwoływać się poprzez wektor wierszowy W.

Podany algorytm posiada jeszcze jedną zaletę: redukuje błędy zaokrągleń, ponieważ gwarantuje dzielenie przez największy element.

Poniższy program wykorzystuje ten algorytm do wyliczania wartości wyznacznika. Jest to modyfikacja poprzedniego programu.

Macierz:

Dane wejściowe:

5
0 3 7 4 2
9 2 2 1 1
3 6 2 8 9
9 4 -2 -1 -3
0 5 3 -6 -11
C++
// Macierze
// (C)2019 mgr Jerzy Wałaszek
// Metody numeryczne 0064
//---------------------------

#include <iostream>
#include <windows.h>
#include <iomanip>

using namespace std;

// Definicja klasy,
// może być w pliku
// nagłówkowym
//-----------------

#ifndef _matrix_class
  #define _matrix_class

template <class T>
         class matrix
{
  private:
    // Wyrazy macierzy
    T * A;
    T EPS;
  public:
    // Stopień macierzy
    int n;
    // Błąd obliczania wyznacznika
    bool error;
    matrix();  // Konstruktor
    ~matrix(); // Destruktor
    T getv(int r, int c);
    void setv(int r, int c, T v);
    T absT(T x);
    T det(); // Oblicza wyznacznik
};

// Konstruktor tworzy
// macierz ze strumienia
// wejścia cin
//----------------------
template <class T>
         matrix<T>::matrix()
{
  error = false;
  EPS = (T)0.000000001;

  // Stopień macierzy
  cin >> n;

  // Wielkość macierzy
  int s = n * n;

  // Rezerwujemy pamięć
  // na macierz n x n
  A = new T [s];

  // Wczytujemy wyrazy
  // macierzy
  for(int i = 0; i < s; i++)
    cin >> A[i];
}

// Destruktor usuwa macierz
// i zwraca zajętą przez
// nią pamięć
//-------------------------
template <class T>
         matrix<T>::~matrix()
{
  delete [] A;
}

// Zwraca wartość elementu
// r - numer wiersza
// c - numer kolumny
//-------------------------
template <class T>
         T matrix<T>
         ::getv(int r, int c)
{
  return A[r * n + c];
}

// Ustawia wartość elementu
// r - numer wiersza
// c - numer kolumny
// v - wartość elementu
//-------------------------
template <class T>
         void matrix<T>
         ::setv(int r, int c, T v)
{
  A[r * n + c] = v;
}

template <class T>
         T matrix<T>::absT(T x)
{
  if(x < (T)0)
    x = - x;
  return x;
}

// Oblicza wyznacznik macierzy
template <class T>
         T matrix<T>::det()
{
  int i, j, k, * W, md, maxw;
  T sc, akk, maxe;

  // Mnożnik korekcyjny
  // dla wyznacznika
  md = 1;

  // Tworzymy wektor wierszowy
  W = new int [n];

  // Inicjujemy wektor wierszowy
  // numerami wierszy
  for(i = 0; i < n; i++)
    W[i] = i;

  // Rozkład LU
  for(k = 0; k < n; k++)
  {
    // numer wiersza
    // z elementem max
    maxw = k;
    maxe = absT(getv(W[k],k));

    // Szukamy elementu max
    for(i = k + 1; i < n; i++)
      if(absT(getv(W[i],k)) > maxe)
      {
        maxw = i;
        maxe = absT(getv(W[i],k));
      }

     // Jeśli max jest zero,
     // to macierz jest
     // zdegenerowana
     if(maxe <= EPS)
     {
       error = true;
       delete [] W;
       return 0;
     }

     // Jeśli max nie leży
     // na przekątnej,
     // zamieniamy wiersze
     if(maxw != k)
     {
       // Modyfikujemy mnożnik
       md = -md;
       swap(W[k], W[maxw]);
     }

     // Dzielnik
     akk = getv(W[k],k);

     // Normalizujemy kolumnę
     for(i = k + 1; i < n; i++)
        setv(W[i],k,getv(W[i],k)/akk);

     // Modyfikujemy podmacierz
     for(i = k + 1; i < n; i++)
       for(j = k + 1; j < n; j++)
         setv(W[i],j,getv(W[i],j)
              -getv(W[i],k)
              *getv(W[k],j));
  }

  // Obliczamy wyznacznik;
  sc = md;
  for(k = 0; k < n; k++)
    sc *= getv(W[k],k);
  delete [] W;
  return sc;
}

#endif // _matrix_class

// Program główny
//---------------

int main()
{
  SetConsoleOutputCP(CP_UTF8);
  SetConsoleCP(CP_UTF8);

  cout << setprecision(2)
       << fixed;

  // Wczytujemy dane dla macierzy
  cout << "Wpisz dane dla macierzy:"
       << endl << endl;

  matrix<double> A;

  cout
  << endl
  << "Wyznacznik metodą LU "
     "z częściowym wyborem "
     "elementu głównego\n"
     "---------------------"
     "---------------------"
     "-----------------\n\n";

  for(int i = 0; i < A.n; i++)
  {
    for(int j = 0; j < A.n; j++)
      cout << setw(8)<< A.getv(i,j);
    cout << endl;
  }

  cout << endl
       << "Wyznacznik = "
       << A.det();

  if(A.error)
    cout
    << " - BŁĄD W OBLICZENIACH !!!";

  cout << endl << endl;
  system("pause");
  return 0;
}
Wynik
Wpisz dane dla macierzy:

5
0 3 7 4 2
9 2 2 1 1
3 6 2 8 9
9 4 -2 -1 -3
0 5 3 -6 -11

Wyznacznik metodą LU z częściowym wyborem elementu głównego
-----------------------------------------------------------

    0.00    3.00    7.00    4.00    2.00
    9.00    2.00    2.00    1.00    1.00
    3.00    6.00    2.00    8.00    9.00
    9.00    4.00   -2.00   -1.00   -3.00
    0.00    5.00    3.00   -6.00  -11.00

Wyznacznik = -8598.00
Python (dodatek)
# Macierze
# (C)2026 mgr Jerzy Wałaszek
# Metody numeryczne 0064
#---------------------------

# Definicja klasy
#----------------
class Matrix:

    # Konstruktor tworzy
    # macierz ze strumienia
    # wejścia cin
    #----------------------
    def __init__(self):
        self.error = False
        self.EPS = 0.000000001

        # Czytamy stopień macierzy
        # z pierwszego wiersza
        self.n = int(input())

        # Wczytujemy n wierszy
        # i tworzymy z nich
        # macierz - listę list
        self.a = [[float(x)
         for x in input().split()]
         for _ in range(self.n)]

    # Oblicza wyznacznik
    # macierzy
    def det(self):
        # Mnożnik korekcyjny
        # dla wyznacznika
        md = 1

        # Tworzymy wektor
        # wierszowy
        w = [i for i in range(self.n)]

        # Rozkład LU
        for k in range(self.n):
            # numer wiersza
            # z elementem max
            maxw = k
            maxe = abs(self.a[w[k]][k])

            # Szukamy elementu max
            for i in range(k+1,self.n):
                if (abs(self.a[w[i]][k])
                    > maxe):
                    maxw = i
                    maxe = \
                     abs(self.a[w[i]][k])

            # Jeśli max jest zerowe,
            # to macierz jest
            # zdegenerowana
            if maxe <= self.EPS:
                # Sygnalizujemy błąd
                self.error = True
                return 0

            # Jeśli max nie leży
            # na przekątnej, to
            # zamieniamy wiersze
            if maxw != k:
                # Modyfikujemy mnożnik
                md = -md
                w[k], w[maxw] = \
                 w[maxw], w[k]

            # Dzielnik
            akk = self.a[w[k]][k]

            # Normalizujemy kolumnę
            for i in range(k+1,self.n):
                self.a[w[i]][k] /= akk

            # Modyfikujemy podmacierz
            for i in range(k+1,self.n):
                for j in range(k+1,self.n):
                    self.a[w[i]][j] -= \
                     self.a[w[i]][k] \
                     *self.a[w[k]][j]

        # Obliczamy wyznacznik;
        sc = md
        for k in range(self.n):
            sc *= self.a[w[k]][k]
    
        return sc

# Program główny
#---------------

# Wczytujemy dane
# dla macierzy
print("Wpisz dane dla macierzy:\n")

t = Matrix()

print()
print("Wyznacznik metodą LU "
      "z częściowym wyborem "
       "elementu głównego\n"
       "---------------------"
       "---------------------"
       "-----------------\n")

for i in range(t.n):
    for j in range(t.n):
        print(f"{t.a[i][j]:8.2f}",
              end="")
    print()

print()
print(f"Wyznacznik = {t.det():.2f}",
      end="")

if t.error:
    print(" - BŁĄD W OBLICZENIACH !!!")

print()
input("Naciśnij Enter...")

do podrozdziału  do strony 

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: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.