Metody lineární algebry#

import numpy as np
import scipy.linalg as la
import matplotlib.pyplot as plt

Základní operace s maticemi#

Skalární součin#

Skalární součin dvou vektorů \( \vec{x} \in \mathbb{R}^{n} \) a \( \vec{y} \in \mathbb{R}^{n} \) je definován následovně:

\[ \vec{x} \cdot \vec{y} = \sum_{i=0}^{n-1} x_i y_i. \]
Úkol

Napište funkci, která počítá skalární součin dvou vektorů. Nepoužijte žádné knihovní funkce (np.dot() nebo operátoru @). Zkontrolujte správnost pomocí vhodné knihovní funkce.

Tip

  • Pro získání rozměrů matice nebo vektoru můžete použít x.shape, A.shape[i] (velikost v i-tém směru) nebo np.shape(A)[i].

  • Pro procházení dvou polí najednou lze použít konstrukce for (xi,yi) in zip(x,y):.

  • Nový vektor vytvářejte pomocí np.zero().

x = np.random.rand(3)
y = np.random.rand(3)
print(x,y)

def skalarni_soucin(x, y):
    ## DOPLŇTE ##
    s = 0
    for i in range(len(x)):
        s += x[i]*y[i]
    return s

print(skalarni_soucin(x,y), np.dot(x,y))
[0.20350165 0.5543405  0.8436529 ] [0.34839323 0.44640505 0.58522528]
0.8120860027740153 0.8120860027740153

Součin matice s vektorem#

Součin matice \( \mathbb{A} \in \mathbb{R}^{m \ \times \ n} \) a vektoru \( \vec{x} \in \mathbb{R}^{n} \) je definován následovně:

\[ y_i = (\mathbb{A} \cdot \vec{x})_i = \sum_{j = 0}^{n-1} a_{ij} x_j, \quad i = 0, 1, \dots, m-1. \]
Úkol

Napište funkci, která počítá součin matice s vektorem. Nepoužijte žádné knihovní funkce (np.dot() nebo operátoru @). Zkontrolujte správnost pomocí vhodné knihovní funkce. Lze využít předchozí funkci skalarni_soucin().

A = np.random.rand(3,4)
x = np.random.rand(4)
print(A,x)

def soucin_matice_vektor(A, x):
    ## DOPLŇTE ##
    y = np.zeros(shape=A.shape[0])
    for i in range(A.shape[0]):
        for j in range(A.shape[1]):
            y[i] += A[i,j]*x[j]
    return y

print(soucin_matice_vektor(A,x), np.dot(A,x))
[[0.22633532 0.44566251 0.62000639 0.54792683]
 [0.57009264 0.29031861 0.77997205 0.4865204 ]
 [0.20178447 0.84438415 0.70275164 0.92962111]] [0.92842928 0.04714665 0.65948466 0.25394788]
[0.77917739 1.18090867 0.92668173] [0.77917739 1.18090867 0.92668173]

Součin matice s maticí#

Součin dvou matic \( \mathbb{A} \in \mathbb{R}^{m \ \times \ n} \) a \( \mathbb{B} \in \mathbb{R}^{n \ \times \ p} \) je definován následovně:

\[ (\mathbb{A} \cdot \mathbb{B})_{ij} = \sum_{k = 0}^{n-1} a_{ik} b_{kj}, \qquad i = 0, 1, \dots, m-1, \quad j = 0, 1, \dots, p-1. \]
Úkol

Napište funkci, která počítá součin dvou matic. Nepoužijte žádné knihovní funkce (np.dot() nebo operátoru @). Zkontrolujte správnost pomocí vhodné knihovní funkce.

A = np.random.rand(3,4)
B = np.random.rand(4,3)
print(A)
print(B)

def soucin_matic(A, B):
    ## DOPLŇTE ##
    C = np.zeros(shape=[A.shape[0], B.shape[1]])
    for i in range(A.shape[0]):
        for j in range(B.shape[1]):
            for k in range(A.shape[1]):
                C[i,j] += A[i,k]*B[k,j]
    return C

