MN08: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Przykry (dyskusja | edycje)
mNie podano opisu zmian
 
m Zastępowanie tekstu – „,↵</math>” na „</math>,”
 
(Nie pokazano 19 wersji utworzonych przez 3 użytkowników)
Linia 1: Linia 1:


==Szybka transformacja Fouriera (FFT)==
<!--
 
Konwertowane  z pliku LaTeX przez latex2mediawiki, zob. http://www.ii.uj.edu.pl/&nbsp;pawlik1/latex2mediawiki.php.
Algorytm FFT dla dyskretnej transformacji Fouriera (DFT) i jego krewniacy dla
Niezb�dne rozszerzenia i modyfikacje oryginalnego latex2mediawiki
wyznaczania dyskretnej transformacji cosinusów (DCT i MDCT), choć dotyczą
wprowadzi� przykry@mimuw.edu.pl
pozornie dość abstrakcyjnych zadań, zrewolucjonizowały wiele dziedzin życia.
-->
Między innymi, wykorzystuje się je w
* kompresji obrazów w formacie JPEG (DCT)
* kompresji dźwięku w formacie MP3 i pokrewnych (MDCT)
* rozwiązywaniu ważnych równań różniczkowych cząstkowych (DFT)
   
   
a także do
=Wielkie układy równań liniowych=
* fitrowania szumów (DFT)
* szybkiego mnożenia wielomianów (DFT)
W niniejszym wykładzie ograniczymy się do przedstawienia szybkiego algorytmu
rozwiązywania zadania DFT, algorytmy rozwiązywania zadań pokrewnych (DCT, MDCT,
itp.) opierają się na podobnych zasadach.


Zacznijmy od postawienia zadania obliczeniowego DFT. Jest ono (pozornie!)
{{powrot |Metody numeryczne | do strony głównej
jednocześnie banalne i wydumane:
przedmiotu <strong>Metody numeryczne</strong>}}


\beginprob
Wraz z coraz większymi modelami pojawiającymi się w praktyce obliczeniowej,
Dla danego zestawu <math>\displaystyle N</math> liczb <math>\displaystyle f_\alpha\in C</math>, <math>\displaystyle \alpha = 0,\ldots, N-1</math>,
coraz częściej zachodzi potrzeba rozwiązywania zadań algebry liniowej, w której
wyznaczyć  <math>\displaystyle N</math> wartości
macierze są co prawda wielkiego wymiaru, ale najczęściej <strong>rozrzedzone</strong>, to
znaczy jest w nich bardzo dużo zer. Bardzo często zdarza się, że macierz
wymiaru <math>N</math> ma tylko <math>O(N)</math> niezerowych elementów. Wykorzytanie tej specyficznej
własności macierzy nie tylko prowadzi do algorytmów istotnie szybszych od ich
analogów dla macierzy gęstych (to znaczy takich, które (w założeniu) mają <math>N^2</math>
elementów), ale wręcz są jedynym sposobem na to, by niektóre zadania w ogóle
stały się rozwiązywalne przy obecnym stanie techniki obliczeniowej!


<center><math>\displaystyle c_j = \sum_{\alpha = 0}^{N-1} f_\alpha \omega_N^{j\alpha},
Jednym ze szczególnie ważnych źródeł układów równań z macierzami rozrzedzonymi są np. <strong>równania różniczkowe cząstkowe</strong> (a więc np. modele pogody, naprężeń w konstrukcji samochodu, przenikania kosmetyków do głębszych warstw skóry, itp.).
</math></center>


dla <math>\displaystyle j=0,\ldots, N-1</math>, przy czym <math>\displaystyle \omega_N = \exp(-i\frac{2\Pi}{N})</math>. Jak
Modele wielostanowych systemów kolejkowych (np. routera obsługującego wiele komputerów) także prowadzą do gigantycznych układów równań z macierzami rozrzedzonymi o specyficznej strukturze.
pamiętamy, jednostka urojona <math>\displaystyle i</math> spełnia <math>\displaystyle i = \sqrt{-1}</math>. Taką operację nazywamy
''dyskretną transformacją Fouriera'', DFT.
\endprob


Ponieważ dane <math>\displaystyle f</math> są wektorem <math>\displaystyle f = [f_0,\ldots,f_{N-1}]^T</math>, wynik <math>\displaystyle c</math> też jest wektorem, a zadanie jest liniowe,
Z reguły zadania liniowe ''wielkiego wymiaru'' będą miały strukturę macierzy rozrzedzonej, gdyż najczęściej związki pomiędzy niewiadomymi w równaniu nie dotyczą wszystkich, tylko wybranej grupy.
możemy wszystko zapisać macierzowo:


<center><math>\displaystyle c = F_N f,
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
</math></center>
<span  style="font-variant:small-caps;">Przykład: Macierz z kolekcji Boeinga</span>  
<div class="solution" style="margin-left,margin-right:3em;">