print(soucin_matic(A,B))
print(np.dot(A,B))        
[[0.10135879 0.69883083 0.45457066 0.52305277]
 [0.56112835 0.46921067 0.24237972 0.40405999]
 [0.70784711 0.81939215 0.21476439 0.0320192 ]]
[[0.95302307 0.02676025 0.13412802]
 [0.96670268 0.77940009 0.2201264 ]
 [0.65344265 0.40723556 0.77268612]
 [0.99510268 0.53669345 0.71815166]]
[[1.58968597 1.01321753 0.89429782]
 [1.5488179  0.69628077 0.65600848]
 [1.63890181 0.76222067 0.46425207]]
[[1.58968597 1.01321753 0.89429782]
 [1.5488179  0.69628077 0.65600848]
 [1.63890181 0.76222067 0.46425207]]

K snadnějšímu otestování správnosti lze použít následující funkci, která porovná výsledeky na zvolený počet míst:

try:
    np.testing.assert_array_almost_equal(soucin_matic(A, B), np.dot(A, B), decimal=7)
except AssertionError as E:
    print(E)
else:
    print("The implementation is correct.")
The implementation is correct.

Řešení soustavy lineárních rovnic#

Hlavní úlohou lineární algebry je řešení soustavy rovnic. Tuto úlohu dokáže počítač řešit stejně jako člověk, jelikož jsou známy algoritmy s konečným počtem kroků. Stačí tedy provést konečný počet aritmetických operací s konečným počtem čísel.

Jelikož tento typ lze snadno řešit na počítači, často se jiné úlohy převádí právě na soustavu lineárních rovnic, kterou dokážeme řešit. Příkladem tohoto postupu je řešení obyčejných diferenciálních rovnic (ODR) nebo parciálních diferenciálních rovnic (PDR), se kterými se setkáme v poslední kapitole.

Matematicky můžeme problém zapsat následovně:

\[ \mathbb{A}\vec{x} = \vec{b}. \]

Metody řešící soustavu lineárních rovnic můžeme rozdělit do tří skupin:

  • Metody přímé - přímý běh s daným počtem kroků podle velikosti matice

  • Metody iterační - postupné zpřesňování výsledku

  • Metody optimalizační - minimalizace funkce \(|\mathbb{A}\vec{x} - \vec{b}|\) gradientími metodami (viz kapitola 8)

Přímé metody pro řešení soustavy lineárních rovnic#

Gaussova eliminace#

Gaussova eleminace je algoritmus, který transformuje libovolnou matici na horní (nebo dolní) trojúhelníkovou. To výrazně zjednodušší celou soustavu. Řešení pak lze spočítat pomocí tzv. zpětného běhu. Jednotlivé kroky celého algoritmu si zde ukážeme.

Je to vlastně zobecněný postup eliminace členů v soustavě rovnic. Podobný postup známe již ze střední - sčítání/odčítání rovnic s cílem eliminovat dostatek členů tak, abychom mohli postupně vyjádřit a spočítat jednotlivé složky řešení.

Gaussova eliminace postup

Dopředný běh

Matici \( \mathbb{A} \in \mathbb{R}^{n \ \times \ n} \) je možné transformovat na horní trojúhelníkovou pomocí vzorce:

\[\begin{split} \begin{align} a_{jk} &:= a_{jk} - \frac{a_{ji}}{a_{ii}} a_{ik}, \qquad i = 0, 1, \dots, n-1, \quad j = i + 1, i + 2, \dots, n-1, \quad k = 0, 1, \dots, n-1, \\ b_{j} &:= b_{j}- \frac{a_{ji}}{a_{ii}} b_{i}, \qquad i = 0, 1, \dots, n-1, \quad j = i + 1, i + 2, \dots, n-1. \end{align} \end{split}\]
Úkol

Implementujte Gaussovu eliminaci, tedy převedení libovolné matice na horní trojúhelníkovou, pomocí uvedeného vzorce. Jaká je složitost celého algoritmu?

Tip

Dejte si pozor na nechtěné přepsání hodnoty \(a_{ji}\), která je potřeba k aktualizaci celého řádku!

A = np.random.rand(4,4)
b = np.random.rand(4)
print(A, b)

def gauss_elim_dopredna(A, b):
    A = A.copy()
    b = b.copy()
    ## DOPLŇTE ##
    for i in range(A.shape[0]):
        for j in range(i+1,A.shape[0]): # indexy [i+1,n-1]
            a = A[j,i]
            for k in range(i,A.shape[1]): # zde staci prochazet indexy [i,n-1]
                A[j,k] -= a / A[i,i] * A[i,k]
            b[j] -= a / A[i,i] * b[i]
    return A,b

B,_ = gauss_elim_dopredna(A,b)     
print(B)
[[0.54181406 0.09292209 0.65864946 0.4490366 ]
 [0.35231957 0.25207071 0.86527811 0.07756792]
 [0.37608874 0.55219664 0.70344033 0.25866283]
 [0.24911291 0.08746683 0.1174474  0.48516246]] [0.45382933 0.4728917  0.02524169 0.06630322]
[[ 5.41814058e-01  9.29220941e-02  6.58649460e-01  4.49036601e-01]
 [ 0.00000000e+00  1.91647268e-01  4.36985241e-01 -2.14422251e-01]
 [ 0.00000000e+00  5.55111512e-17 -8.65770774e-01  4.92627240e-01]
 [ 0.00000000e+00  0.00000000e+00  0.00000000e+00  1.65232063e-01]]

Předchozí implementace lze zjednodušit při použití řezání polí. Zde pro názornost používáme for-cykly. Díky tomu je snadno vidět, že složitost algoritmu je \(O(N^3)\).

Zpětný běh

Soustava lineárních rovnic \( \mathbb{A} x = b \) s horní trojúhelníkovou maticí \( \mathbb{A} \in \mathbb{R}^{n \ \times \ n} \) je možné vyřešit pomocí zpětné substituce neboli zpětného běhu Gaussovy eliminace:

\[ x_i := \frac{1}{a_{ii}} \left( b_i - \sum_{j = i + 1}^{n-1} a_{ij} x_j \right), \qquad i = n-1, n-2, \dots, 0. \]
Úkol

Implementujte zpětný běh Gaussovy eliminace, tedy řešení soustavy linerních rovnic s trojúhelníkovou maticí, pomocí uvedeného vzorce. Správnost ověřte pomocí knihovní funkce scipy.linalg.solve().

A = np.triu(np.random.rand(4, 4)) # vynuluje v matici cisla pod diagonalou
b = np.random.rand(4)
print(A, b)

def gauss_elim_zpetny(A,b):
    ## DOPLŇTE ##
    x = np.zeros(A.shape[0])
    for i in range(A.shape[0]-1, -1, -1):
        x[i] = b[i] / A[i,i]
        for j in range(i+1,A.shape[1]):
            x[i] -= A[i,j]*x[j] / A[i,i]
    return x

print(gauss_elim_zpetny(A, b))
print(la.solve(A, b)) # kontrola
[[0.28261947 0.31088426 0.98006575 0.21658714]
 [0.         0.47217157 0.24367598 0.22737901]
 [0.         0.         0.28393215 0.93321029]
 [0.         0.         0.         0.70837816]] [0.12961106 0.38487749 0.54491015 0.22189565]
[-3.09210456  0.20517378  0.88960355  0.31324462]
[-3.09210456  0.20517378  0.88960355  0.31324462]

Teď již stačí jen použít obě metody na řešení libovolné soustavy linearních rovnic:

A = np.random.rand(4,4)
b = np.random.rand(4)
print(A, b)

def gauss_elimimace(A,b):
    A,b = gauss_elim_dopredna(A, b)
    return gauss_elim_zpetny(A, b)

try:
    np.testing.assert_array_almost_equal(gauss_elimimace(A, b), np.linalg.solve(A, b), decimal=7)