gdzie
Spójrzmy na macierz sztywności dla modelu silnika lotniczego, wygenerowaną swego czasu w zakładach Boeinga i pochodzącą z dyskretyzacji pewnego równania różniczkowego cząstkowego. Pochodzi z [http://www.cise.ufl.edu/research/sparse/matrices/Boeing  kolekcji Tima Davisa]. Jest to mała macierz, wymiaru 8032 (w kolekcji spotkasz równania z milionem i więcej niewiadomych).


<center><math>\displaystyle
[[Image:MNsparseA.png|thumb|550px|center|Struktura niezerowych elementów macierzy bcsstk38.]]
F_N = (\omega_N^{j\alpha})_{j,\alpha = 0,\ldots, N-1} =
\begin{pmatrix}
1 & 1 & 1 & \cdots & 1\\
1 & \omega_N & \omega_N^2 & \cdots & \omega_N^{N-1} \\
1 & \omega_N^2 & \omega_N^4 & \cdots & \omega_N^{2(N-1)} \\
  &            &            & \cdots & \\
1 & \omega_N^{N-1} & \omega_N^{2(N_1)} & \cdots & \omega_N^{(N-1)^2}
\end{pmatrix} .
</math></center>


Tak więc, gdyby naiwnie podejść do zadania, moglibyśmy je rozwiązać brutalnie,
Jej <strong>współczynnik wypełnienia</strong> (to znaczy, stosunek liczby niezerowych do wszystkich elementów macierzy) wynosi jedynie <math>0.006</math>, a więc macierz jest bardzo rozrzedzona: w każdym wierszu średnio tylko 44 niezerowe elementy.
tworząc na początek macierz <math>\displaystyle F_N</math>, a następnie wyznaczając iloczyn macierzy
</div></div>
przez wektor, co łatwo zrobić kosztem <math>\displaystyle O(N^2)</math> operacji. Dlatego istotne jest,
że algorytm FFT, który za chwilę omówimy, będzie działać kosztem <math>\displaystyle O(N\log\,N)</math>,
czyli praktycznie liniowym (w obu przypadkach stałe proporcjonalności równe
2) w dodatku będzie miał znacznie mniejsze wymagania pamięciowe. Już nawet gdy
<math>\displaystyle N=8</math> (przypadek kompresji JPEG), mamy <math>\displaystyle N^2 = 512</math>, gdy tymczasem  <math>\displaystyle N\log\,N =
24</math>, więc zysk jest ponaddwudziestokrotny, co ma kapitalne znaczenie, bo zamiast
czekać na zapis zdjęcia (powiedzmy) pół sekundy, czekalibyśmy długie 10 (sic!)
sekund... albo nasza(?) cyfrówka kosztowałaby majątek...


{{fakt|||
<!--
Macierz <math>\displaystyle F</math> jest symetryczna oraz <math>\displaystyle \frac{1}{N} F_N^* F_N = I</math>.
}}


Zauważmy, że nasza macierz ma jeszcze więcej specjalnej struktury, powtarza się
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
w niej bardzo wiele takich samych współczynników (sprawdź dla <math>\displaystyle N=4</math>, w ogólności
<span  style="font-variant:small-caps;">Przykład: Macierz MBEACXC</span>  
ma tylko <math>\displaystyle N</math> różnych wyrazów), gdyż <math>\displaystyle \omega_N^N = 1</math> (<math>\displaystyle \omega^j</math> dla
<div class="solution" style="margin-left,margin-right:3em;">
<math>\displaystyle j=0,\ldots,N-1</math> to nic innego jak wszystkie zespolone pierwiastki z jedynki), a
jak wiadomo, mnożenie przez 1 nic nie kosztuje --- o ile tylko się wie, że ''gdzie'' jest ta jedynka. Z grubsza na tym właśnie będzie polegać siła algorytmu
FFT!


W wyprowadzeniu algorytmu szybkiej transformacji Fouriera, (ang. Fast Fourier
Dane pochodzą z serwisu
Transform, FFT) oprzemy się poraz kolejny na regule "dziel i rządź". Dla
[http://math.nist.gov/MatrixMarket  MatrixMarket]. Jest to niezbyt duża macierz, wymiaru <math>496 \times 496</math>, niesymetryczna, o elementach rzeczywistych. Źródłem tej macierzy jest pewien model ekonomiczny.
uproszczenia analizy przyjmiemy, że <math>\displaystyle N</math> jest naturalną potęgą dwójki, w
szczególności <math>\displaystyle N = 2m</math> dla pewnego naturalnego <math>\displaystyle m</math>.


Rzeczywiście, rozbijając naszą sumę na sumę po indeksach parzystych i
[[Image:MNmbeacxc|thumb|550px|center|Struktura niezerowych elementów macierzy MBEACXC]]
sumę  po indeksach nieparzystych, mamy


<center><math>\displaystyle \aligned c_j &= f_0 \omega_N^{0\cdot j} + f_2 \omega_N^{2\cdot j} + \ldots + f_{N-2}
Tylko pozornie macierz ta wydaje się dość gęsta, w rzeczywistości jej współczynnik wypełnienia wynosi około 20 procent.  
\omega_N^{(N-2)\cdot j}\\
</div></div>
&& + f_1 \omega_N^{1\cdot j} + f_3 \omega_N^{3\cdot j} + \ldots + f_{N-1}
\omega_N^{(N-1)\cdot j}.
\endaligned</math></center>


Suma po indeksach parzystych da się zapisać w postaci
-->
==Reprezentacja macierzy rzadkich==


<center><math>\displaystyle
Zacznijmy od sposobu przechowywania macierzy rozrzedzonych. Naturalnie, nie ma
\sum_{k=0}^{m-1} f_{2k} (\omega_m) ^ {jk}
sensu przechowywać wszystkich <strong>zerowych</strong> jej elementów: wystarczy
</math></center>
ograniczyć się do zachowania tych niezerowych! W ten sposób zmniejszamy zarówno
wymagania pamięciowe, jak i liczbę operacji zmiennoprzecinkowych potrzebnych do
prowadzenia działań na macierzy (np. w przypadku mnożenia macierzy przez
wektor, nie będziemy mnożyć przez zera!).


i analogicznie suma po indeksach nieparzystych da się zapisać
===Format współrzędnych===


<center><math>\displaystyle
Do zapamiętania macierzy <math>A</math> wymiaru <math>N\times M</math> o <math>NNZ</math> niezerowych elementów wykorzystujemy trzy wektory: <code>I</code>,
\omega_N^j\cdot \sum_{k=0}^{m-1} f_{2k+1} (\omega_m) ^ {jk}
<code>J</code> --- oba typu <code>int</code> --- oraz <code>V</code>, typu <code>double</code>,
</math></center>
wszystkie o długości <math>NNZ</math>, przy czym


gdyż <math>\displaystyle \omega_N^2 = \omega_m</math>, a więc wygląda na to, że nasze zadanie wyznaczenia
<center><math>
dyskretnej transformacji Fouriera wymiaru <math>\displaystyle N</math> da się sprowadzić do analogicznych zadań mniejszego
A_{I[k], J[k]} = V[k], \qquad\forall k = 0,\ldots, NNZ-1</math></center>
rozmiaru.
[[Image:MNaij-matrix-format.png|thumb|550px|center|Format AIJ]]


Rzeczywiście, korzystając z tego, że <math>\displaystyle j = \beta m + \widetilde{j}</math>, gdzie <math>\displaystyle \widetilde{j}
W tym formacie wprowadzane są macierze rzadkie do Octave'a i MATLABa:
= 0,\ldots, m-1</math>, oraz <math>\displaystyle \beta = 0</math> lub <math>\displaystyle 1</math>, mamy <math>\displaystyle \omega_m^j =
\omega_m^{\widetilde{j}}</math>.


Oznaczając
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>A = sparse(I,J,V,N,N);
</pre></div>
Jednak wewnętrznie przechowywane są w innym formacie, o którym [[#Format spakowanych kolumn (wierszy)|poniżej]].


<center><math>\displaystyle \aligned \Phi_{\widetilde{j}} &= \sum_{k=0}^{m-1} f_{2k} \omega_m^{\widetilde{j}k}, \qquad
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
\widetilde{j} = 0,\ldots, m-1,\\
<span  style="font-variant:small-caps;">Przykład</span>
\Psi_{\widetilde{j}} &= \sum_{k=0}^{m-1} f_{2k+1} \omega_m^{\widetilde{j}k},  
<div class="solution" style="margin-left,margin-right:3em;">
\endaligned</math></center>


(jak widać są to DFT dla dwa razy krótszych wektorów, złożonych z tylko
Pokażemy jak w Octave wprowadzić macierz rozrzedzoną.
parzystych lub tylko nieparzystych współrzędnych <math>\displaystyle f</math>) dostajemy ostatecznie


<center><math>\displaystyle \aligned c_{\widetilde{j}} = \Phi_{\widetilde{j}} + \omega_N^{\widetilde{j}} \Psi_{\widetilde{j}},\\
<div style="font-family: monospace; white-space: pre; border-style: dashed; border-width: thin; border-color: black; margin: 1em; padding:1em; color: #444; background-color:#fdfdfd;"><realnowiki><realnowiki><realnowiki><realnowiki><nowiki>octave:10> I = [1, 1, 1, 2, 3, 1, 4];
c_{\widetilde{j}+m} = \Phi_{\widetilde{j}} - \omega_N^{\widetilde{j}} \Psi_{\widetilde{j}}.
octave:11> J = [1, 3, 2, 2, 3, 4, 4];
\endaligned</math></center>
octave:12> V = [1, 2, 3, 4, 5, 6, 7];
octave:13> N = 4;
octave:14> A = sparse(I,J,V,N,N)
A =


Tym samym, wyznaczenie DFT wymiaru <math>\displaystyle N</math> udało się rzeczywiście sprowadzić
Compressed Column Sparse (rows = 4, cols = 4, nnz = 7)
do wyznaczenia dwóch DFT wymiaru <math>\displaystyle m = N/2</math> oraz drobnej manipulacji na
ich wynikach zgodnie z powyższym wzorem. Oczywiście, te mniejsze transformacje
można wyznaczyć takim samym sposobem, co prowadzi do zależności rekurencyjnej,
która kończy się na wektorach długości 1, na których DFT to po prostu
identyczność.


Proste sprawdzenie pokazuje, że koszt takiego algorytmu jest rzędu
  (1, 1) ->  1
<math>\displaystyle O(N\log\,N)</math>, a nie <math>\displaystyle O(N^2)</math>, jak dla naiwnego algorytmu mnożenia wektora przez
  (1, 2) ->  3
gęstą macierz. Zysk jest więc, nawet dla niewielkich <math>\displaystyle N</math>, istotny.
  (2, 2) ->  4
  (1, 3) ->  2
  (3, 3) -> 5
  (1, 4) ->  6
  (4, 4) ->  7
 
octave:15> spy(A);
</nowiki></realnowiki></realnowiki></realnowiki></realnowiki></div>
Strukturę jej niezerowych elementów ukaże nam polecenie <code style="color: #006">spy(A)</code>. Tak właśnie zostały wygenerowane obrazki macierzy w niniejszym wykładzie.


{{algorytm|Prosta wersja algorytmu FFT||
</div></div>
<pre>


function y <nowiki>=</nowiki> fft(x)
===Format spakowanych kolumn (wierszy)===
N <nowiki>=</nowiki> length(f);
if N <nowiki>=</nowiki><nowiki>=</nowiki> 1
y <nowiki>=</nowiki> x;
else
<math>\displaystyle \omega</math> <nowiki>=</nowiki> <math>\displaystyle \exp(-\frac{2\Pi}{N}i)</math>;
<math>\displaystyle \omega_k</math> <nowiki>=</nowiki> <math>\displaystyle \omega^{N/2-1}</math>;
u <nowiki>=</nowiki> fft( f[0:2:N-2] );
v <nowiki>=</nowiki> fft( f[1:2:N-1] );
v <nowiki>=</nowiki> v * <math>\displaystyle \omega_k</math>;
y <nowiki>=</nowiki> [ u+v ; u-v ];
end
end
</pre>}}


===Efektywna implementacja FFT===
Format współrzędnych nie narzucał żadnego uporządkowania elementów macierzy
--- można było je umieszczać w dowolnej kolejności. Narzucenie sensownego
porządku mogłoby wspomóc realizację wybranych istotnych operacji na macierzy,
na przykład, aby wygodnie było realizować działanie (prawostronnego) mnożenia
macierzy przez wektor, dobrze byłoby przechowywać elementy macierzy ''wierszami''. Tak właśnie jest zorganizowany format spakowanych wierszy (''Compressed Sparse Row, CSR''). Analogicznie jest zdefiniowany format
spakowanych kolumn (''Compressed Sparse Column, CSC''), którym zajmiemy się bliżej.


Jak już zdążyliśmy się przyzwyczaić, w algorytmach numerycznych unikamy
Podobnie jak w przypadku formatu współrzędnych, macierz w formacie CSC jest
stosowania jawnej rekurencji, gdy tylko to możliwe. W przypadku FFT można jej
przechowywana w postaci trzech wektorów: <code>AV</code> jest wektorem typu
również uniknąć, wyznaczając zawczasu --- korzystając z tzw. odwrócenia bitów
<code>double</code> o długości <math>NNZ</math>, zawierającym <strong>kolejne</strong> niezerowe elementy
--- porządek, w którym należy składać 1-wymiarowe DFT w coraz dłuższe wektory
macierzy <math>A</math> wpisywane <strong>kolumnami</strong>, <code>AI</code> jest wektorem typu <code>int</code>
zgodnie ze wzorem powyżej tak, by na końcu dostać pożądany wektor <math>\displaystyle c = F_Nf</math>.
o długości <math>NNZ</math>, zawierającym numery wierszy macierzy <math>A</math> odpowiadających
elementom z <code>AV</code>. Natomiast zamiast tablicy <code>J</code>, jak to było w
formacie współrzędnych, mamy krótszy wektor typu <code>int</code>, <code>AP</code>, o
długości <math>M</math>, zawierający na <math>j</math>-tym miejscu indeks pozycji w <code>AV</code>, od
którego rozpoczynają się w <code>AV</code> elementy <math>j</math>-tej kolumny macierzy <math>A</math>.


Ponadto, istnieją warianty algorytmu FFT, które np. działają na danych
[[Image:MNcsc-matrix-format.png|thumb|550px|center|Format CSC]]
rzeczywistych. Na analogicznych zasadach co FFT oparte są również algorytmy
wykonujące tzw. dyskretną transformację cosinusów (DCT) i jej wariant MDCT
stosowany w kodekach audio takich jak MP3, AAC, czy Ogg-Vorbis..  


===Interpolacja trygonometryczna===
Mamy więc zależność, przy założeniu, że <math>AP[0]=0</math>,
<center><math>
A_{AI[AP[j]+k],j+1} = AV[AP[j]+k], \qquad j = 0,\ldots,M-1, \, k =
0,\ldots,AP[j+1]-1</math></center>


Jednym z wielu zadań, w których daje się zastosować algorytm FFT, jest zadanie
Taki (z drobnymi modyfikacjami) format macierzy wykorzystują np. pakiety ARPACK
interpolacji trygonometrycznej:
i UMFPACK (a także, wewnętrznie, Octave i MATLAB).


\beginprob [Interpolacja trygonometryczna]
===Format diagonalny===
Dla danych węzłów <math>\displaystyle x_k = \frac{2\pi k }{N}</math>, <math>\displaystyle k = 0,\ldots,N-1</math>, znaleźć
wielomian <math>\displaystyle P</math> (zmiennej rzeczywistej, o wartościach zespolonych) stopnia <math>\displaystyle \leq N-1</math> postaci
<center><math>\displaystyle
P(x) = \sum_{k=0}^{N-1} \beta_k \bar{\omega}^k,
</math></center>


gdzie <math>\displaystyle \bar{\omega} = e^{ix} = \cos(x) + i\, \sin(x)</math> (stąd nazwa: trygonometryczny)
Znacznie mniej uniwersalny niż poprzednie i dlatego rzadziej spotykany. Kolejne
taki, że
diagonale macierzy przechowujemy w kolejnych wierszach macierzy <math>P\times N</math>,
<center><math>\displaystyle
gdzie <math>P</math> jest liczbą niezerowych diagonal w <math>A\in R^{N\times N}</math>.
P(x_k) = y_k, \qquad k = 0,\ldots,N-1,
</math></center>


dla zadanych wartości <math>\displaystyle y_k</math>.  
Szczególnie wygodny do reprezentacji macierzy taśmowych. Wykorzystywany m.in.
\endprob
przez funkcję LAPACKa <code style="color: #903">DGBSV</code> służącą rozwiązywaniu równań z macierzami
taśmowymi.


W języku macierzy DFT możemy zapisać zadanie interpolacji trygonometrycznej jako
===Uwagi praktyczne===
zadanie wyznaczania wektora <math>\displaystyle \beta</math> takiego, że
<center><math>\displaystyle
\bar{F_N}\beta  = y
</math></center>


dla zadanego wektora <math>\displaystyle y</math>.
Mnożenie macierzy w formacie AIJ przez wektor jest kilka razy wolniejsze w porównaniu do macierzy w formacie CSC/CSR (z tych dwóch oczywiście (dlaczego?) CSR jest najszybszy).  Co więcej, okazuje się, że w typowych implementacjach, mnożenie macierzy rozrzedzonej (reprezentowanej np. w formacie CSC) przez wektor jest mało efektywne, mniej więcej na poziomie dziesięciu procent możliwości obliczeniowych procesora.  


{{twierdzenie|||
Jeśli już ''poważnie'' myśleć o przyspieszeniu mnożenia macierzy przez wektor (np. gdy chcemy stosować [[#Stacjonarne metody iteracyjne|iteracyjną metodę rozwiązywania układu równań]] z tą macierzą), warto rozważyć [http://bebop.cs.berkeley.edu/pubs/vuduc2003-dissertation.pdf  inne formaty] --- łączące w sobie podział macierzy na bloki (tak, jak w algorytmach BLAS) i przechowywanie (w zasadzie) tylko niezerowych elementów macierzy.
Współczynniki <math>\displaystyle \beta = [\beta_0,\ldots,\beta_{N-1}]^T</math> poszukiwanego wielomianu
trygonometrycznego wyrażają się wzorem
<center><math>\displaystyle
\beta = \frac{1}{N} F_N y.
</math></center>


}}
==Macierze specjalne==


{{dowod|||
Zajmiemy się teraz zadaniem rozwiązywania układu równań liniowych
Jak wiemy, <math>\displaystyle \frac{1}{N} F_N^* F_N = I</math>, więc, z symetrii macierzy <math>\displaystyle F_N</math> wynika,
<center><math>
że <math>\displaystyle (\bar{F_N})^{-1} = \frac{1}{N} F_N</math>.
Ax = b</math>,</center>
}}


Można pokazać, że  gdy dane są rzeczywiste, zadanie interpolacji
ale w sytuacji, gdy macierz <math>A</math> jest rozrzedzona i dużego wymiaru. Dokonamy przeglądu kilku rodzajów algorytmów mających na celu wykorzystanie rozrzedzenia macierzy dla obniżenia kosztu wyznaczenia rozwiązania układu.
trygonometrycznej można wyrazić
korzystając wyłącznie z liczb rzeczywistych.
Jeśli <math>\displaystyle y_k\in R</math>, <math>\displaystyle k = 0,\ldots,N-1</math>, to wtedy (rzeczywisty) wielomian trygonometryczny
<center><math>\displaystyle
p(x) = \sum_{k=0}^{N-1} \left( a_k \cos(\frac{2k\pi}{N} x) - b_k \sin(\frac{2k\pi}{N} x)\right),
</math></center>


gdzie <math>\displaystyle a_k = Re(\beta_k)</math>, <math>\displaystyle b_k = Im(\beta_k)</math>, interpoluje <math>\displaystyle y_k</math> w węzłach
Należy pamiętać, że z reguły najlepsze wyniki uzyskuje się, gdy konkretny
<math>\displaystyle x_k</math>. Oczywiście, powyższa formuła w rzeczywistości ma o połowę mniej wyrazów,
algorytm dobierze się do konkretnej macierzy. W zastosowaniach pojawiają się
ze względu na własności funkcji trygonometrycznych.
m.in. macierze rzadkie o bardzo szczególnej strukturze, dla nich warto stosować
wyspecjalizowane algorytmy.


===Splot===
===Macierze  taśmowe===


W niektórych zastosowaniach potrzebne jest wyznaczenie ''splotu'' dwóch
Macierz <math>A</math> taka, że dla <math>|i-j| \geq d</math>, <math>a_{ij} = 0</math>, nazywamy macierzą taśmową
wektorów, to znaczy, wyznaczenie wyrażeń postaci
z rozstępem <math>d</math>, o szerokości pasma <math>2d+1</math>.
<center><math>\displaystyle
z_k = \sum_{j=0}^{N-1} u_j v_{k-j}, \qquad k = 0, \ldots, N-1.
</math></center>


Zapisując to macierzowo, szukamy iloczynu wektora <math>\displaystyle u</math> z cykliczną macierzą Toeplitza
Łatwo sprawdzić, że algorytm eliminacji Gaussa (bez wyboru elementu głównego)
(macierz Toeplitza ma stałe wyrazy wzdłuż diagonali) wyznaczoną przez <math>\displaystyle v</math>,
nie spowoduje dodatkowego wypełnienia w takiej macierzy (a więc da się wykonać w
<center><math>\displaystyle
miejscu). W przypadku konieczności wyboru elementu głównego, pesymistyczne
z =
oszacowanie rozstępu macierzy rozkładu LU jest równe <math>\max\{2d,N\}</math> --- tak
\begin{pmatrix}
więc, dla niezbyt dużych <math>d</math> wciąż wynikowa macierz jest taśmowa. 
v_0 & v_1 & \cdots      & v_{N-1} \\
v_{N-1} & v_0 & \cdots & v_{N-2}\\
\vdots  & \ddots & \ddots & \vdots \\
v_1    &  \cdots    &  v_{N-1} & v_0
\end{pmatrix}  \cdot \begin{pmatrix}  u_0\\u_1\\\vdots\\u_{N-1}\end{pmatrix} ,  
</math></center>


tak więc pozornie zadanie wyznaczenia splotu powinno kosztować tyle, co
W szczególności, gdy macierz jest taśmowa z pasmem o rozstępie <math>d</math> i jednocześnie
mnożenie macierzy przez wektor, a więc <math>\displaystyle O(N^2)</math> operacji. Tymczasem
diagonalnie dominująca, wtedy rozkład LU takiej macierzy da się wykonać w
prosty rachunek pozwala sprawdzić, że odpowiednie transformacje Fouriera,
miejscu kosztem <math>O(d^2\,N)</math>, czyli liniowym względem <math>N</math>.
<math>\displaystyle \hat{z} = F_N z</math>, <math>\displaystyle \hat{u} = F_N u</math>, <math>\displaystyle \hat{v} = F_N v</math>  
spełniają równanie z macierzą diagonalną!


<center><math>\displaystyle
W [http://www.netlib.org/lapack  LAPACKu] zaimplementowano szybki ''solver'' równań z macierzami taśmowymi,
\hat{z} = N \cdot \begin{pmatrix} \hat{v}_0 & & \\
<code style="color: #903">DGBSV</code>, ale wymagający specjalnego sposobu
& \ddots & \\
przechowywania macierzy, wykorzystującego format diagonalny.
& & \hat{v}_{N-1}
\end{pmatrix}  \cdot \hat{u} = N\cdot \begin{pmatrix} \hat{v}_0 \hat{u}_0\\
\vdots  \\
\hat{v}_{N-1}\hat{u}_{N-1}
\end{pmatrix} ,
</math></center>


a to mnożenie daje się wykonać kosztem liniowym, tym samym całe zadanie daje się
===Macierze trójdiagonalne===
policzyć kosztem <math>\displaystyle O(N\log\,N)</math>.


===Biblioteki===
Szczególnym przypadkiem macierzy taśmowych są macierze trójdiagonalne, tzn. taśmowe o
rozstępie <math>d=1</math>:


Najpopularniejszą obecnie biblioteką implementującą algorytm FFT dla DFT, DCT i
<center><math>
innych pokrewnych (bez ograniczenia, że wymiar jest naturalną potęgą dwójki),
T =
jest [http://www.fftw.org FFTW] której nazwa jest skrótowcem od niezbyt
\begin{pmatrix}
skromnie brzmiącego ang. The Fastest Fourier Transform in the West. Z tej
  a_1 & c_1 &  &  & \\
biblioteki korzystają m.in. funkcje MATLABa i Octave'a <code>fft</code> oraz
  b_2 & a_2 & c_2      &  & \\
<code>ifft</code> dla transformacji DFT (to znaczy mnożenia przez <math>\displaystyle F_N</math>) i,
  &  b_3 & a_3 & \ddots & \\
odpowiednio, transformacji odwrotnej, <math>\displaystyle \frac{1}{N}\bar{F}_N</math>.
  & & \ddots & \ddots  & c_{N-1}\\
  &  &      & b_N  & a_N
\end{pmatrix}  
</math></center>


FFTW jest napisana w C i w dużym stopniu wykorzystuje możliwości współczesnych
Zadanie rozwiązywania równań z taką macierzą,
procesorów, takie jak potokowanie i instrukcje wektorowe SSE2 i SSE3. Poniżej
<center><math>
pokazujemy przykładowy prościutki kod w C, realizujący DFT na pojedynczym
T x = e
wektorze zespolonym.
</math></center>


<div class="code" style="background-color:#e8e8e8; padding:1em"><pre>
jest bardzo często spotykane, więc warto przytoczyć algorytm w całej okazałości --- popularnie zwany <strong>algorytmem przeganiania</strong> (niektóre źródła nazywają go ''algorytmem Thomasa'').
   
/* Kompilacja:
gcc -o dft dft.c -lfftw3 -lm */
#include <complex.h> /* rozszerzenie GCC dające operacje na typach zespolonych */
#include <fftw3.h>
#include <math.h>
#define N 8


int main(void)
Zacznijmy jednak od stwierdzenia, kiedy macierz trójdiagonalna  nie wymaga wyboru elementu głównego:
{
fftw_complex *F, *C;
fftw_plan Plan;
double normfactor <nowiki>=</nowiki> 1.0/N;
int i;


F <nowiki>=</nowiki> fftw_malloc( N * sizeof(fftw_complex) );
{{stwierdzenie|||
C <nowiki>=</nowiki> fftw_malloc( N * sizeof(fftw_complex) );
Jeśli macierz <math>T</math> ma <strong>słabo dominującą diagonalę</strong>, tzn.


for( i <nowiki>=</nowiki> 0; i < N; i++ ) /* inicjalizacja wartości tablicy F */
<center><math>|a_i|\,\ge\,|b_i|\,+\,|c_i|,\qquad 1\le i\le N</math>,</center>
{
F[i] <nowiki>=</nowiki> i*M_PI*(1-0.5*I);
}


Plan <nowiki>=</nowiki> fftw_plan_dft_1d( N, F, C, FFTW_FORWARD, FFTW_ESTIMATE );
(<math>b_1=0=c_N</math>) i przynajmniej dla jednego indeksu "<math>i</math>" mamy powyżej
 
ostrą nierówność "<math>></math>", to algorytm przeganiania jest wykonalny bez
fftw_execute( Plan );
przestawień wierszy. Ponadto wymaga on <math>7N</math> operacji arytmetycznych,
 
a więc jest prawie optymalny.
for( i <nowiki>=</nowiki> 0; i < N; i++ ) /* normalizacja wyznaczonego C */
}}
C[i] *<nowiki>=</nowiki> normfactor;


for( i <nowiki>=</nowiki> 0; i < N; i++ )
{{algorytm|Metoda przeganiania (w miejscu)|Metoda przeganiania|
<Source>
for (i = 2; i <= N; i++)
{
{
printf("F[i, creal(F[i]), cimag(F[i]), i, creal(C[i]), cimag(C[i]));  
<math>l</math> = <math>b_i</math>/<math>a_{i-1}</math>;
}
<math>a_i</math> = <math>a_i</math> - <math>l</math> * <math>c_{i-1}</math>;
<math>e_i</math> = <math>e_i</math> - <math>l</math> * <math>e_{i-1}</math>;
}
<math>x_N</math> = <math>e_N</math>/<math>a_N</math>;
for (i = N-1; i >= 1; i--)
<math>x_i</math> = (<math>e_i</math> - <math>c_i</math> * <math>x_{i+1}</math>)/<math>a_i</math>;
</Source>}}


/* .... teraz moglibyśmy zmienić wartości F i ponownie wyznaczyć C,
==Metody bezpośrednie==
korzystając z tego samego Planu! */


/* sprzątamy */
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<span  style="font-variant:small-caps;">Przykład: Strzałka Wilkinsona</span>
<div class="solution" style="margin-left,margin-right:3em;">


fftw_destroy_plan( Plan );
Rozważmy układ równań z macierzą diagonalnie dominującą postaci
fftw_free( F );
fftw_free( C );


return(0);
<center><math>A = \begin{pmatrix}
}
* & * & * & \cdots & * \\
</pre></div>
* & * &        &  & \\
   
* &  & *      &  &  \\
Zwrócmy uwagę na linię <code>Plan<nowiki>=</nowiki>fftw_plan_dft_1d(...)</code>. To tutaj dokonywane są
\vdots & & & \ddots  & \\
ustalenia, w jakiej kolejności mają być prowadzone obliczenia. Jest to operacja
* &  &      &  & *
dość kosztowna, dlatego jeśli mamy wyznaczyć wiele takich samych DFT, tylko na
  \end{pmatrix}
różnych danych, należy przed pierwszą DFT taki plan zachować, a potem już gotowy
</math></center>
wykorzystać, gdy chcemy aplikować DFT dla następnych danych.


==Interpolacja splajnowa==
gdzie <math>*</math> oznacza jakiś niezerowy element.
Łatwo sprawdzić, że chociaż wyjściowa macierz jest rozrzedzona, to zastosowanie do niej eliminacji Gaussa powoduje, że w wyniku
dostajemy <strong>gęste</strong> czynniki rozkładu.


Interpolacja  wielomianami interpolacyjnymi, chociaż korzysta z funkcji gładkich
Tymczasem wystarczy odwrócić kolejność równań i numerację niewiadomych (co dla
i łatwo reprezentowalnych w komputerze, ma jednak również pewne wady.
macierzy jest równoznaczne z odwróceniem porządku wierszy i kolumn, korzystając
Zauważmy, że błąd interpolacji może być bardzo duży (zjawisko Rungego), a poza
z pewnej (jakiej?) macierzy permutacji <math>P</math>):
tym, interpolacja jest nielokalna: nawet mała zmiana warości funkcji w pojedynczym węźle
może powodować dużą zmianę zachowania całego wielomianu interpolacyjnego.
Czasem więc lepiej jest zastosować innego rodzaju
interpolację, np. posługując się funkcjami sklejanymi. 


===Co to są funkcje sklejane?===
<center><math>\widetilde{A} = PAP^T = \begin{pmatrix}
* &        &  & & *\\
  &  \ddots  & & & \vdots\\
  &      & * &  & * \\
    &        &  &  * & * \\
* & \cdots & * & * & *
\end{pmatrix}
</math></center>


W ogólności przez funkcję sklejaną rozumie się każdą
Wtedy okazuje się, że rozkład naszej macierzy nie powoduje już ''wypełnienia'' czynników rozkładu!
funkcję przedziałami wielomianową. Nas będą jednak
</div></div>
interesować szczególne funkcje tego typu i dlatego termin
''funkcje sklejane'' zarezerwujemy dla funkcji
przedziałami wielomianowych i posiadających dodatkowe
własności, które teraz określimy.


Niech dany będzie przedział skończony <math>\displaystyle [a,b]</math> i węzły
Właśnie na tym polega główny problem w rozwiązywaniu układów z macierzami
rzadkimi metodami bezpośrednimi: jak maksymalnie wykorzystać rozrzedzenie macierzy tak, by czynniki rozkładu były możliwie mało wypełnione. Albowiem wiedząc to będziemy mogli ograniczyć się jedynie do fizycznego wyznaczenia wartości <strong>niezerowych</strong> elementów macierzy rozkładu. Ponadto wymagania pamięciowe algorytmu nie będą istotnie wykraczać ponad ilość pamięci potrzebnej na przechowanie danych (tzn. macierzy).


<center><math>\displaystyle a \,=\, x_0 \,<\, x_1 \,<\, \cdots \,<\, x_n \,=\, b,
W ogólnym przypadku rozwiązanie takiego zadania jest trudne i większość
</math></center>
algorytmów opiera się na pewnych heurystykach, które jednak w praktyce warto wspomóc wcześniejszą analizą konkretnego układu równań jaki mamy rozwiązać. Najczęściej dąży się do takiego przenumerowania równań i niewiadomych, by w efekcie z góry przewidzieć, gdzie wystąpią zera w macierzach rozkładu --- i, by takich zer było jak najwięcej (by wypełnienie było jak najmniejsze)! Na architekturach z pamięcią hierarchiczną dąży się także do tego, by w trakcie rozkładu można było korzystać z BLAS Level 3, a więc permutacje wierszy i kolumn macierzy muszą to także brać pod uwagę.


przy czym <math>\displaystyle n\ge 1</math>.
Stosuje się kilka strategii wyznaczania korzystnych permutacji
 
(''reorderingu''), z których warto wymienić
{{definicja|||
* przybliżone algorytmy minimalnego stopnia (''approximate minimum degree, AMD''), np. [http://www.cise.ufl.edu/research/sparse/amd AMD];
Funkcję <math>\displaystyle s:R\toR</math> nazywamy funkcją
* techniki podziału grafów na (prawie) rozłączne składowe ''nested dissection, ND'', np. [http://glaros.dtc.umn.edu/gkhome/views/metis  METIS].
sklejaną rzędu <math>\displaystyle r</math> (<math>\displaystyle r\ge 1</math>) odpowiadającą węzłom
<math>\displaystyle x_j</math>, <math>\displaystyle 0\le j\le n</math>, jeśli spełnione są następujące
dwa warunki:
   
; (i)
:  <math>\displaystyle s</math> jest wielomianem stopnia co najwyżej <math>\displaystyle 2r-1</math> na każdym
z przedziałów <math>\displaystyle [x_{j-1},x_j]</math>, tzn.  
<math>\displaystyle s_{|[x_{j-1},x_j]}\in\Pi_{2r-1}</math>, <math>\displaystyle 1\le j\le n</math>,
 
; (ii)
: <math>\displaystyle s</math> jest <math>\displaystyle (2r-2)</math>-krotnie różniczkowalna w sposób
ciągły na całej prostej, tzn. <math>\displaystyle s\in C^{(2r-2)}(R)</math>.
Jeśli ponadto
; (iii)
:  <math>\displaystyle s</math> jest wielomianem stopnia co najwyżej <math>\displaystyle r-1</math> poza
<math>\displaystyle (a,b)</math>, tzn. <math>\displaystyle s_{|(-\infty,a]},s_{|[b,+\infty)}\in\Pi_{r-1}</math>,
   
   
to <math>\displaystyle s</math> jest naturalną funkcją sklejaną.  
Używają ich biblioteki takie jak [http://www.cise.ufl.edu/research/sparse/umfpack  UMFPACK], czy [http://www.cse.clrc.ac.uk/nag/hsl  HSL].
}}


Klasę naturalnych funkcji sklejanych rzędu <math>\displaystyle r</math> opartych na
W Octave mamy do dyspozycji także kilka procedur generujących takie permutacje, w tym: <code style="color: #006">colamd</code> (AMD dla macierzy niesymetrycznych) oraz <code style="color: #006">symamd</code> (AMD dla macierzy symetrycznych). Większy wybór oferuje MATLAB, jednak należy bezwzględnie pamiętać o jednym: nie ma uniwersalnej metody ''reorderingu'' i dla konkretnej macierzy może istnieć specjalna metoda, która da oszałamiające rezultaty, podczas gdy standardowe podejścia nie dadzą efektu.
węzłach <math>\displaystyle x_j</math> będziemy oznaczać przez
<math>\displaystyle {\cal S}_r(x_0,\ldots,x_n)</math>, albo po prostu <math>\displaystyle {\cal S}_r</math>,  
jeśli węzły są ustalone.  


Na przykład, funkcją sklejaną rzędu pierwszego (<math>\displaystyle r=1</math>)
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
jest funkcja ciągła i liniowa na poszczególnych
<span  style="font-variant:small-caps;">Przykład: Rozwiązywanie układu z macierzą rozrzedzoną w Octave</span>  
przedziałach <math>\displaystyle [x_{j-1},x_j]</math>. Jest ona naturalna, gdy poza
<div class="solution" style="margin-left,margin-right:3em;">
<math>\displaystyle (a,b)</math> jest funkcją stała. Tego typu funkcje nazywamy
''liniowymi funkcjami sklejanymi''.


Najważniejszymi a praktycznego punktu widzenia są jednak
Najprostszy sposób na wykorzystanie metody bezpośredniego rozwiązywania układu z macierzą rzadką to zastosowanie znanego nam operatora do macierzy typu rozrzedzonego:
funkcje sklejane rzędu drugiego odpowiadające <math>\displaystyle r=2</math>. Są
to funkcje, które są na <math>\displaystyle R</math> dwa razy różniczkowalne w
sposób ciągły, a na każdym z podprzedziałów są
wielomianami stopnia co najwyżej trzeciego. W tym przypadku
mówimy o ''kubicznych funkcjach sklejanych''. Funkcja
sklejana kubiczna jest naturalna, gdy poza <math>\displaystyle (a,b)</math> jest
wielomianem liniowym.


===Interpolacja i gładkość===
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>A = sparse(...);
x = A \ b;
</pre></div>
Octave domyślnie przyłoży do <math>A</math> reordering <code style="color: #006">colamd</code> i następnie skorzysta z biblioteki UMFPACK, by rozwiązać taki układ. Dodatkowo, badane jest wcześniej, czy macierz nie jest przypadkiem taśmowa o wąskiej wstędze: jeśli jest, to korzysta się z LAPACKa.


Pokażemy najpierw ważny lemat, który okaże się kluczem
(Jak widzisz, operator rozwiązywania układu równań jest mocno przeciążony: działa i na macierzach kwadratowych, i na prostokątnych, i na rozrzedzonych --- na każdym rodzaju w inny sposób. Nie dziwi Cię chyba, dlaczego wygodnie było napisać Octave w C++?)
do dowodu dalszych twierdzeń.  


Niech <math>\displaystyle W^r(a,b)</math> będzie klasą funkcji <math>\displaystyle f:[a,b]\toR</math> takich,
</div></div>
że <math>\displaystyle f</math> jest <math>\displaystyle (r-1)</math> razy różniczkowalna na <math>\displaystyle [a,b]</math> w sposób
ciągły oraz <math>\displaystyle f^{(r)}(x)</math> istnieje prawie wszędzie na <math>\displaystyle [a,b]</math>  
i jest całkowalna z kwadratem, tzn.


<center><math>\displaystyle \aligned W^r(a,b) &= \{\,f\in C^{(r-1)}([a,b]):\, f^{(r)}(x)
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
    \mbox{ istnieje p.w. na  }  [a,b] \\
<span style="font-variant:small-caps;">Przykład: Wypełnienie pewnej macierzy w zależności od użytego reorderingu</span>  
    && \qquad\qquad\qquad\qquad  \mbox{  oraz  }
<div class="solution" style="margin-left,margin-right:3em;">
      f^{(r)}\in{\cal L}_2(a,b)\,\}.
\endaligned</math></center>


Oczywiście każda funkcja sklejana rzędu <math>\displaystyle r</math> (niekoniecznie
Rozważmy ponownie macierz pochodzącą z kolekcji [http://www.cise.ufl.edu/research/sparse/matrices/Boeing  Tima Davisa]. Jest to macierz symetryczna, dodatnio określona, wymiaru 8032, o 355460 elementach niezerowych i, w konsekwencji, o wypełnieniu około 0.6 procent.  
naturalna) należy do <math>\displaystyle W^r(a,b)</math>.  


{{lemat|||
[[Image:MNsparseA.png|thumb|550px|center|Struktura niezerowych elementów macierzy.]]
Niech <math>\displaystyle f\in W^r(a,b)</math> będzie funkcją
zerującą się w węzłach, tzn.  


<center><math>\displaystyle f(x_j)\,=\,0,\qquad 0\le j\le n.
Zobaczymy, jak w zależności od użytego algorytmu permutacji kolumn i
</math></center>
wierszy poradzi sobie algorytm rozkładu Cholesky'ego.
 
Wtedy dla dowolnej naturalnej funkcji sklejanej <math>\displaystyle s\in {\cal S}_r</math>
mamy
 
<center><math>\displaystyle \int_a^b f^{(r)}(x)s^{(r)}(x)\,dx\,=\,0.  
</math></center>
 
}}


\noindent
[[Image:MNsparsechol.png|thumb|550px|center|Czynnik rozkładu Cholesky'ego <math>L</math> wykonanego
''Dowód''\quad Dla <math>\displaystyle r=1</math> funkcja <math>\displaystyle s'</math> jest przedziałami stała.  
standardowym algorytmem. Czas rozkładu: 0.892013]]
Oznaczając przez <math>\displaystyle a_j</math> jej wartość na <math>\displaystyle [x_{j-1},x_j]</math> dostajemy


<center><math>\displaystyle \aligned \int_a^b f'(x)s'(x)\,dx &=
[[Image:MNsparsecholcolamd.png|thumb|550px|center|Czynnik rozkładu Cholesky'ego <math>L</math> z reorderingiem
          \sum_{j=1}^n\int_{t_{j-1}}^{t_j} f'(x)a_j\,dx \\
COLAMD. Czas rozkładu: 0.813038]]
        &= \sum_{j=1}^n a_j(f(x_j)-f(x_{j-1}))\,=\,0,
\endaligned</math></center>


ponieważ <math>\displaystyle f</math> zeruje się w <math>\displaystyle t_j</math>.  
[[Image:MNsparsecholsymamd.png|thumb|550px|center|Czynnik rozkładu Cholesky'ego <math>L</math> z reorderingiem
SYMAMD. Czas rozkładu: 0.487683s. Prawie dwa razy
szybciej niż bez reorderingu, chociaż i tak wskutek wzrostu wypełnienia macierzy
w dolnym trójkącie mamy aż 2 procent niezerowych elementów.]]


Rozpatrzmy teraz przypadek <math>\displaystyle r\ge 2</math>. Ponieważ
[[Image:MNsparsecholsymrcm.png|thumb|550px|center|Czynnik rozkładu Cholesky'ego <math>L</math> z odwrotnym reorderingiem
Cuthill-McKee. Czas rozkładu: 0.845928]]


<center><math>\displaystyle (f^{(r-1)}s^{(r)})'\,=\,f^{(r)}s^{(r)}\,+\,f^{(r-1)}s^{(r+1)},
[[Image:MNsparsecholcolperm.png|thumb|550px|center|Czynnik rozkładu Cholesky'ego <math>L</math> z jeszcze
</math></center>
innym (bardzo tanim, ale jak widać czasem zupełnie złym) reorderingiem. Czas rozkładu: 5.947936]]


to
Na zakończenie popatrzmy, jak ważne jest spostrzeżenie symetrii macierzy:


<center><math>\displaystyle \int_a^b f^{(r)}(x)s^{(r)}(x)\,dx\,=\,f^{(r-1)}(x)s^{(r)}(x)
[[Image:MNsparseLlu.png|thumb|550px|center|Rozkład LU, czynnik <math>L</math> (bez reorderingu). Czas rozkładu LU: 1.696018s. Nieakceptowalny, podobnie
    \Big |_a^b \,-\,\int_a^b f^{(r-1)}(x)s^{(r+1)}(x)\,dx.
jak drastyczne wypełnienie macierzy.]]
</math></center>


Wobec tego, że <math>\displaystyle s</math> jest poza przedziałem <math>\displaystyle (a,b)</math> wielomianem stopnia
[[Image:MNsparseUlu.png|thumb|550px|center|Rozkład LU, czynnik <math>U</math> (bez reorderingu)]]
co najwyżej <math>\displaystyle r-1</math> oraz <math>\displaystyle s^{(r)}</math> jest ciągła na <math>\displaystyle R</math>, mamy
<math>\displaystyle s^{(r)}(a)=0=s^{(r)}(b)</math>, a stąd


<center><math>\displaystyle f^{(r-1)}(x)s^{(r)}(x)\Big |_a^b\,=\,0.  
Jak widać, w naszym przypadku standardowe algorytmy (COLAMD i SYMAMD) poradziły sobie całkiem nieźle, chociaż wypełnienie i tak znacząco wzrosło. Zapewne, dla tej macierzy, która jest specyficznego typu --- pochodzi z dyskretyzacji równania różniczkowego --- algorytm ND mógłby tu jeszcze lepiej zadziałać.
</math></center>


Postępując podobnie, tzn. całkując przez części <math>\displaystyle r-1</math> razy
</div></div>
otrzymujemy w końcu


<center><math>\displaystyle \int_a^b f^{(r)}(x)s^{(r)}(x)\,dx\,=\,(-1)^{r-1}
==Stacjonarne metody iteracyjne==
      \int_a^b f'(x)s^{(2r-1)}(x)\,dx.
</math></center>


Funkcja <math>\displaystyle s^{(2r-1)}</math> jest przedziałami stała, a więc możemy  
Gdy macierz jest rozrzedzona, mnożenie takiej macierzy przez wektor jest bardzo tanie (koszt jest proporcjonalny do liczby niezerowych elementów macierzy).
teraz zastosować ten sam argument jak dla <math>\displaystyle r=1</math>, aby pokazać,  
Dlatego, jeśli możemy zadowolić się <strong>rozwiązaniem przybliżonym</strong> układu, a w zamian osiągnąć je tanim kosztem, warto rozważyć metody iteracyjne.
że ostatnia całka jest równa zeru. <math>\displaystyle \quad\Box</math>


Funkcje sklejane chcielibyśmy zastosować do interpolacji funkcji.  
Najprostsze metody iteracyjne (najprostsze w analizie i implementacji, ale ---
Ważne jest więc, aby odpowiednie zadanie interpolacyjne miało
jak można się domyślić --- w praktyce najmniej efektywne) polegają na rozkładzie
jednoznaczne rozwiązanie.
macierzy na część "łatwo odwracalną", <math>M</math>, i "resztę", <math>Z</math>. Dokładniej,
jeśli <math>M</math> jest nieosobliwa, to równanie <math>Ax = b</math> można zapisać jako
zadanie punktu stałego


{{twierdzenie|||
<center><math>Mx = Zx + b</math>,</center>
Jeśli <math>\displaystyle n+1\ge r</math>, to dla dowolnej funkcji
<math>\displaystyle f:[a,b]\toR</math> istnieje dokładnie jedna naturalna funkcja sklejana
<math>\displaystyle s_f\in {\cal S}_r</math> interpolująca <math>\displaystyle f</math> w węzłach <math>\displaystyle x_j</math>, tzn. taka,
że


<center><math>\displaystyle s_f(x_j)\,=\,f(x_j),\qquad 0\le j\le n.
gdzie <math>Z=M-A</math>. Inaczej:
</math></center>


}}
<center><math>x = M^{-1}(Zx + b)</math>,</center>


\noindent
i zastosować doń [[MN02#Iteracja prosta Banacha|metodę iteracji prostej Banacha]]:
''Dowód''\quad Pokażemy najpierw, że jedyną naturalną
funkcją sklejaną interpolującą dane zerowe jest funkcja zerowa.
Rzeczywiście, jeśli <math>\displaystyle s(x_j)=0</math> dla <math>\displaystyle 0\le j\le n</math>, to podstawiając
w Lemacie [[##bwazny|Uzupelnic: bwazny ]] <math>\displaystyle f=s</math> otrzymujemy


<center><math>\displaystyle \int_a^b \Big(s^{(r)}(x)\Big)^2\,dx\,=\,0.
<center><math>x_{k+1} = M^{-1}(Zx_k + b)</math></center>
</math></center>


Stąd <math>\displaystyle s^{(r)}</math> jest funkcją zerową, a więc <math>\displaystyle s</math> jest
Takie metody nazywamy stacjonarnymi metodami iteracyjnymi. Aby przeanalizować zbieżność takiej metody, warto rozpatrzyć przypadek ogólniejszy
wielomianem stopnia co najwyżej <math>\displaystyle r-1</math> zerującym się w co
najmniej <math>\displaystyle n+1</math> punktach <math>\displaystyle x_j</math>. Wobec tego, że <math>\displaystyle n+1>r-1</math>,
otrzymujemy <math>\displaystyle s\equiv 0</math>.


Zauważmy teraz, że problem znalezienia naturalnej funkcji sklejanej
<center><math>
<math>\displaystyle s</math> interpolującej <math>\displaystyle f</math> można sprowadzić do rozwiązania kwadratowego
  x_k\,=\,B x_{k-1}\,+\, c</math>,</center>
układu równań liniowych. Na każdym przedziale <math>\displaystyle [x_{i-1},x_i]</math>,  
<math>\displaystyle 1\le i\le n</math>, jest ona postaci


<center><math>\displaystyle s(x)\,=\,w_i(x)\,=\,\sum_{j=0}^{2r-1} a_{i,j}x^j,
dla pewnej macierzy <math>B</math> oraz wektora <math>c</math>. (Dla stacjonarnej metody iteracyjnej, <math>B=M^{-1}Z</math> oraz <math>c=M^{-1}b</math>.)
</math></center>


dla pewnych współczynników <math>\displaystyle a_{i,j}\inR</math>, a na
W  tym przypadku
<math>\displaystyle (-\infty,a]</math> i <math>\displaystyle [b,\infty)</math> mamy odpowiednio


<center><math>\displaystyle s(x)\,=\,w_0(x)\,=\,\sum_{j=0}^{r-1} a_{0,j}x^j
<center><math>x_k- x^*\,=\,B^k( x_0- x^*),  
</math></center>
</math></center>


i
a stąd i z nierówności <math>\|B^k\|\le\|B\|^k</math>, mamy


<center><math>\displaystyle s(x)\,=\,w_{n+1}(x)\,=\,\sum_{j=0}^{r-1} a_{n+1,j}x^j.  
<center><math>\| x_k- x^*\|\,\le\,
          \|B\|^k\| x_0- x^*\|.  
</math></center>
</math></center>


Aby wyznaczyć <math>\displaystyle s</math>, musimy więc znaleźć ogółem <math>\displaystyle 2r(n+1)</math>  
Warunkiem dostatecznym zbieżności iteracji prostych
współczynników <math>\displaystyle a_{i,j}</math>, przy czym są one związane
jest więc <math>\|B\|<1</math>. Okazuje się, że warunkiem koniecznym i dostatecznym
<math>\displaystyle (2r-1)(n+1)</math> warunkami jednorodnymi wynikającymi z gładkości,
zbieżności tej iteracji dla dowolnego wektora startowego <math>x_0</math> jest


<center><math>\displaystyle w_i^{(k)}(x_i)\,=\,w_{i+1}^{(k)}(x_i)
<center><math>\rho = \max\{ |\lambda| : \lambda  \mbox{ jest wartością własną } B\} < 1</math></center>
</math></center>


dla <math>\displaystyle 0\le i\le n</math> i <math>\displaystyle 0\le k\le 2r-2</math>, oraz <math>\displaystyle n+1</math>  
Tak więc, metody oparte na iteracji prostej będą zbieżne liniowo z ilorazem
niejednorodnymi warunkami interpolacyjnymi,
<math>\rho</math>.


<center><math>\displaystyle w_i(x_i)\,=\,f(x_i)
Zaletą stacjonarnych metod iteracyjnych jest również ich prostota,  
</math></center>
przez co są one łatwe do zaprogramowania, co łatwo zobaczyć na przykładach metod: [[#Metoda Jacobiego|Jacobiego]] i [[#Metoda Gaussa-Seidela|Gaussa--Seidela]], które teraz omówimy.


dla <math>\displaystyle 0\le i\le n</math>. Otrzymujemy więc układ <math>\displaystyle 2r(n+1)</math>
===Metoda Jacobiego===
równań liniowych ze względu na <math>\displaystyle 2r(n+1)</math> niewiadomych
<math>\displaystyle a_{i,j}</math>.


Naturalna funkcja sklejana interpolująca <math>\displaystyle f</math> jest
Biorąc <math>M =  \mbox{diag} (A)</math>, gdzie <math>\mbox{diag} (A)</math> jest macierzą diagonalną składającą się
wyznaczona jednoznacznie wtedy i tylko wtedy, gdy układ ten
z wyrazów stojących na głównej przekątnej macierzy
ma jednoznaczne rozwiązanie. To zaś zachodzi, gdy zero jest
<math>A</math>, układ <math>A x= b</math> jest równoważny układowi
jedynym rozwiązaniem układu jednorodnego. Rzeczywiście,
układ jednorodny odpowiada zerowym warunkom interpolacyjnym,
przy których, jak pokazaliśmy wcześniej, zerowa funkcja
sklejana (której odpowiada <math>\displaystyle a_{i,j}=0</math>, <math>\displaystyle \forall i,j</math>
jest jedynym rozwiązaniem zadania interpolacyjnego. 
<math>\displaystyle \quad\Box</math>


Naturalne funkcje sklejane możemy więc używać do
<center><math>M x = Z x +  b</math>,</center>
interpolacji funkcji. Pokażemy teraz inną ich własność,  
która jest powodem dużego praktycznego zainteresowania
funkcjami sklejanymi.


{{twierdzenie|||
a stąd (o ile na przekątnej macierzy <math>A</math> nie mamy zera)
Niech <math>\displaystyle f\in W^r(a,b)</math> i niech <math>\displaystyle s_f\in {\cal S}_r</math>
otrzymujemy metodę iteracyjną
będzie naturalną funkcją sklejaną rzędu <math>\displaystyle r</math> interpolującą
<math>\displaystyle f</math> w węzłach <math>\displaystyle x_j</math>, <math>\displaystyle 0\le j\le n</math>. Wtedy


<center><math>\displaystyle
<center><math>x_k\,=\,B x_{k-1}\,+\, c,  
  \int_a^b \Big(  f^{(r)}(x)\Big)^2\,dx\,\ge\,
  \int_a^b \Big(s_f^{(r)}(x)\Big)^2\,dx.
</math></center>
</math></center>


}}
gdzie <math>B=M^{-1}Z</math> i <math>c=M^{-1} b</math>, zwaną
<strong>metodą Jacobiego</strong>.


\noindent
Rozpisując ją po współrzędnych dostajemy (numer iteracji wyjątkowo zaznaczamy w postaci ''górnego'' indeksu) układ rozszczepionych równań:
''Dowód''\quad Jeśli przedstawimy <math>\displaystyle f</math> w postaci
<math>\displaystyle f=s_f+(f-s_f)</math>, to


<center><math>\displaystyle \aligned \int_a^b\Big(f^{(r)}(x)\Big)^2\,dx 
<center><math>x^{(k)}_i = \frac{1}{a_{ii}} \left( b_i - \sum_{j\neq i} a_{ij}x^{(k-1)}_j \right)</math>,</center>
    &= \int_a^b\Big(s_f^{(r)}(x)\Big)^2\,dx\,+\,
        \int_a^b\Big((f-s_f)^{(r)}(x)\Big)^2\,dx \\
    & & \qquad\qquad 2\,\int_a^b s_f^{(r)}(x)(f-s_f)^{(r)}(x)\,dx.
\endaligned</math></center>


Funkcja <math>\displaystyle f-s_f</math> jest w klasie <math>\displaystyle W^r(a,b)</math> i zeruje się w węzłach
co znaczy dokładnie tyle, że w <math>i</math>-tym równaniu wyjściowego układu przyjmujemy za współrzędne <math>x</math> wartości z poprzedniej iteracji i na tej podstawie wyznaczamy wartość <math>x_i</math>.
<math>\displaystyle x_j</math>, <math>\displaystyle 0\le j\le n</math>. Z Lematu [[##bwazny|Uzupelnic: bwazny ]] wynika więc, że
<math>\displaystyle \int_a^b s_f^{(r)}(x)(f-s_f)^{(r)}(x)dx=0</math>, a stąd ([[##nst|Uzupelnic: nst ]]).
<math>\displaystyle \quad\Box</math>


Wartość całki <math>\displaystyle \int_a^b(f^{(r)}(x))^2 dx</math> może być w
{{twierdzenie|O zbieżności metody Jacobiego|O zbieżności metody Jacobiego|
ogólności uważana za miarę gładkości funkcji.
Nierówność ([[##nst|Uzupelnic: nst ]]) możemy więc zinterpretować w
następujący sposób. Naturalna funkcja sklejana jest w
klasie <math>\displaystyle W^r(a,b)</math> najgładszą funkcją spełniającą dane
warunki interpolacyjne w wybranych węzłach <math>\displaystyle x_j</math>.


Konsekwencją Lematu [[##bwazny|Uzupelnic: bwazny ]] są również inne
W metodzie Jacobiego warunek dostateczny zbieżności,
aproksymacyjne własności naturalnych funkcji sklejanych,
<math>\|B\|<1</math>, jest spełniony np. wtedy, gdy macierz <math>A</math> ma
zob. U. [[##optspln|Uzupelnic: optspln ]].  
dominującą przekątną, tzn. gdy


Jak już wspomnieliśmy, najczęściej używanymi są kubiczne
<center><math>
funkcje sklejane. Dlatego rozpatrzymy je oddzielnie.
  |a_{i,i}|\,>\,\sum_{j\neq i}|a_{i,j}|,\qquad \forall i = 1,\ldots, N</math></center>


===Kubiczne funkcje sklejane===
}}


Jeśli zdecydowaliśmy się na użycie kubicznych funkcji
{{dowod|||
sklejanych to powstaje problem wyznaczenia <math>\displaystyle s_f\in{\cal S}_2</math>  
Rzeczywiście, ponieważ wyraz <math>(i,j)</math> macierzy <math>M^{-1}Z</math>  
interpolującej daną funkcję <math>\displaystyle f</math>, tzn. takiej, że
wynosi <math>0</math> dla <math>i=j</math> oraz <math>a_{i,j}/a_{i,i}</math> dla <math>i\neq j</math>, a więc
<math>\displaystyle s_f(x_i)=f(x_i)</math> dla <math>\displaystyle 0\le i\le n</math>. W tym celu, na każdym
przedziale <math>\displaystyle [x_i,x_{i+1}]</math> przedstawimy <math>\displaystyle s_f</math> w postaci jej
rozwinięcia w szereg Taylora w punkcie <math>\displaystyle x_i</math>,


<center><math>\displaystyle s_f(x)\,=\,w_i(x)\,=\,a_i+b_i(x-x_i)+
<center><math>\begin{align} \|M^{-1}Z\|_\infty \;=\; \max_{1\le i\le N}
    c_i\frac{(x-x_i)^2}2+d_i\frac{(x-x_i)^3}6,
  \sum_{j=1,j\ne i}^N{|a_{i,j}|}/{|a_{i,i}|}  \\
</math></center>
    && =\;\max_{1\le i\le N}
      \sum_{j=1}^N{|a_{i,j}|}/{|a_{i,i}|}\,-\,1\;<\;1,
\end{align}</math></center>


i podamy algorytm obliczania <math>\displaystyle a_i,b_i,c_i,d_i</math>
przy czym ostatnia nierówność wynika z warunku diagonalnej dominacji.  
dla <math>\displaystyle 0\le i\le n-1</math>.  
}}
 
Warunki brzegowe i warunki ciągłości
dla <math>\displaystyle s_f''</math> dają nam <math>\displaystyle w_0''(x_0)=0=w_{n-1}''(x_n)</math> oraz
<math>\displaystyle w_i''(x_{i+1})=w_{i+1}''(x_{i+1})</math>, czyli
 
<center><math>\displaystyle \aligned c_0 &= 0, \\ 
  c_i+d_ih_i &= c_{i+1}, \qquad 0\le i\le n-2, \\
  c_{n-1}+d_{n-1}h_{n-1} &= 0,
\endaligned</math></center>


gdzie <math>\displaystyle h_i=x_{i+1}-x_i</math>. Stąd, przyjmując dodatkowo
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<math>\displaystyle c_n=0</math>, otrzymujemy
<span  style="font-variant:small-caps;">Przykład: Macierz laplasjanu</span>  
<div class="solution" style="margin-left,margin-right:3em;">


<center><math>\displaystyle
Macierz <math>N\times N</math>, zwana <strong>macierzą jednowymiarowego laplasjanu</strong>
  d_i\,=\,\frac{c_{i+1}-c_i}{h_i},\qquad 1\le i\le n-1.
<center><math>
L = \begin{pmatrix}
2 & -1 &      &  \\
-1 & 2 & \ddots & \\
    & \ddots & \ddots & -1 \\
    &      &  -1 & 2
\end{pmatrix}
</math></center>
</math></center>


Z warunków ciągłości dla <math>\displaystyle s_f'</math> dostajemy z kolei
pojawia się w bardzo wielu zastosowaniach, także jako podzadanie w algorytmach
numerycznych. Ta macierz jest macierzą taśmową, symetryczną i dodatnio
określoną,  więc układ równań z tą macierzą można bez trudu rozwiązać metodami
bezpośrednimi, kosztem <math>O(N)</math>. Stosując do niej metodę Jacobiego mamy <math>M = 2I</math> oraz <math>Z = L - M</math>. Obliczając
normę macierzy iteracji Jacobiego dostajemy <math>||M^{-1}Z||_\infty
= 1</math>, co nie rozstrzyga jeszcze o jej zbieżności lub niezbieżności.


<center><math>\displaystyle b_i+c_ih_i+d_ih_i^2//2\,=\,b_{i+1},
Potrzebna będzie bardziej subtelna analiza. Okazuje się, że są znane wzory na wartości własne macierzy <math>L</math>:
    \qquad 0\le i\le n-2,
</math></center>


i uwzględniając ([[##dei|Uzupelnic: dei ]])  
<center><math>\lambda_j = 4\sin^2 \left(\frac{j \pi }{2(N+1)} \right)</math>,</center>


<center><math>\displaystyle
dla <math>1 \leq j \leq N</math>. W konsekwencji, wartościami własnymi <math>M^{-1}Z = \frac{1}{2}Z = \frac{1}{2}L - I</math> są liczby <math>\mu_i = \frac{1}{2} \lambda_i - 1</math>. Ponieważ <math>0 < \mu_i < 1</math>, znaczy to, że metoda Jacobiego jest zbieżna dla macierzy <math>L</math>.
  b_{i+1}\,=\,b_i+h_i(c_{i+1}+c_i)//2,
    \qquad 0\le i\le n-2.  
</math></center>


Warunki ciągłości <math>\displaystyle s_f</math> dają w końcu
Z drugiej strony, nie dajmy się zwieść optymizmowi matematyka (''"nie martw się, jest zbieżny..."''): nietrudno sprawdzić, że <math>||Z||_2 = 1 - O(N^{-2}) < 1</math>, co oznacza, że metoda Jacobiego --- choć zbieżna --- dla dużych <math>N</math> staje się
zbieżna tak wolno, że w praktyce bezużyteczna!
</div></div>


<center><math>\displaystyle
===Metoda Gaussa-Seidela===
  a_i+b_ih_i+c_ih_i^2//2+d_ih_i^3//6\,=\,a_{i+1},
    \qquad 0\le i\le n-2.
</math></center>


Równania ([[##dei|Uzupelnic: dei ]]),([[##bei|Uzupelnic: bei ]]) i ([[##aai|Uzupelnic: aai ]]) definiują
<!--  
nam na odcinku <math>\displaystyle [a,b]</math> naturalną kubiczną funkcję
Ciekawostką jest, że Gauss nie miał z nią nic wspólnego, a Seidel był podobno jej przeciwnikiem...
sklejaną. Ponieważ poszukiwana funkcja sklejana <math>\displaystyle s_f</math> ma
-->
interpolować <math>\displaystyle f</math>, mamy dotatkowych <math>\displaystyle n+1</math> warunków
interpolacyjnych <math>\displaystyle w_i(x_i)=f(x_i)</math>, <math>\displaystyle 0\le i\le n-1</math>,
Heurystyka tej metody opiera się na zmodyfikowaniu metody Jacobiego tak, by w każdym momencie iteracji korzystać z najbardziej "aktualnych" współrzędnych przybliżenia rozwiązania <math>x</math>.
oraz <math>\displaystyle w_{n-1}(x_n)=f(x_n)</math>, z których
 
<center><math>\displaystyle
  a_i\,=\,f(x_i), \qquad 0\le i\le n-1.
</math></center>
 
Teraz możemy ([[##aai|Uzupelnic: aai ]]) przepisać jako
 
<center><math>\displaystyle f(x_{i+1})\,=\,f(x_i)+b_ih_i+c_ih_i^2+d_ih_i^3//6,
</math></center>
 
przy czym wzór ten zachodzi również dla <math>\displaystyle i=n-1</math>.
Po wyrugowaniu <math>\displaystyle b_i</math> i podstawieniu <math>\displaystyle d_i</math> z ([[##dei|Uzupelnic: dei ]]),
mamy
 
<center><math>\displaystyle
  b_i\,=\,f(x_i,x_{i+1})+h_i(c_{i+1}+2c_i)//6,
  \qquad 0\le i\le n-1,
</math></center>
 
gdzie <math>\displaystyle f(x_i,x_{i+1})</math> jest odpowiednią różnicą
dzieloną. Możemy teraz powyższe wyrażenie na <math>\displaystyle b_i</math>  
podstawić w równaniach ([[##bei|Uzupelnic: bei ]]), aby otrzymać
 
<center><math>\displaystyle c_ih_i//6+c_{i+1}(h_i+h_{i+1})//3+c_{i+1}h_{i+1}//6
  \,=\,f(x_{i+1},x_{i+2})-f(x_i,x_{i+1}).  
</math></center>
 
Wprowadzając oznaczenie
 
<center><math>\displaystyle
  c_i^*\,=\,c_i//6,
</math></center>
 
możemy to równanie przepisać jako


<center><math>\displaystyle \frac{h_i}{h_i+h_{i+1}}c_i^*\,+\,2\,c_{i+1}^*\,+\,
Rzeczywiście, przecież rozwiązując równanie metody Jacobiego:
  \frac{h_{i+1}}{h_i+h_{i+1}}c_{i+2}^*\,=\,
  f(x_i,x_{i+1},x_{i+2}),
</math></center>


<math>\displaystyle 0\le i\le n-2</math>, albo w postaci macierzowej
<center><math>x^{(k)}_i = \frac{1}{a_{ii}} \left( b_i - \sum_{j\neq i} a_{ij}x^{(k-1)}_j \right)</math>,</center>


<center><math>\displaystyle
nietrudno zauważyć, że w części sumy moglibyśmy odwoływać się do "dokładniejszych" wartości <math>x^{(k)}_j</math>: dla <math>j < i</math>, tzn.
  \left(\begin{array} {cccccc}
    2  & w_1 \\
  u_2 &  2  & w_2 \\
      & u_3 &  2    & w_3 \\
      &    &\ddots &\ddots  &\ddots \\
      &    &      & u_{n-2} &  2  & w_{n-2} \\
      &    &      &        & u_{n-1} &  2
    \end{array} \right)
  \left(\begin{array} {c}
    c_1^*\\ c_2^*\\ c_3^*\\ \vdots\\ c_{n-2}^*\\ c_{n-1}^*
      \end{array} \right) \,=\,
  \left(\begin{array} {c}
    v_1\\ v_2\\ v_3\\ \vdots\\ v_{n-2}\\ v_{n-1}
      \end{array} \right),
</math></center>


gdzie
<center><math>x^{(k)}_i = \frac{1}{a_{ii}} \left( b_i - \sum_{j< i} a_{ij}x^{(k)}_j - \sum_{j>i} a_{ij}x^{(k-1)}_j \right)</math></center>
 
<center><math>\displaystyle \aligned && u_i\,=\,\frac{h_{i-1}}{h_{i-1}+h_i},\qquad
    w_i\,=\,\frac{h_i}{h_{i-1}+h_i}, \\
  && v_i\,=\,f(x_{i-1},x_i,x_{i+1}).
\endaligned</math></center>
 
Ostatecznie, aby znaleźć współczynniki <math>\displaystyle a_i,b_i,c_i,d_i</math>
należy najpierw rozwiązać układ równań ([[##ukltrd|Uzupelnic: ukltrd ]]),
a potem zastosować wzory ([[##wzc|Uzupelnic: wzc ]]), ([[##dei|Uzupelnic: dei ]]),
([[##wzb|Uzupelnic: wzb ]]) i ([[##wza|Uzupelnic: wza ]]).
 
Zauważmy, że macierz układu ([[##ukltrd|Uzupelnic: ukltrd ]]) jest
trójdiagonalna i ma dominującą przekątną. Układ
można więc rozwiązać kosztem proporcjonalnym do
wymiaru <math>\displaystyle n</math> używając algorytmu przeganiania z
U. [[##trojd|Uzupelnic: trojd ]]. Koszt znalezienia wszystkich
współczynników kubicznej funkcji sklejanej
interpolującej <math>\displaystyle f</math> jest więc też proporcjonalny do <math>\displaystyle n</math>.
 
Na końcu oszacujemy jeszcze błąd interpolacji naturalnymi
kubicznymi funkcjami sklejanymi na przedziale <math>\displaystyle [a,b]</math>.
Będziemy zakładać, że <math>\displaystyle f</math> jest dwa razy
różniczkowalna w sposób ciągły.
 
{{twierdzenie|||
Jeśli <math>\displaystyle f\in F^1_M([a,b])</math> to
 
<center><math>\displaystyle \|f-s_f\|_{C([a,b])}\,\le\,5\,M\,
      \max_{1\le i\le n}(x_i-x_{i-1})^2.
</math></center>


W szczególności, dla podziału równomiernego
W języku rozkładu macierzy <math>A = M - Z</math> i iteracji <math>x_{k+1} = M^{-1}(Zx_k + b)</math> mamy więc <math>M =  \mbox{tril} (A)</math> (dolny trójkąt macierzy <math>A</math>).
<math>\displaystyle x_i=a+(b-a)i/ń</math>, <math>\displaystyle 0\le i\le n</math>, mamy


<center><math>\displaystyle \|f-s_f\|_{ C([a,b])}\,\le\,
{{twierdzenie|O zbieżności metody Gaussa-Seidela|O zbieżności metody Gaussa-Seidela|
      5\,M\,\Big(\frac{b-a}n\Big)^2.
</math></center>


Jeśli macierz <math>A</math> jest diagonalnie dominująca, to metoda Gaussa--Seidela jest zbieżna dla dowolnego wektora startowego <math>x_0</math>.
}}
}}


{{dowod|||
Inny wariant tej metody dostalibyśmy, biorąc za <math>M</math> górny trójkąt macierzy <math>A</math>.
Wykorzystamy obliczoną wcześniej 
postać interpolującej funkcji sklejanej <math>\displaystyle s_f</math>.
Dla <math>\displaystyle x\in [x_i,x_{i+1}]</math> mamy


<center><math>\displaystyle \aligned w_i(x) &= f(x_i)\,+\,\left(\frac{f(x_{i+1})-f(x_i)}{h_i}
Metoda Gaussa--Seidela jest w wielu przypadkach rzeczywiście szybciej zbieżna od metody Jacobiego, np. tak jest w przypadku macierzy jednowymiarowego Laplasjanu. Wciąż jednak, dodajmy, dla zadań bardzo źle uwarunkowanych jej zbieżność jest zbyt wolna by ją stosować jako samodzielną metodę.
  -h_i(c_{i+1}^*+2c_i^*)\right)(x-x_i) \\ &&\qquad\qquad \,+\,
    3c_i^*(x-x_i)^2\,+\,\frac{c_{i+1}^*-c_i^*}{h_i}(x-x_i)^3.
\endaligned</math></center>


Z rozwinięcia <math>\displaystyle f</math> w szereg Taylora w punkcie <math>\displaystyle x_i</math> dostajemy
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<math>\displaystyle f(x)=f(x_i)+f'(x_i)(x-x_i)+f''(\xi_1)(x-x_i)^2//2</math> oraz
<span  style="font-variant:small-caps;">Uwaga</span>  
<math>\displaystyle (f(x_{i+1})-f(x_i))//h_i=f'(x_i)+h_if''(\xi_2)//2</math>. Stąd
<div class="solution" style="margin-left,margin-right:3em;">


<center><math>\displaystyle \aligned \lefteqn{f(x)-s_f(x) \,=\, f(x)-w_i(x)} \\
Obie metody, Jacobiego i (zwłaszcza) Gaussa--Seidela stosuje się także czasem w prostych algorytmach rozwiązywania <strong>układów równań nieliniowych</strong>: ich zaletą jest to, że głównym składnikiem iteracji jest rozwiązywanie [[MN02|<strong>skalarnego</strong> równania nieliniowego]] na każdym kroku metody.
  &= \frac{f''(\xi_1)}2(x-x_i)^2-\left(\frac{f''(\xi_2)}2
</div></div>
      -(c_{i+1}^*+2c_i^*)\right)h_i(x-x_i) \\
  & & \qquad\qquad\qquad -3c_i^*(x-x_i)^2
      -\frac{c_{i+1}^*-c_i^*}{h_i}(x-x_i)^3,
\endaligned</math></center>


oraz
===Złożoność stacjonarnych metod iteracyjnych===


<center><math>\displaystyle \aligned
Zastanówmy się teraz nad złożonością metod iteracyjnych. Ponieważ możemy jedynie znaleźć pewne przybliżenie rozwiązania dokładnego <math>x^*</math>, przez  złożoność metody będziemy rozumieli koszt kombinatoryczny obliczenia <math>x_k</math> z zadaną dokładnością <math>\epsilon>0</math>. Dla uproszczenia założymy, że medoda jest zbieżna liniowo z ilorazem <math>\rho</math>. Zauważmy, że aby zredukować błąd początkowy do  <math>\epsilon>0</math>, wystarczy wykonać <math>k=k(\epsilon)</math> iteracji, gdzie <math>k</math> spełnia
  |f(x)-s_f(x)| &\le &(M+2|c_{i+1}^*|+6|c_i^*|)h_i^2 \\
    &= (M+8\max_{1\le i\le n-1}|c_i^*|)h_i^2.
\endaligned</math></center>


Niech teraz <math>\displaystyle \max_{1\le i\le n-1}|c_i^*|=|c_s^*|</math>.
<center><math>\rho^k\| x_0- x^*\|\,\le\,\epsilon</math>,</center>
Z postaci układu ([[##ukltrd|Uzupelnic: ukltrd ]]) otrzymujemy


<center><math>\displaystyle \aligned |c_s^*| &= 2|c_s^*|-|c_s^*|(u_s+w_s) \,\le\,
czyli
    |u_sc_{s-1}^*+2c_s^*+w_sc_{s+1}| \\
  &= |f(x_{s-1},x_s,x_{s+1})|\,\le\,
      \Big|\frac{f''(\xi_3)}2\Big|\,\le\,\frac 12 M,
\endaligned</math></center>


a stąd i z ([[##psik|Uzupelnic: psik ]])  
<center><math>k\,\ge\,\frac{\log(1/\epsilon) - \log(1/\| x_0- x^*\|)}{\log(1/\rho)}</math></center>


<center><math>\displaystyle |f(x)-s_f(x)|\,\le\,5Mh_i^2,
Liczba ta zależy więc w istotny sposób od błędu  początkowego i (przede wszystkim) od współczynnika redukcji błędu <math>\rho</math>, natomiast zależność od dokładności <math>\epsilon</math> i wymiaru <math>N</math> układu jest dużo mniej istotna (w zadaniach praktycznych -- takich jak jednowymiarowy laplasjan --- jednak często
</math></center>
okazuje się, że... <math>\rho</math> zależy od <math>N</math>!).


co kończy dowód.
Zakładając, że koszt jednej iteracji wynosi <math>c=c(N)</math> (<math>c(N)</math> jest tym mniejszy, im mniejsza jest liczba niezerowych elementów macierzy <math>A</math>), złożoność  metody jest proporcjonalna do
}}


{{uwaga|||
<center><math>c(N)\,\frac{\log(1/\epsilon)}{\log(1/\rho)}.  
Zamiast terminu funkcje sklejane używa
się też często terminów ''splajny'' (ang.
''spline''-sklejać), albo ''funkcje gięte''.
}}
 
{{uwaga|||
 
Niech
 
<center><math>\displaystyle W^r_M(a,b)\,=\,\Big\{\,f\in W^r(a,b):\,
    \int_a^b\left(f^{(r)}(x)\right)^2\,dx\le M\,\Big\}.
</math></center>
</math></center>


Ustalmy węzły <math>\displaystyle a=x_0<\cdots<x_n=b</math>. Dla <math>\displaystyle f\in W^r_M(a,b)</math>,  
Stąd oczywisty wniosek (prawdziwy nie tylko dla metod stacjonarnych), że metody iteracyjne warto stosować zamiast metod bezpośrednich w przypadku, gdy
niech <math>\displaystyle s_f</math> będzie naturalną funkcją sklejaną
* wymiar <math>N</math> układu <math>A x= b</math> jest "duży", oraz
interpolującą <math>\displaystyle f</math> w <math>\displaystyle x_j</math>, <math>\displaystyle 0\le j\le n</math>, a <math>\displaystyle a_f</math>
* macierz <math>A</math> układu jest "rozrzedzona", tzn. ma stosunkowo niewielką liczbę elementów  niezerowych, np. proporcjonalną do <math>N</math>.
dowolną inną aproksymacją korzystającą jedynie
 
z informacji o wartościach <math>\displaystyle f</math> w tych węzłach , tzn.
Układy o tych własnościach powstają często przy numerycznym rozwiązywaniu równań różniczkowych cząstkowych.


<center><math>\displaystyle a_f\,=\,\phi(f(x_0),\ldots,f(x_n)).
==Metody przestrzeni Kryłowa==
</math></center>


Załóżmy, że błąd aproksymacji mierzymy nie w normie
Zupełnie inny pomysł na realizację metody iteracyjnej przedstawiają metody przestrzeni Kryłowa, gdzie kolejne przybliżenie <math>x_k</math> dobiera się w taki sposób, by minimalizowało pewną miarę błędu na podprzestrzeni Kryłowa
Czebyszewa, ale w normie średniokwadratowej, zdefiniowanej
jako


<center><math>\displaystyle \|g\|_{{\cal L}_2(a,b)}\,=\,\sqrt{\int_a^b (g(x))^2\,dx}.
<center><math>K_k =  \mbox{span} \{r,Ar,\ldots, A^{k-1}r \}</math>,</center>
</math></center>


Wtedy
gdzie <math>r = b-Ax_0</math> jest ''residuum'' na początku iteracji. (Zwróć uwagę, że przestrzeń Kryłowa jest rozpięta przez kolejne wektory [[MN13#Metoda potęgowa|metody potęgowej]]] --- to nie przypadek!). W zależności od wyboru sposobu miary błędu, dostajemy inną metodę iteracyjną, takie jak CG, GMRES, PCR, BiCG, i inne. Tutaj omówimy pokrótce tylko najpopularniejszą: CG.


<center><math>\displaystyle \sup_{f\in W^r_M(a,b)}\|f-s_f\|_{{\cal L}_2(a,b)}\,\le\,
===CG===
  \sup_{f\in W^r_M(a,b)}\|f-a_f\|_{{\cal L}_2(a,b)}.
</math></center>


Aproksymacja naturalnymi funkcjami sklejanymi jest więc
Metoda <strong>gradientów sprzężonych</strong>, w skrócie CG (''conjugate gradients''), działa przy założeniu, że <math>A</math> jest <strong>symetryczna i dodatnio określona</strong>.
optymalna w klasie <math>\displaystyle W^r_M(a,b)</math>.  


Można również pokazać, że interpolacja <math>\displaystyle s_f^*</math>
Kolejne przybliżenie <math>x_k</math> ma minimalizować błąd w normie energetycznej
naturalnymi funkcjami sklejanymi na węzłach równoodległych
indukowanej przez <math>A</math>,
<math>\displaystyle x_j=a+(b-a)j/ń</math>, <math>\displaystyle 0\le j\le n</math>, jest optymalna co do
rzędu w klasie <math>\displaystyle W^r_M(a,b)</math>, wśród wszystkich aproksymacji
korzystających jedynie z informacji o wartościach funkcji
w <math>\displaystyle n+1</math> dowolnych punktach, oraz


<center><math>\displaystyle \max_{f\in W^r_M(a,b)}\|f-s^*_f\|_{{\cal L}_2(a,b)}
<center><math>||x_k -x||_A =  \sqrt{(x_k -x)^TA(x_k -x)}
    \,\asymp\,n^{-r}.
</math></center>
</math></center>


}}
na przestrzeni afinicznej <math>x_0 + K_k</math>. Okazuje się (co ''nie jest'' oczywiste --- trzeba skorzystać z rozmaitych własności ortogonalności generowanych wektorów), że takie zadanie minimalizacji daje się bardzo efektywnie rozwiązać, skąd dostajemy bardzo zwarty algorytm:


{{uwaga|||
{{algorytm|Metoda CG|Metoda CG|
 
<Source>r = b-A*x;
Tak jak wielomiany, naturalne
<math>\rho_0</math> = <math>||r||_2^2</math>; <math>\beta</math> = 0; k = 1;
funkcje sklejane interpolujące dane funkcje można
while (!stop)
reprezentować przez ich współczynniki w różnych bazach.
{
Do tego celu można na przykład użyć bazy kanonicznej
p = r + <math>\beta</math>*p;
<math>\displaystyle K_j</math>, <math>\displaystyle 0\le j\le n</math>, zdefiniowanej równościami
w = A*p;
 
<math>\alpha</math> = <math>\rho_{k-1}</math>/<math>p^Tw</math>;
<center><math>\displaystyle K_j(x_i)\,=\,\left\{\begin{array} {ll}
x = x + <math>\alpha</math>*p;
        0 &\quad i\ne j, \\
r = r - <math>\alpha</math>*w;
        1 &\quad i=j, \end{array} \right.
<math>\rho_k</math> = <math>||r||_2^2</math>;
</math></center>
<math>\beta</math> = <math>\frac{\rho_{k}}{\rho_{k-1}}</math>;
 
k++;
przy której <math>\displaystyle s_f(x)=\sum_{j=0}^n f(x_j)K_j(x)</math>. Baza
}
kanoniczna jest jednak niewygodna w użyciu, bo funkcje <math>\displaystyle K_j</math>  
</Source>}}
w ogólności nie zerują się na żadnym podprzedziale,
a tym samym manipulowanie nimi jest utrudnione.


Częściej używa się bazy B-sklejanej <math>\displaystyle B_j</math>, <math>\displaystyle 0\le j\le n</math>.
Jak widać, całą iterację da się wykonać przechowując w pamięci tylko kilka wektorów (a nie, jak możnaby się obawiać, całą przestrzeń <math>K_k</math>), a najdroższym jej elementem jest mnożenie macierzy przez wektor.
W przypadku funkcji kubicznych, <math>\displaystyle r=2</math>, jest ona zdefiniowana
przez następujące warunki:


<center><math>\displaystyle \aligned B_j(x_j) &= 1, \qquad \mbox{ dla  }  0\le j\le n, \\
{{twierdzenie|O zbieżności CG jako metody bezpośredniej|O zbieżności CG jako metody bezpośredniej|
    B_j(x) &= 0,\qquad \mbox{ dla  }  x\le x_{j-2}, j\ge 2,
      \mbox{  oraz  dla  }  x\ge x_{j+2}, j\le n-2. 
\endaligned</math></center>


Dla <math>\displaystyle B_0</math> i <math>\displaystyle B_1</math> dodatkowo żądamy, aby
Niech <math>A\in R^{N\times N}</math> będzie symetryczna i dodatnio określona. Algorytm CG znajdzie dokładne rozwiązanie po co najwyżej <math>N</math> iteracjach.
 
<center><math>\displaystyle B_0''(x_0)\,=\,0\,=\,B_1''(x_0),\qquad B_1(x_0)\,=\,0,
</math></center>
 
a dla <math>\displaystyle B_{n-1}</math> i <math>\displaystyle B_n</math> podobnie
 
<center><math>\displaystyle B_{n-1}''(x_n)\,=\,0\,=\,B_n''(x_n),
      \qquad B_{n-1}(x_{n-1})\,=\,0.
</math></center>
 
Wtedy <math>\displaystyle B_j</math> nie zeruje się tylko na przedziale
<math>\displaystyle (x_{j-2},x_{j+2})</math>, Wyznaczenie współczynników
rozwinięcia w bazie <math>\displaystyle \{B_i\}_{i=0}^n</math> funkcji sklejanej
interpolującej <math>\displaystyle f</math> wymaga rozwiązania układu liniowego
z macierzą trójdiagonalną <math>\displaystyle \{B_j(x_i)\}_{i,j=0}^n</math>, a
więc koszt obliczenia tych współczynników jest
proporcjonalny do <math>\displaystyle n</math>.  
}}
}}


{{uwaga|||
Powyższe twierdzenie, choć teoretycznie interesujące, ma małą wartość praktyczną z dwóch powodów:
* dla bardzo dużych <math>N</math>, wykonanie <math>N</math> iteracji może być wciąż zbyt kosztownym zadaniem;
Oprócz naturalnych funkcji
* ponieważ w arytmetyce skończonej precyzji ortogonalność --- z której w bardzo istotny sposób korzysta się przy wyprowadzeniu algorytmu --- pogarsza się z iteracji na iterację i w konsekwencji, po wielu iteracjach, jakość <math>x_k</math> przestaje się poprawiać.
sklejanych często rozpatruje się też ''okresowe funkcje
sklejane''. Są to funkcje <math>\displaystyle \tilde s:R\toR</math> spełniające
warunki '''(i)''', '''(ii)''' z Rozdziału [[##warfs|Uzupelnic: warfs ]], oraz
warunek:
 
; (iii)'
<math>\displaystyle \tilde s^{(i)}</math> jest dla <math>\displaystyle 0\le i\le r-1</math>  
funkcją okresową o okresie <math>\displaystyle (b-a)</math>, tzn.
<math>\displaystyle \tilde s^{(i)}(x)=\tilde s^{(i)}(x+(b-a))</math>, <math>\displaystyle \forall x</math>.  
   
   
Klasę okresowych funkcji sklejanych rzędu <math>\displaystyle r</math> oznaczymy
Dlatego wygodniej potraktować CG jako metodę iteracyjną. Zachodzi bowiem
przez <math>\displaystyle \widetilde{\cal S}_r</math>. Funkcje te mają podobne
własności jak naturalne funkcje sklejane. Dokładniej,
niech


<center><math>\displaystyle
{{twierdzenie|Zbieżność CG jako metody iteracyjnej|Zbieżność CG jako metody iteracyjnej|
  \tilde W^r(a,b)\,=\,\{\,f\in W^r(a,b):\,
    f^{(i)}(a)=f^{(i)}(b),\; 0\le i\le r-1\,\},
</math></center>


tzn. <math>\displaystyle \tilde W^r(a,b)</math> jest klasą funkcji z <math>\displaystyle W^r(a,b)</math>,
Po <math>k</math> iteracjach metody CG,  
które można przedłużyć do funkcji, krórych wszystkie
pochodne do rzędu <math>\displaystyle r-1</math> włącznie są <math>\displaystyle (b-a)</math>-okresowe
na <math>\displaystyle R</math>. Wtedy dla dowolnej funkcji <math>\displaystyle f\in\tilde W^r(a,b)</math>
zerującej się w węzłach <math>\displaystyle x_j</math>, oraz dla dowolnej
<math>\displaystyle \tilde s\in\widetilde{\cal S}_r</math> mamy


<center><math>\displaystyle
<center><math>||x_k - x||_A \leq 2 \, \left( \frac{\sqrt{ \mbox{cond} (A)} - 1}{\sqrt{ \mbox{cond} (A)} +1}\right)^k \, ||x_0 - x||_A</math>,</center>
  \int_a^b f^{(r)}(x)\tilde s^{(r)}(x)\,dx\,=\,0.
</math></center>
 
Jest to odpowiednik Lematu [[##bwazny|Uzupelnic: bwazny ]] w przypadku okresowym.
W szczególności wynika z niego jednoznaczność
rozwiązania zadania interpolacyjnego dla okresowych
funkcji <math>\displaystyle f</math> (tzn. takich, że <math>\displaystyle f(a)=f(b)</math>), jak również
odpowiednia własność minimalizacyjna okresowych funkcji
sklejanych. Dokładniej, jeśli <math>\displaystyle f\in\tilde W^r(a,b)</math> oraz
<math>\displaystyle \tilde s_f\in\widetilde{\cal S}_r</math> interpoluje <math>\displaystyle f</math> w węzłach
<math>\displaystyle x_j</math>, <math>\displaystyle 0\le j\le n</math>, to
 
<center><math>\displaystyle \int_a^b \Big(  f^{(r)}(x)\Big)^2\,dx\,\ge\,
  \int_a^b \Big(\tilde s_f^{(r)}(x)\Big)^2\,dx.
</math></center>


gdzie <math>\mbox{cond} (A) = ||A||_2||A^{-1}||_2</math>.
}}
}}


{{uwaga|||
===GMRES===


Klasyczne zadanie
Metoda GMRES (''Generalized Minimum RESidual'') nie wymaga ani symetrii, ani dodatniej określoności macierzy, jest więc bardziej uniwersalna, choć też bardziej kosztowna od CG. Jej szczegółowe omówienie, w tym --- oszacowania szybkości zbieżności --- wykracza niestety poza ramy niniejszego wykładu.
aproksymacyjne w przestrzeniach funkcji definiuje się
w następujący sposób.  


Niech <math>\displaystyle F</math> będzie pewną przestrzenią liniową funkcji
===Ściskanie macierzy===
<math>\displaystyle f:[a,b]\toR</math>, w której określona została norma
<math>\displaystyle \|\cdot\|</math>. Niech <math>\displaystyle V_n\subset F</math> będzie podprzestrzenią
w <math>\displaystyle F</math> wymiaru <math>\displaystyle n</math>. Dla danej <math>\displaystyle f\in F</math>, należy znaleźć
funkcję <math>\displaystyle v_f\in F</math> taką, że


<center><math>\displaystyle \|f-v_f\|\,=\,\min_{v\in V_n}\|f-v\|.  
Zbieżność wszystkich poznanych metod iteracyjnych zależy od własności
</math></center>
spektralnych macierzy układu. Pojawiające się w zastosowaniach macierze
 
często mają niekorzystne własności spektralne (np. bardzo duży wskaźnik uwarunkowania), przez co metody iteracyjne zbiegają na nich bardzo wolno.
Okazuje się, że tak postawione zadanie ma rozwiązanie
<math>\displaystyle v_f</math>, choć nie zawsze jest ono wyznaczone jednoznacznie,  
zob. ćw. [[##rkl|Uzupelnic: rkl ]].  


Jako przykład, rozpatrzmy <math>\displaystyle F=W^r(a,b)</math>. Utożsamiając
Dlatego bardzo korzystne może być wstępne przetransformowanie układu
funkcje <math>\displaystyle f_1,f_2\in W^r(a,b)</math> takie, że
<math>\displaystyle f_1(x)-f_2(x) \in\Pi_{r-1}</math>, zdefiniujemy w <math>\displaystyle W^r(a,b)</math> normę


<center><math>\displaystyle \|f\|\,=\,\sqrt{\int_a^b\left(f^{(r)}(x)\right)^2\,dx}. 
<center><math>Ax = b
</math></center>
</math></center>


Dla ustalonych węzłów <math>\displaystyle a=x_0<\cdots<x_n=b</math>, niech
z macierzą o niekorzystnych własnościach, do układu


<center><math>\displaystyle V_{n+1}\,=\,{\cal S}_r
<center><math>MAx = Mb</math>,</center>
</math></center>


będzie podprzestrzenią w <math>\displaystyle W^r(a,b)</math> naturalnych funkcji
gdzie macierz <math>MA</math> ma znacznie korzystniejsze własności z punktu widzenia używanej metody iteracyjnej. W przypadku macierzy symetrycznych widzieliśmy, że kluczowe znaczenie dla zbieżności metody miał rozkład wartości własnych: jeśli wartości własne były bardzo rozrzucone po prostej, to uwarunkowanie było bardzo duże i w konsekwencji zbieżność powolna. Aby zbieżność była szybsza, kluczowe jest, by:
sklejanych rzędu <math>\displaystyle r</math> opartych węzłach <math>\displaystyle x_j</math>, <math>\displaystyle 0\le j\le n</math>.  
* wartości własne <math>MA</math> były upakowane w klastrach
Oczywiście <math>\displaystyle  \mbox{dim} {\cal S}_r=n+1</math>, co wynika z
* najlepiej wszystkie w (małym) otoczeniu wartości 1
jednoznaczności rozwiązania w <math>\displaystyle {\cal S}_r</math> zadania
interpolacji. Okazuje się, że wtedy optymalną dla
Jeśli więc chcielibyśmy przekształcić macierz tak, by metoda iteracyjna dla <math>MA</math> zbiegała szybko, musimy w jakiś sposób "ścisnąć" spektrum macierzy <math>A</math> w okolice jedności. Taką operację nazywamy <strong>ściskaniem</strong> (''preconditioning''), a macierz <math>M</math> --- <strong>macierzą ściskającą</strong>.
<math>\displaystyle f\in W^r(a,b)</math> jest naturalna funkcja sklejana <math>\displaystyle s_f</math>  
interpolująca <math>\displaystyle f</math> w węzłach <math>\displaystyle x_j</math>, tzn.  


<center><math>\displaystyle \|f-s_f\|\,=\,\min_{s\in {\cal S}_r}\|f-s\|.  
Aby całość miała sens, macierz ściskająca <math>M</math> powinna:
</math></center>
* być łatwa w konstrukcji,
* być tania w mnożeniu przez wektor (głównym elementem każdej metody iteracyjnej jest mnożenie macierzy przez wektor: <math>M\cdot (A \cdot x)</math>),
* macierz <math>MA</math> powinna mieć znacznie korzystniejsze własności z punktu widzenia używanej metody iteracyjnej.
Kilka ekstremalnych (lecz wątpliwej jakości) "propozycji" na macierz ściskającą to <math>M = I</math> (łatwa w
konstrukcji i tania w mnożeniu, ale niestety nic nie polepsza...) oraz
<math>M=A^{-1}</math> (rewelacyjnie poprawia zbieżność metody iteracyjnej, dając zbieżność
w jednej iteracji, ale bardzo droga w konstrukcji i mnożeniu). Widać więc, że
należy poszukiwać czegoś pośredniego, co niskim kosztem przybliża działanie
macierzy odwrotnej.


Rzeczywiście, ponieważ norma w przestrzeni <math>\displaystyle W^r(a,b)</math>
Dlatego jednym z powszechniej stosowanych (aczkolwiek wciąż nie najbardziej skutecznych) sposobów ściskania są te oparte na zastosowaniu jednego kroku klasycznej metody iteracyjnej.
generowana jest przez iloczyn skalarny


<center><math>\displaystyle (f_1,f_2)\,=\,
[[Image:MNpcgconv.png|thumb|550px|center|Zbieżność metody CG bez żadnego  ściskania oraz ściśniętej
    \int_a^b f_1^{(r)}(x)f_2^{(r)}(x)\,dx,
imadłem opartym na jednej iteracji (blokowej) metody Jacobiego.]]
</math></center>


jest to przestrzeń unitarna. Znane twierdzenie mówi, że
Inne sposoby ściśnięcia macierzy wykorzystują np. techniki tzw. <strong>niepełnego
w przestrzeni unitarnej najbliższą danej <math>\displaystyle f</math> funkcją
rozkładu</strong> macierzy, albo --- w specyficznych przypadkach --- tzw.<strong> metody
w dowolnej domkniętej podprzestrzeni <math>\displaystyle V</math> jest rzut
wielosiatkowe</strong>.
prostopadły <math>\displaystyle f</math> na <math>\displaystyle V</math>, albo równoważnie, taka funkcja
<math>\displaystyle v_f\in V_{n+1}</math>, że iloczyn skalarny


<center><math>\displaystyle (f-v_f, v)\,=\,0, \qquad\forall v\in V.  
Okazuje się, że zarówno CG jak i GMRES da się zaimplementować tak, by w jednej
</math></center>
iteracji było konieczne tylko jedno mnożenie przez macierz ściskającą.


W naszym przypadku, ostatnia równość jest równoważna
==Literatura==


<center><math>\displaystyle \int_a^b(f-v_f)^{(r)}(x)s^{(r)}(x)\,dx\,=\,0,
W celu dogłębnego zapoznania się z omawianym na wykładzie materiałem, przeczytaj <b>rozdział 4.6 i 4.7</b> w
    \quad\forall s\in{\cal S}_r.  
* D. Kincaid, W. Cheney <cite>Analiza numeryczna</cite>, Wydawnictwa Naukowo-Techniczne, Warszawa 2006, ISBN 83-204-3078-X.
</math></center>
 
Tym razem wymienione rozdziały nie wystarczą do dogłębnego zrozumienia omawianych treści. Warto więc sięgnąć po jeden z podręczników
To zaś jest na mocy Lematu [[##bwazny|Uzupelnic: bwazny ]] prawdą gdy <math>\displaystyle v_f</math>  
* <span style="font-variant:small-caps">C. T. Kelley</span>, <cite>Iterative Solution of Systems of Linear and Nonlinear Equations</cite>, SIAM, 1995,
interpoluje <math>\displaystyle f</math> w punktach <math>\displaystyle x_j</math>, czyli <math>\displaystyle v_f=s_f</math>.
* <span style="font-variant:small-caps">Y. Saad</span>, <cite>[http://www-users.cs.umn.edu/~saad/books.html  Iterative Methods for Sparse Linear Systems]</cite>, PWS, 1996.
 
Dodajmy jeszcze, że nie zawsze interpolacja daje najlepszą
aproksymację w sensie klasycznym, zob. ćw. [[##intkla|Uzupelnic: intkla ]].  
}}

Aktualna wersja na dzień 21:44, 11 wrz 2023


Wielkie układy równań liniowych

<<< Powrót do strony głównej przedmiotu Metody numeryczne

Wraz z coraz większymi modelami pojawiającymi się w praktyce obliczeniowej, coraz częściej zachodzi potrzeba rozwiązywania zadań algebry liniowej, w której macierze są co prawda wielkiego wymiaru, ale najczęściej rozrzedzone, to znaczy jest w nich bardzo dużo zer. Bardzo często zdarza się, że macierz wymiaru N ma tylko O(N) niezerowych elementów. Wykorzytanie tej specyficznej własności macierzy nie tylko prowadzi do algorytmów istotnie szybszych od ich analogów dla macierzy gęstych (to znaczy takich, które (w założeniu) mają N2 elementów), ale wręcz są jedynym sposobem na to, by niektóre zadania w ogóle stały się rozwiązywalne przy obecnym stanie techniki obliczeniowej!

Jednym ze szczególnie ważnych źródeł układów równań z macierzami rozrzedzonymi są np. równania różniczkowe cząstkowe (a więc np. modele pogody, naprężeń w konstrukcji samochodu, przenikania kosmetyków do głębszych warstw skóry, itp.).

Modele wielostanowych systemów kolejkowych (np. routera obsługującego wiele komputerów) także prowadzą do gigantycznych układów równań z macierzami rozrzedzonymi o specyficznej strukturze.

Z reguły zadania liniowe wielkiego wymiaru będą miały strukturę macierzy rozrzedzonej, gdyż najczęściej związki pomiędzy niewiadomymi w równaniu nie dotyczą wszystkich, tylko wybranej grupy.

Przykład: Macierz z kolekcji Boeinga

Spójrzmy na macierz sztywności dla modelu silnika lotniczego, wygenerowaną swego czasu w zakładach Boeinga i pochodzącą z dyskretyzacji pewnego równania różniczkowego cząstkowego. Pochodzi z kolekcji Tima Davisa. Jest to mała macierz, wymiaru 8032 (w kolekcji spotkasz równania z milionem i więcej niewiadomych).

Struktura niezerowych elementów macierzy bcsstk38.

Jej współczynnik wypełnienia (to znaczy, stosunek liczby niezerowych do wszystkich elementów macierzy) wynosi jedynie 0.006, a więc macierz jest bardzo rozrzedzona: w każdym wierszu są średnio tylko 44 niezerowe elementy.


Reprezentacja macierzy rzadkich

Zacznijmy od sposobu przechowywania macierzy rozrzedzonych. Naturalnie, nie ma sensu przechowywać wszystkich zerowych jej elementów: wystarczy ograniczyć się do zachowania tych niezerowych! W ten sposób zmniejszamy zarówno wymagania pamięciowe, jak i liczbę operacji zmiennoprzecinkowych potrzebnych do prowadzenia działań na macierzy (np. w przypadku mnożenia macierzy przez wektor, nie będziemy mnożyć przez zera!).

Format współrzędnych

Do zapamiętania macierzy A wymiaru N×M o NNZ niezerowych elementów wykorzystujemy trzy wektory: I, J --- oba typu int --- oraz V, typu double, wszystkie o długości NNZ, przy czym

AI[k],J[k]=V[k],k=0,,NNZ1
Format AIJ

W tym formacie wprowadzane są macierze rzadkie do Octave'a i MATLABa:

A = sparse(I,J,V,N,N);

Jednak wewnętrznie przechowywane są w innym formacie, o którym poniżej.

Przykład

Pokażemy jak w Octave wprowadzić macierz rozrzedzoną.

<realnowiki><realnowiki><realnowiki><realnowiki>octave:10> I = [1, 1, 1, 2, 3, 1, 4]; octave:11> J = [1, 3, 2, 2, 3, 4, 4]; octave:12> V = [1, 2, 3, 4, 5, 6, 7]; octave:13> N = 4; octave:14> A = sparse(I,J,V,N,N) A = Compressed Column Sparse (rows = 4, cols = 4, nnz = 7) (1, 1) -> 1 (1, 2) -> 3 (2, 2) -> 4 (1, 3) -> 2 (3, 3) -> 5 (1, 4) -> 6 (4, 4) -> 7 octave:15> spy(A); </realnowiki></realnowiki></realnowiki></realnowiki>

Strukturę jej niezerowych elementów ukaże nam polecenie spy(A). Tak właśnie zostały wygenerowane obrazki macierzy w niniejszym wykładzie.

Format spakowanych kolumn (wierszy)

Format współrzędnych nie narzucał żadnego uporządkowania elementów macierzy --- można było je umieszczać w dowolnej kolejności. Narzucenie sensownego porządku mogłoby wspomóc realizację wybranych istotnych operacji na macierzy, na przykład, aby wygodnie było realizować działanie (prawostronnego) mnożenia macierzy przez wektor, dobrze byłoby przechowywać elementy macierzy wierszami. Tak właśnie jest zorganizowany format spakowanych wierszy (Compressed Sparse Row, CSR). Analogicznie jest zdefiniowany format spakowanych kolumn (Compressed Sparse Column, CSC), którym zajmiemy się bliżej.

Podobnie jak w przypadku formatu współrzędnych, macierz w formacie CSC jest przechowywana w postaci trzech wektorów: AV jest wektorem typu double o długości NNZ, zawierającym kolejne niezerowe elementy macierzy A wpisywane kolumnami, AI jest wektorem typu int o długości NNZ, zawierającym numery wierszy macierzy A odpowiadających elementom z AV. Natomiast zamiast tablicy J, jak to było w formacie współrzędnych, mamy krótszy wektor typu int, AP, o długości M, zawierający na j-tym miejscu indeks pozycji w AV, od którego rozpoczynają się w AV elementy j-tej kolumny macierzy A.

Format CSC

Mamy więc zależność, przy założeniu, że AP[0]=0,

AAI[AP[j]+k],j+1=AV[AP[j]+k],j=0,,M1,k=0,,AP[j+1]1

Taki (z drobnymi modyfikacjami) format macierzy wykorzystują np. pakiety ARPACK i UMFPACK (a także, wewnętrznie, Octave i MATLAB).

Format diagonalny

Znacznie mniej uniwersalny niż poprzednie i dlatego rzadziej spotykany. Kolejne diagonale macierzy przechowujemy w kolejnych wierszach macierzy P×N, gdzie P jest liczbą niezerowych diagonal w ARN×N.

Szczególnie wygodny do reprezentacji macierzy taśmowych. Wykorzystywany m.in. przez funkcję LAPACKa DGBSV służącą rozwiązywaniu równań z macierzami taśmowymi.

Uwagi praktyczne

Mnożenie macierzy w formacie AIJ przez wektor jest kilka razy wolniejsze w porównaniu do macierzy w formacie CSC/CSR (z tych dwóch oczywiście (dlaczego?) CSR jest najszybszy). Co więcej, okazuje się, że w typowych implementacjach, mnożenie macierzy rozrzedzonej (reprezentowanej np. w formacie CSC) przez wektor jest mało efektywne, mniej więcej na poziomie dziesięciu procent możliwości obliczeniowych procesora.

Jeśli już poważnie myśleć o przyspieszeniu mnożenia macierzy przez wektor (np. gdy chcemy stosować iteracyjną metodę rozwiązywania układu równań z tą macierzą), warto rozważyć inne formaty --- łączące w sobie podział macierzy na bloki (tak, jak w algorytmach BLAS) i przechowywanie (w zasadzie) tylko niezerowych elementów macierzy.

Macierze specjalne

Zajmiemy się teraz zadaniem rozwiązywania układu równań liniowych

Ax=b,

ale w sytuacji, gdy macierz A jest rozrzedzona i dużego wymiaru. Dokonamy przeglądu kilku rodzajów algorytmów mających na celu wykorzystanie rozrzedzenia macierzy dla obniżenia kosztu wyznaczenia rozwiązania układu.

Należy pamiętać, że z reguły najlepsze wyniki uzyskuje się, gdy konkretny algorytm dobierze się do konkretnej macierzy. W zastosowaniach pojawiają się m.in. macierze rzadkie o bardzo szczególnej strukturze, dla nich warto stosować wyspecjalizowane algorytmy.

Macierze taśmowe

Macierz A taka, że dla |ij|d, aij=0, nazywamy macierzą taśmową z rozstępem d, o szerokości pasma 2d+1.

Łatwo sprawdzić, że algorytm eliminacji Gaussa (bez wyboru elementu głównego) nie spowoduje dodatkowego wypełnienia w takiej macierzy (a więc da się wykonać w miejscu). W przypadku konieczności wyboru elementu głównego, pesymistyczne oszacowanie rozstępu macierzy rozkładu LU jest równe max{2d,N} --- tak więc, dla niezbyt dużych d wciąż wynikowa macierz jest taśmowa.

W szczególności, gdy macierz jest taśmowa z pasmem o rozstępie d i jednocześnie diagonalnie dominująca, wtedy rozkład LU takiej macierzy da się wykonać w miejscu kosztem O(d2N), czyli liniowym względem N.

W LAPACKu zaimplementowano szybki solver równań z macierzami taśmowymi, DGBSV, ale wymagający specjalnego sposobu przechowywania macierzy, wykorzystującego format diagonalny.

Macierze trójdiagonalne

Szczególnym przypadkiem macierzy taśmowych są macierze trójdiagonalne, tzn. taśmowe o rozstępie d=1:

T=(a1c1b2a2c2b3a3cN1bNaN)

Zadanie rozwiązywania równań z taką macierzą,

Tx=e

jest bardzo często spotykane, więc warto przytoczyć algorytm w całej okazałości --- popularnie zwany algorytmem przeganiania (niektóre źródła nazywają go algorytmem Thomasa).

Zacznijmy jednak od stwierdzenia, kiedy macierz trójdiagonalna nie wymaga wyboru elementu głównego:

Stwierdzenie

Jeśli macierz T ma słabo dominującą diagonalę, tzn.

|ai||bi|+|ci|,1iN,

(b1=0=cN) i przynajmniej dla jednego indeksu "i" mamy powyżej ostrą nierówność ">", to algorytm przeganiania jest wykonalny bez przestawień wierszy. Ponadto wymaga on 7N operacji arytmetycznych, a więc jest prawie optymalny.

Algorytm Metoda przeganiania (w miejscu)


for (i = 2; i <= N; i++)
{
	<math>l</math> = <math>b_i</math>/<math>a_{i-1}</math>;
	<math>a_i</math> = <math>a_i</math> - <math>l</math> * <math>c_{i-1}</math>;
	<math>e_i</math> = <math>e_i</math> - <math>l</math> * <math>e_{i-1}</math>;
} 
<math>x_N</math> = <math>e_N</math>/<math>a_N</math>;
for (i = N-1; i >= 1; i--)
	<math>x_i</math> = (<math>e_i</math> - <math>c_i</math> * <math>x_{i+1}</math>)/<math>a_i</math>;

Metody bezpośrednie

Przykład: Strzałka Wilkinsona

Rozważmy układ równań z macierzą diagonalnie dominującą postaci

A=(**********)

gdzie * oznacza jakiś niezerowy element. Łatwo sprawdzić, że chociaż wyjściowa macierz jest rozrzedzona, to zastosowanie do niej eliminacji Gaussa powoduje, że w wyniku dostajemy gęste czynniki rozkładu.

Tymczasem wystarczy odwrócić kolejność równań i numerację niewiadomych (co dla macierzy jest równoznaczne z odwróceniem porządku wierszy i kolumn, korzystając z pewnej (jakiej?) macierzy permutacji P):

A~=PAPT=(**********)

Wtedy okazuje się, że rozkład naszej macierzy nie powoduje już wypełnienia czynników rozkładu!

Właśnie na tym polega główny problem w rozwiązywaniu układów z macierzami rzadkimi metodami bezpośrednimi: jak maksymalnie wykorzystać rozrzedzenie macierzy tak, by czynniki rozkładu były możliwie mało wypełnione. Albowiem wiedząc to będziemy mogli ograniczyć się jedynie do fizycznego wyznaczenia wartości niezerowych elementów macierzy rozkładu. Ponadto wymagania pamięciowe algorytmu nie będą istotnie wykraczać ponad ilość pamięci potrzebnej na przechowanie danych (tzn. macierzy).

W ogólnym przypadku rozwiązanie takiego zadania jest trudne i większość algorytmów opiera się na pewnych heurystykach, które jednak w praktyce warto wspomóc wcześniejszą analizą konkretnego układu równań jaki mamy rozwiązać. Najczęściej dąży się do takiego przenumerowania równań i niewiadomych, by w efekcie z góry przewidzieć, gdzie wystąpią zera w macierzach rozkładu --- i, by takich zer było jak najwięcej (by wypełnienie było jak najmniejsze)! Na architekturach z pamięcią hierarchiczną dąży się także do tego, by w trakcie rozkładu można było korzystać z BLAS Level 3, a więc permutacje wierszy i kolumn macierzy muszą to także brać pod uwagę.

Stosuje się kilka strategii wyznaczania korzystnych permutacji (reorderingu), z których warto wymienić

  • przybliżone algorytmy minimalnego stopnia (approximate minimum degree, AMD), np. AMD;
  • techniki podziału grafów na (prawie) rozłączne składowe nested dissection, ND, np. METIS.

Używają ich biblioteki takie jak UMFPACK, czy HSL.

W Octave mamy do dyspozycji także kilka procedur generujących takie permutacje, w tym: colamd (AMD dla macierzy niesymetrycznych) oraz symamd (AMD dla macierzy symetrycznych). Większy wybór oferuje MATLAB, jednak należy bezwzględnie pamiętać o jednym: nie ma uniwersalnej metody reorderingu i dla konkretnej macierzy może istnieć specjalna metoda, która da oszałamiające rezultaty, podczas gdy standardowe podejścia nie dadzą efektu.

Przykład: Rozwiązywanie układu z macierzą rozrzedzoną w Octave

Najprostszy sposób na wykorzystanie metody bezpośredniego rozwiązywania układu z macierzą rzadką to zastosowanie znanego nam operatora do macierzy typu rozrzedzonego:

A = sparse(...);
x = A \ b;

Octave domyślnie przyłoży do A reordering colamd i następnie skorzysta z biblioteki UMFPACK, by rozwiązać taki układ. Dodatkowo, badane jest wcześniej, czy macierz nie jest przypadkiem taśmowa o wąskiej wstędze: jeśli jest, to korzysta się z LAPACKa.

(Jak widzisz, operator rozwiązywania układu równań jest mocno przeciążony: działa i na macierzach kwadratowych, i na prostokątnych, i na rozrzedzonych --- na każdym rodzaju w inny sposób. Nie dziwi Cię chyba, dlaczego wygodnie było napisać Octave w C++?)

Przykład: Wypełnienie pewnej macierzy w zależności od użytego reorderingu

Rozważmy ponownie macierz pochodzącą z kolekcji Tima Davisa. Jest to macierz symetryczna, dodatnio określona, wymiaru 8032, o 355460 elementach niezerowych i, w konsekwencji, o wypełnieniu około 0.6 procent.

Struktura niezerowych elementów macierzy.

Zobaczymy, jak w zależności od użytego algorytmu permutacji kolumn i wierszy poradzi sobie algorytm rozkładu Cholesky'ego.

Czynnik rozkładu Cholesky'ego L wykonanego standardowym algorytmem. Czas rozkładu: 0.892013
Czynnik rozkładu Cholesky'ego L z reorderingiem COLAMD. Czas rozkładu: 0.813038
Czynnik rozkładu Cholesky'ego L z reorderingiem SYMAMD. Czas rozkładu: 0.487683s. Prawie dwa razy szybciej niż bez reorderingu, chociaż i tak wskutek wzrostu wypełnienia macierzy w dolnym trójkącie mamy aż 2 procent niezerowych elementów.
Czynnik rozkładu Cholesky'ego L z odwrotnym reorderingiem Cuthill-McKee. Czas rozkładu: 0.845928
Czynnik rozkładu Cholesky'ego L z jeszcze innym (bardzo tanim, ale jak widać czasem zupełnie złym) reorderingiem. Czas rozkładu: 5.947936

Na zakończenie popatrzmy, jak ważne jest spostrzeżenie symetrii macierzy:

Rozkład LU, czynnik L (bez reorderingu). Czas rozkładu LU: 1.696018s. Nieakceptowalny, podobnie jak drastyczne wypełnienie macierzy.
Rozkład LU, czynnik U (bez reorderingu)

Jak widać, w naszym przypadku standardowe algorytmy (COLAMD i SYMAMD) poradziły sobie całkiem nieźle, chociaż wypełnienie i tak znacząco wzrosło. Zapewne, dla tej macierzy, która jest specyficznego typu --- pochodzi z dyskretyzacji równania różniczkowego --- algorytm ND mógłby tu jeszcze lepiej zadziałać.

Stacjonarne metody iteracyjne

Gdy macierz jest rozrzedzona, mnożenie takiej macierzy przez wektor jest bardzo tanie (koszt jest proporcjonalny do liczby niezerowych elementów macierzy). Dlatego, jeśli możemy zadowolić się rozwiązaniem przybliżonym układu, a w zamian osiągnąć je tanim kosztem, warto rozważyć metody iteracyjne.

Najprostsze metody iteracyjne (najprostsze w analizie i implementacji, ale --- jak można się domyślić --- w praktyce najmniej efektywne) polegają na rozkładzie macierzy na część "łatwo odwracalną", M, i "resztę", Z. Dokładniej, jeśli M jest nieosobliwa, to równanie Ax=b można zapisać jako zadanie punktu stałego

Mx=Zx+b,

gdzie Z=MA. Inaczej:

x=M1(Zx+b),

i zastosować doń metodę iteracji prostej Banacha:

xk+1=M1(Zxk+b)

Takie metody nazywamy stacjonarnymi metodami iteracyjnymi. Aby przeanalizować zbieżność takiej metody, warto rozpatrzyć przypadek ogólniejszy

xk=Bxk1+c,

dla pewnej macierzy B oraz wektora c. (Dla stacjonarnej metody iteracyjnej, B=M1Z oraz c=M1b.)

W tym przypadku

xkx*=Bk(x0x*),

a stąd i z nierówności BkBk, mamy

xkx*Bkx0x*.

Warunkiem dostatecznym zbieżności iteracji prostych jest więc B<1. Okazuje się, że warunkiem koniecznym i dostatecznym zbieżności tej iteracji dla dowolnego wektora startowego x0 jest

ρ=max{|λ|:λ jest wartością własną B}<1

Tak więc, metody oparte na iteracji prostej będą zbieżne liniowo z ilorazem ρ.

Zaletą stacjonarnych metod iteracyjnych jest również ich prostota, przez co są one łatwe do zaprogramowania, co łatwo zobaczyć na przykładach metod: Jacobiego i Gaussa--Seidela, które teraz omówimy.

Metoda Jacobiego

Biorąc M=diag(A), gdzie diag(A) jest macierzą diagonalną składającą się z wyrazów stojących na głównej przekątnej macierzy A, układ Ax=b jest równoważny układowi

Mx=Zx+b,

a stąd (o ile na przekątnej macierzy A nie mamy zera) otrzymujemy metodę iteracyjną

xk=Bxk1+c,

gdzie B=M1Z i c=M1b, zwaną metodą Jacobiego.

Rozpisując ją po współrzędnych dostajemy (numer iteracji wyjątkowo zaznaczamy w postaci górnego indeksu) układ rozszczepionych równań:

xi(k)=1aii(bijiaijxj(k1)),

co znaczy dokładnie tyle, że w i-tym równaniu wyjściowego układu przyjmujemy za współrzędne x wartości z poprzedniej iteracji i na tej podstawie wyznaczamy wartość xi.

Twierdzenie O zbieżności metody Jacobiego

W metodzie Jacobiego warunek dostateczny zbieżności, B<1, jest spełniony np. wtedy, gdy macierz A ma dominującą przekątną, tzn. gdy

|ai,i|>ji|ai,j|,i=1,,N

Dowód

Rzeczywiście, ponieważ wyraz (i,j) macierzy M1Z wynosi 0 dla i=j oraz ai,j/ai,i dla ij, a więc

M1Z=max1iNj=1,jiN|ai,j|/|ai,i|=max1iNj=1N|ai,j|/|ai,i|1<1,

przy czym ostatnia nierówność wynika z warunku diagonalnej dominacji.

Przykład: Macierz laplasjanu

Macierz N×N, zwana macierzą jednowymiarowego laplasjanu

L=(2112112)

pojawia się w bardzo wielu zastosowaniach, także jako podzadanie w algorytmach numerycznych. Ta macierz jest macierzą taśmową, symetryczną i dodatnio określoną, więc układ równań z tą macierzą można bez trudu rozwiązać metodami bezpośrednimi, kosztem O(N). Stosując do niej metodę Jacobiego mamy M=2I oraz Z=LM. Obliczając normę macierzy iteracji Jacobiego dostajemy ||M1Z||=1, co nie rozstrzyga jeszcze o jej zbieżności lub niezbieżności.

Potrzebna będzie bardziej subtelna analiza. Okazuje się, że są znane wzory na wartości własne macierzy L:

λj=4sin2(jπ2(N+1)),

dla 1jN. W konsekwencji, wartościami własnymi M1Z=12Z=12LI są liczby μi=12λi1. Ponieważ 0<μi<1, znaczy to, że metoda Jacobiego jest zbieżna dla macierzy L.

Z drugiej strony, nie dajmy się zwieść optymizmowi matematyka ("nie martw się, jest zbieżny..."): nietrudno sprawdzić, że ||Z||2=1O(N2)<1, co oznacza, że metoda Jacobiego --- choć zbieżna --- dla dużych N staje się zbieżna tak wolno, że w praktyce bezużyteczna!

Metoda Gaussa-Seidela

Heurystyka tej metody opiera się na zmodyfikowaniu metody Jacobiego tak, by w każdym momencie iteracji korzystać z najbardziej "aktualnych" współrzędnych przybliżenia rozwiązania x.

Rzeczywiście, przecież rozwiązując równanie metody Jacobiego:

xi(k)=1aii(bijiaijxj(k1)),

nietrudno zauważyć, że w części sumy moglibyśmy odwoływać się do "dokładniejszych" wartości xj(k): dla j<i, tzn.

xi(k)=1aii(bij<iaijxj(k)j>iaijxj(k1))

W języku rozkładu macierzy A=MZ i iteracji xk+1=M1(Zxk+b) mamy więc M=tril(A) (dolny trójkąt macierzy A).

Twierdzenie O zbieżności metody Gaussa-Seidela

Jeśli macierz A jest diagonalnie dominująca, to metoda Gaussa--Seidela jest zbieżna dla dowolnego wektora startowego x0.

Inny wariant tej metody dostalibyśmy, biorąc za M górny trójkąt macierzy A.

Metoda Gaussa--Seidela jest w wielu przypadkach rzeczywiście szybciej zbieżna od metody Jacobiego, np. tak jest w przypadku macierzy jednowymiarowego Laplasjanu. Wciąż jednak, dodajmy, dla zadań bardzo źle uwarunkowanych jej zbieżność jest zbyt wolna by ją stosować jako samodzielną metodę.

Uwaga

Obie metody, Jacobiego i (zwłaszcza) Gaussa--Seidela stosuje się także czasem w prostych algorytmach rozwiązywania układów równań nieliniowych: ich zaletą jest to, że głównym składnikiem iteracji jest rozwiązywanie skalarnego równania nieliniowego na każdym kroku metody.

Złożoność stacjonarnych metod iteracyjnych

Zastanówmy się teraz nad złożonością metod iteracyjnych. Ponieważ możemy jedynie znaleźć pewne przybliżenie rozwiązania dokładnego x*, przez złożoność metody będziemy rozumieli koszt kombinatoryczny obliczenia xk z zadaną dokładnością ϵ>0. Dla uproszczenia założymy, że medoda jest zbieżna liniowo z ilorazem ρ. Zauważmy, że aby zredukować błąd początkowy do ϵ>0, wystarczy wykonać k=k(ϵ) iteracji, gdzie k spełnia

ρkx0x*ϵ,

czyli

klog(1/ϵ)log(1/x0x*)log(1/ρ)

Liczba ta zależy więc w istotny sposób od błędu początkowego i (przede wszystkim) od współczynnika redukcji błędu ρ, natomiast zależność od dokładności ϵ i wymiaru N układu jest dużo mniej istotna (w zadaniach praktycznych -- takich jak jednowymiarowy laplasjan --- jednak często okazuje się, że... ρ zależy od N!).

Zakładając, że koszt jednej iteracji wynosi c=c(N) (c(N) jest tym mniejszy, im mniejsza jest liczba niezerowych elementów macierzy A), złożoność metody jest proporcjonalna do

c(N)log(1/ϵ)log(1/ρ).

Stąd oczywisty wniosek (prawdziwy nie tylko dla metod stacjonarnych), że metody iteracyjne warto stosować zamiast metod bezpośrednich w przypadku, gdy

  • wymiar N układu Ax=b jest "duży", oraz
  • macierz A układu jest "rozrzedzona", tzn. ma stosunkowo niewielką liczbę elementów niezerowych, np. proporcjonalną do N.

Układy o tych własnościach powstają często przy numerycznym rozwiązywaniu równań różniczkowych cząstkowych.

Metody przestrzeni Kryłowa

Zupełnie inny pomysł na realizację metody iteracyjnej przedstawiają metody przestrzeni Kryłowa, gdzie kolejne przybliżenie xk dobiera się w taki sposób, by minimalizowało pewną miarę błędu na podprzestrzeni Kryłowa

Kk=span{r,Ar,,Ak1r},

gdzie r=bAx0 jest residuum na początku iteracji. (Zwróć uwagę, że przestrzeń Kryłowa jest rozpięta przez kolejne wektory metody potęgowej] --- to nie przypadek!). W zależności od wyboru sposobu miary błędu, dostajemy inną metodę iteracyjną, takie jak CG, GMRES, PCR, BiCG, i inne. Tutaj omówimy pokrótce tylko najpopularniejszą: CG.

CG

Metoda gradientów sprzężonych, w skrócie CG (conjugate gradients), działa przy założeniu, że A jest symetryczna i dodatnio określona.

Kolejne przybliżenie xk ma minimalizować błąd w normie energetycznej indukowanej przez A,

||xkx||A=(xkx)TA(xkx)

na przestrzeni afinicznej x0+Kk. Okazuje się (co nie jest oczywiste --- trzeba skorzystać z rozmaitych własności ortogonalności generowanych wektorów), że takie zadanie minimalizacji daje się bardzo efektywnie rozwiązać, skąd dostajemy bardzo zwarty algorytm:

Algorytm Metoda CG


r = b-A*x;
<math>\rho_0</math> = <math>||r||_2^2</math>; <math>\beta</math> = 0; k = 1;
while (!stop)
{
	p = r + <math>\beta</math>*p;
	w = A*p;
	<math>\alpha</math> = <math>\rho_{k-1}</math>/<math>p^Tw</math>;
	x = x + <math>\alpha</math>*p;
	r = r - <math>\alpha</math>*w;
	<math>\rho_k</math> = <math>||r||_2^2</math>;
	<math>\beta</math> = <math>\frac{\rho_{k}}{\rho_{k-1}}</math>;
	k++;	
}

Jak widać, całą iterację da się wykonać przechowując w pamięci tylko kilka wektorów (a nie, jak możnaby się obawiać, całą przestrzeń Kk), a najdroższym jej elementem jest mnożenie macierzy przez wektor.

Twierdzenie O zbieżności CG jako metody bezpośredniej

Niech ARN×N będzie symetryczna i dodatnio określona. Algorytm CG znajdzie dokładne rozwiązanie po co najwyżej N iteracjach.

Powyższe twierdzenie, choć teoretycznie interesujące, ma małą wartość praktyczną z dwóch powodów:

  • dla bardzo dużych N, wykonanie N iteracji może być wciąż zbyt kosztownym zadaniem;
  • ponieważ w arytmetyce skończonej precyzji ortogonalność --- z której w bardzo istotny sposób korzysta się przy wyprowadzeniu algorytmu --- pogarsza się z iteracji na iterację i w konsekwencji, po wielu iteracjach, jakość xk przestaje się poprawiać.

Dlatego wygodniej potraktować CG jako metodę iteracyjną. Zachodzi bowiem

Twierdzenie Zbieżność CG jako metody iteracyjnej

Po k iteracjach metody CG,

||xkx||A2(cond(A)1cond(A)+1)k||x0x||A,

gdzie cond(A)=||A||2||A1||2.

GMRES

Metoda GMRES (Generalized Minimum RESidual) nie wymaga ani symetrii, ani dodatniej określoności macierzy, jest więc bardziej uniwersalna, choć też bardziej kosztowna od CG. Jej szczegółowe omówienie, w tym --- oszacowania szybkości zbieżności --- wykracza niestety poza ramy niniejszego wykładu.

Ściskanie macierzy

Zbieżność wszystkich poznanych metod iteracyjnych zależy od własności spektralnych macierzy układu. Pojawiające się w zastosowaniach macierze często mają niekorzystne własności spektralne (np. bardzo duży wskaźnik uwarunkowania), przez co metody iteracyjne zbiegają na nich bardzo wolno.

Dlatego bardzo korzystne może być wstępne przetransformowanie układu

Ax=b

z macierzą o niekorzystnych własnościach, do układu

MAx=Mb,

gdzie macierz MA ma znacznie korzystniejsze własności z punktu widzenia używanej metody iteracyjnej. W przypadku macierzy symetrycznych widzieliśmy, że kluczowe znaczenie dla zbieżności metody miał rozkład wartości własnych: jeśli wartości własne były bardzo rozrzucone po prostej, to uwarunkowanie było bardzo duże i w konsekwencji zbieżność powolna. Aby zbieżność była szybsza, kluczowe jest, by:

  • wartości własne MA były upakowane w klastrach
  • najlepiej wszystkie w (małym) otoczeniu wartości 1

Jeśli więc chcielibyśmy przekształcić macierz tak, by metoda iteracyjna dla MA zbiegała szybko, musimy w jakiś sposób "ścisnąć" spektrum macierzy A w okolice jedności. Taką operację nazywamy ściskaniem (preconditioning), a macierz M --- macierzą ściskającą.

Aby całość miała sens, macierz ściskająca M powinna:

  • być łatwa w konstrukcji,
  • być tania w mnożeniu przez wektor (głównym elementem każdej metody iteracyjnej jest mnożenie macierzy przez wektor: M(Ax)),
  • macierz MA powinna mieć znacznie korzystniejsze własności z punktu widzenia używanej metody iteracyjnej.

Kilka ekstremalnych (lecz wątpliwej jakości) "propozycji" na macierz ściskającą to M=I (łatwa w konstrukcji i tania w mnożeniu, ale niestety nic nie polepsza...) oraz M=A1 (rewelacyjnie poprawia zbieżność metody iteracyjnej, dając zbieżność w jednej iteracji, ale bardzo droga w konstrukcji i mnożeniu). Widać więc, że należy poszukiwać czegoś pośredniego, co niskim kosztem przybliża działanie macierzy odwrotnej.

Dlatego jednym z powszechniej stosowanych (aczkolwiek wciąż nie najbardziej skutecznych) sposobów ściskania są te oparte na zastosowaniu jednego kroku klasycznej metody iteracyjnej.

Zbieżność metody CG bez żadnego ściskania oraz ściśniętej imadłem opartym na jednej iteracji (blokowej) metody Jacobiego.

Inne sposoby ściśnięcia macierzy wykorzystują np. techniki tzw. niepełnego rozkładu macierzy, albo --- w specyficznych przypadkach --- tzw. metody wielosiatkowe.

Okazuje się, że zarówno CG jak i GMRES da się zaimplementować tak, by w jednej iteracji było konieczne tylko jedno mnożenie przez macierz ściskającą.

Literatura

W celu dogłębnego zapoznania się z omawianym na wykładzie materiałem, przeczytaj rozdział 4.6 i 4.7 w

  • D. Kincaid, W. Cheney Analiza numeryczna, Wydawnictwa Naukowo-Techniczne, Warszawa 2006, ISBN 83-204-3078-X.

Tym razem wymienione rozdziały nie wystarczą do dogłębnego zrozumienia omawianych treści. Warto więc sięgnąć po jeden z podręczników