except AssertionError as E:
    print(E)
else:
    print("The implementation is correct.")
[[0.11558118 0.85758044 0.73607042 0.21577914]
 [0.908435   0.5633516  0.36982372 0.88803557]
 [0.27289227 0.94334655 0.07696696 0.05864567]
 [0.45206168 0.56050908 0.86757872 0.05785772]] [0.00302654 0.23464056 0.09013684 0.86612249]
The implementation is correct.

Pivoting

V této podkapitole si ukážeme několik numerických komplikací, které u Gaussovy eliminace můžou nastat.

Prvním zřejmým problémem je situace, kdy je přední prvek matice (pivot) rovný nule. Ten nemůžeme použít, protože bychom dělili při eliminaci spodních řádků nulou!

Úkol

Použijte Gaussovu eliminaci gauss_elimimace() na řešení soustavy s maticí:

\[\begin{split} \mathbb{A} = \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix}. \end{split}\]

## DOPLŇTE ##

A = np.array([[0, 1], [1, 1]])
b = np.random.rand(2)

print(gauss_elimimace(A, b))
C:\Users\jiral\AppData\Local\Temp\ipykernel_1788\339819762.py:13: RuntimeWarning: divide by zero encountered in long_scalars
  A[j,k] -= a / A[i,i] * A[i,k]
C:\Users\jiral\AppData\Local\Temp\ipykernel_1788\339819762.py:13: RuntimeWarning: invalid value encountered in double_scalars
  A[j,k] -= a / A[i,i] * A[i,k]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In [11], line 6
      3 A = np.array([[0, 1], [1, 1]])
      4 b = np.random.rand(2)
----> 6 print(gauss_elimimace(A, b))

Cell In [10], line 6, in gauss_elimimace(A, b)
      5 def gauss_elimimace(A,b):
----> 6     A,b = gauss_elim_dopredna(A, b)
      7     return gauss_elim_zpetny(A, b)

Cell In [8], line 13, in gauss_elim_dopredna(A, b)
     11         a = A[j,i]
     12         for k in range(0,A.shape[1]):
---> 13             A[j,k] -= a / A[i,i] * A[i,k]
     14         b[j] -= a / A[i,i] * b[i]
     15 return A,b

ValueError: cannot convert float NaN to integer
Pozor

Gaussova eliminace nedokáže matici s nulovými prvky na diagonále převést na horní trojúhelníkovou.

Druhý problém nastává, pokud je pivot velmi malý. Tedy na diagonále matice se nachází velmi nízké hodnoty.

Úkol

Použijte Gaussovu eliminaci gauss_elimimace() na řešení soustavy s maticí a pravou stranou:

\[\begin{split} \mathbb{A} = \begin{pmatrix} 10^{-10} & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{pmatrix}, \quad \vec{b} = \begin{pmatrix} 1 \\ 0.1 \\ 0.01 \end{pmatrix}. \end{split}\]

Výsledek porovnejte s řešením získaným pomocí knihovní funkce scipy.linalg.solve().

## DOPLŇTE ##

A = np.array([[1e-10, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([1, 0.1, 0.001])

print(gauss_elimimace(A, b))
print(la.solve(A, b))
[-0.8010025   1.30401611 -0.53601074]
[-0.801  1.304 -0.536]

Vidíme, že dostáváme výsledek s nezanedbatelnou chybou! Podobný příklad můžete vidět zde.

Chyba vzniká zaokrouhlováním při odčítání velmi odlišných čísel. Při použití malého pivota odčítáme od hodnot v matici vysoké číslo, čímž vzníká velká chyba (výrazně větší než strojová přesnost \(\varepsilon\)).

Dalším problémem je, že malé hodnoty v matici mohou vznikat odečtením blízkých hodnot v předchozích krocích. Nasledné použití této hodnoty jako pivota vede k silnému hromadění chyb!

Předchozím problémům se lze vyhnout vhodným výběrem pivota (tzv. pivotingem). Vhodný pivot je největší dostupné číslo. Máme několik strategií, jak pivota vybrat:

Pivoting strategie

  1. úplný pivoting - výběr z celé dosud neupravené části matice \(\max{|a_{jk}|}\) (nevýhoda: znatelně zpomaluje výpočet)

  2. částečný pivoting - výběr v daném sloupci (sloupcový) a řádku (řádkový)

  3. implicitní pivoting - rychlejší verze sloupcového pivotingu, při výběru je porovnána velikost prvků v daném sloupci normovaném na maximum z prvků v jednotlivých řádcích původní matice

Při výběru pivota z jiného řádku je třeba prohodit odpovídající řádky a pravou stranu. V případě volby pivota v jiném sloupci je třeba prohodit navíc složky hledaného řešení.

V praxi většinou stačí částečný nebo i sloupcový pivoting.

Úkol

Modifikujte Gaussovu eliminační metodu pomocí tak, že použijete sloupcový pivoting. Ověřte zlepšení přesnosti na řešení předcházející soustavy rovnic. Stačí upravit zpětný chod gauss_elim_dopredna().

Tip

Pro nalezení největšího prvku se může hodit funkce np.argmax(). Pro prohození řádků lze použít np.copy() (pro uložení přepisovaného řádku do pomocné proměné) nebo pokročilého Numpy řezu A[[i,j],:] = A[[j,i],:] (prohodí řádky \(i,j\)).

A = np.array([[1e-10, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([1, 0.1, 0.001])

def gauss_elim_dopredna_pivoting(A, b):
    A = A.copy()
    b = b.copy()
    ## DOPLŇTE ##
    for i in range(A.shape[0]):
        # vyber pivota
        p = np.argmax(A[i:,i]) + i
        print(p)
        # swap - prohozeni radku
        arr = A[p, :].copy()
        A[p,:] = A[i, :]
        A[i,:] = arr
        arr = b[p].copy()
        b[p] = b[i]
        b[i] = arr
        print(A,b)
        # eliminace
        for j in range(i+1,A.shape[0]):
            a = A[j,i]
            for k in range(0,A.shape[1]):
                A[j,k] -= a / A[i,i] * A[i,k]
            b[j] -= a / A[i,i] * b[i]
        print(A,b)
    return A,b

def gauss_elimimace_pivoting(A,b):
    A,b = gauss_elim_dopredna_pivoting(A, b)
    return gauss_elim_zpetny(A, b)

print(gauss_elimimace_pivoting(A, b))
print(la.solve(A, b))
2
[[7.e+00 8.e+00 9.e+00]
 [4.e+00 5.e+00 6.e+00]
 [1.e-10 2.e+00 3.e+00]] [0.001 0.1   1.   ]
[[7.         8.         9.        ]
 [0.         0.42857143 0.85714286]
 [0.         2.         3.        ]] [0.001      0.09942857 1.        ]
2
[[7.         8.         9.        ]
 [0.         2.         3.        ]
 [0.         0.42857143 0.85714286]] [0.001      1.         0.09942857]
[[7.         8.         9.        ]
 [0.         2.         3.        ]
 [0.         0.         0.21428571]] [ 0.001       1.         -0.11485714]
2
[[7.         8.         9.        ]
 [0.         2.         3.        ]
 [0.         0.         0.21428571]] [ 0.001       1.         -0.11485714]
[[7.         8.         9.        ]
 [0.         2.         3.        ]
 [0.         0.         0.21428571]] [ 0.001       1.         -0.11485714]
[-0.801  1.304 -0.536]
[-0.801  1.304 -0.536]

Gauss-Jordanova eliminace#

  • Matici \(\mathbb{A}\) převedeme na tvar, kdy jsou na hlavní diagonále samé jedničky

  • Touto metodou lze získat i inverzní matici \(\mathbb{A}^{-1}\)

  • Ukázka

Thomasův algoritmus#

Pro speciální typy matic existují efektivnější algoritmy, které najdou řešení ve výrazně kratším čase. Příkladem je soustava s tridiagonální maticí. Pro takovou matici nemusíme provádět celou Gaussovu eliminaci, ale jen výrazně redukovaný počet kroků (stačí eliminovat pouze vedlejší diagonály).

Systém \( \mathbb{A} \vec{x} = \vec{b} \) s tridiagonální maticí \( \mathbb{A} \) lze efektivně řešit pomocí Thomasova algoritmu. Tridiagonální matici \( \mathbb{A} \) můžeme reprezentovat pomocí tří vektorů \( \vec{p} \), \( \vec{q} \), and \( \vec{r} \) následovně:

\[\begin{split} \mathbb{A} = \begin{pmatrix} q_0 & p_0 & 0 & 0 & \cdots & 0 & 0 & 0 \\ r_1 & q_1 & p_1 & 0 & \cdots & 0 & 0 & 0 \\ 0 & r_2 & q_2 & p_2 & \cdots & 0 & 0 & 0 \\ \vdots & \vdots & \ddots & \ddots & \ddots & \vdots & \vdots & \vdots \\ \vdots & \vdots & \vdots & \ddots & \ddots & \ddots & \vdots & \vdots \\ \vdots & \vdots & \vdots & \vdots & \ddots & \ddots & \ddots & \vdots \\ 0 & 0 & 0 & 0 & \cdots & r_{n-2} & q_{n-2} & p_{n-2} \\ 0 & 0 & 0 & 0 & \cdots & 0 & r_{n-1} & q_{n-1} \end{pmatrix}, \end{split}\]

kde \( p_{n-1} = 0 \) and \( r_0 = 0 \). Řešení \( \mathbb{A} x = b \) získáme zpětným během eliminace:

\[ x_i = \mu_i x_{i+1} + \rho_i, \qquad i = n-1, n-2, \dots, 0, \]

kde koeficienty \( \mu_i \) a \( \rho_i \) jsou získány při dopředném běhu:

\[ \mu_i = -\frac{p_i}{r_i \mu_{i-1} + q_i}, \qquad \rho_i = \frac{b_i - r_i \rho_{i-1}}{r_i \mu_{i-1} + q_i}, \qquad i = 1, 2, \dots, n-1 \]

kde \( \mu_0 = -p_0 \, / \, q_0 \), \( \rho_0 = b_0 \, / \, q_0 \) a \(\mu_{n-1}=0\).

Úkol

Implementujte Thomasův algoritmus pomocí uvedených vzorců. Nejprve je třeba provést dopředný běh (výpočet \(\mu\) a \(\rho\)) a následně zpětný běh. Správnost ověřte pomocí knihovní funkce scipy.linalg.solve().

A = np.triu(np.tril(np.random.rand(5, 5), 1), -1)
b = np.random.rand(5)
print(A,b)

def thomas_alg(A,b):
    ## DOPLŇTE ##    
    mu = np.zeros(A.shape[0])
    rho = np.zeros(A.shape[0])
    x = np.zeros(A.shape[0])
    # dopřený běh
    mu[0] = -A[0,1]/A[0,0]
    rho[0] = b[0]/A[0,0]
    for i in range(1,A.shape[0]):
        if i < A.shape[0]-1:
            mu[i] = -A[i,i+1] / (A[i,i-1]*mu[i-1] + A[i,i])
        rho[i] = (b[i] - A[i,i-1]*rho[i-1]) / (A[i,i-1]*mu[i-1] + A[i,i])
    mu[-1] = 0
    
    # zpětný běh
    x[-1] = rho[-1]
    for i in range(A.shape[0]-2,-1,-1):
        x[i] = mu[i]*x[i+1] + rho[i]
    
    return x
    
print(thomas_alg(A, b))
print(la.solve(A,b))
[[0.83564522 0.49502089 0.         0.         0.        ]
 [0.94611263 0.96958677 0.55657327 0.         0.        ]
 [0.         0.47817651 0.34201066 0.62709574 0.        ]
 [0.         0.         0.22867621 0.73913228 0.82176068]
 [0.         0.         0.         0.6185506  0.41949737]] [0.12470432 0.89065639 0.39036287 0.24113495 0.19270496]
[ 105.68923494 -178.1621758   132.31038848   64.31516518  -94.37360483]
[ 105.68923494 -178.1621758   132.31038848   64.31516518  -94.37360483]

LU dekompozice#

LU dekompozice obrázek

LU dekompozice rozděluje matici na součin spodní a hodní trojúhelníkové matice. Lze ji spočítat pomocí Croutova algoritmu:

\[\begin{split} \begin{align} u_{ij} &= a_{ij} - \sum_{k = 1}^{i - 1} l_{ik} u_{kj}, \quad i = 1, 2, \dots, j, \\ l_{ij} &= \frac{1}{u_{jj}} \left( a_{ij} - \sum_{k = 1}^{j - 1} l_{ik} u_{kj} \right), \quad i = j + 1, j+2, \dots, n, \end{align} \end{split}\]

pro všechna \(j = 1, 2, \dots, n\).

Tento algoritmus má podobnou složitost jako Gaussova eliminace. Je výhodný v případě, kdy řešíme stejnou soustavu rovnic pro mnoho různých pravých stran. V takovém případě by se nevyplatilo provádět Gaussovu eliminaci pořád znova. Naopak, pokud matici rozložíme pomocí LU dekompozice, stačí provést dvakrát zpětný běh Gaussovy eliminace, který je pouze \(O(n^2)\) oproti Gaussově eliminaci \(O(n^3)\)! Matematicky stačí vyřešit:

\[ \mathbb{A} x = (\mathbb{L} \mathbb{U}) x = \mathbb{L} ( \mathbb{U} x) = b, \]
\[ \mathbb{L} y = b, \]
\[ \mathbb{U} x = y. \]

Iterativního zpřesnění řešení#

Další výhodou je možnost rychlého iterativního zpřesnění řešení. Uvažujme nepřesné řešení \(\vec{x}^{\prime}\) soustavy \(\mathbb{A} \vec{x} = \vec{b}\):

\[ \vec{x}^{\prime} = \vec{x} + \delta \vec{x} \hspace{0.5em} \rightarrow \hspace{0.5em} \mathbb{A}(\vec{x} + \delta \vec{x}) = \vec{b} + \delta \vec{b} \hspace{0.5em} \rightarrow \hspace{0.5em} \mathbb{A} \delta\vec{x} = \delta{b} = \mathbb{A} \vec{x}^{\prime} - \vec{b}. \]

Tedy počáteční nepřesné řešení \(\vec{x}_0\) můžeme nepřesné řešení iterativně zpřesňovat pomocí:

\[ \vec{x}_{i+1} = \vec{x}_i + \delta \vec{x}_i, \quad \text{kde} \quad \mathbb{A} (\delta \vec{x}_i) = \vec{b} - \mathbb{A} \vec{x}_i. \]
Poznámka

Pro velké matice (\(N > 50\)) již dochází v přímých metodách k výraznému kumulování zaokrouhlovacích chyb, jelikož počet aritmetických operací roste s \(O(N^3)\). Obzvlášť u matic blízkých singulární matici (lineární závislost řádků) je chyba významně zesilována. Proto je doporučeno využít iterativního zpřesnění, což umožní snížit chybu zpět na strojovou přesnost. Zároveň si tím nepokazíme celkovou složitost, jelikož prvotní řešení soustavy zabere \(O(N^3)\), zatímco jedna iterace zpřesnění má složitost již jen \(O(N^2)\)!

Poznámka

Pro singulární matice nebo blízké singulární můžou přímé, které jsme v této kapitole viděli, snadno selhat. Existují proto specializované SVD metody (Singular Value Decomposition), které si dokáží poradit s obdélníkovými maticemi a maticemi se soustavami s závislými rovnicemi.

Zápočtová úloha#

Úkol - zápočet 2

ZDE