MN05LAB: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Dorota (dyskusja | edycje)
Nie podano opisu zmian
Przykry (dyskusja | edycje)
Nie podano opisu zmian
Linia 1: Linia 1:
<!--  
<!--  
Konwertowane  z pliku LaTeX przez latex2mediawiki, zob. http://www.ii.uj.edu.pl/&nbsp;pawlik1/latex2mediawiki.php
Konwertowane  z pliku LaTeX przez latex2mediawiki, zob. http://www.ii.uj.edu.pl/&nbsp;pawlik1/latex2mediawiki.php.
Niezb�dne rozszerzenia i modyfikacje oryginalnego latex2mediawiki
wprowadzi� przykry@mimuw.edu.pl
-->
-->
   
   
=Ćwiczenia: rozwiązywanie układów równań liniowych=
=Rozwiązywanie układów równań liniowych=
 
{{powrot |Metody numeryczne | do strony głównej
przedmiotu <strong>Metody numeryczne</strong>}}
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
Oglądaj wskazówki i rozwiązania __SHOWALL__<br>
Ukryj wskazówki i rozwiązania __HIDEALL__
</div>
 
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<span  style="font-variant:small-caps;">Uwaga</span>
<div class="solution" style="margin-left,margin-right:3em;">
 
Niektóre fragmenty zadań wymagają wykorzystania procedur LAPACKa; te części odłóż do momentu, gdy opanujesz następny wykład, dotyczący m.in. [[MN06#BLAS, LAPACK i ATLAS|bibliotek algebry liniowej]].
</div></div>
 
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<span  style="display: block; background-color:#fefeee; border-bottom: 1px solid #E5E5E5; line-height: 1.1em; padding-bottom: 0.2em; font-variant:small-caps; color:#1A6ABF;">Ćwiczenie: Metoda Cholesky'ego</span>
<div class="exercise">
 
Ważnym przykładem macierzy
szczególnej postaci są macierze <strong>symetryczne i dodatnio określone</strong>.
Są to macierze spełniające warunki:
 
<center><math>\displaystyle A=A^T \qquad  \mbox{oraz}  \qquad x^T A x\,>\,0,\quad\forall x\ne 0.
</math></center>
 
Dla takich macierzy można nieco zmniejszyć koszt kombinatoryczny
i zużycie pamięci, przeprowadzając trochę inny rozkład na macierze trójkątne:
tak, aby otrzymać
rozkład 
 
<center><math>\displaystyle A\,=\,L\, D\, L^T
</math></center>
 
zamiast <math>\displaystyle PA=LU</math>, przy czym <math>\displaystyle L</math> jest tu jak zwykle macierzą
trójkątną dolną z jedynkami na przekątnej, a <math>\displaystyle D</math> jest macierzą
diagonalną z dodatnimi elementami na diagonali. Opracuj taki algorytm. W jego
implementacji możesz porównywać się z procedurą LAPACKa <code style="color: #903">DPOSV</code>.
Inny wariant tego samego
rozkładu to tak zwany rozkład <strong>Cholesky'ego--Banachiewicza</strong>, w którym, przy tych
samych założeniach na <math>\displaystyle A</math>, szukamy rozkładu wykorzystującego tylko jedną macierz
trójkątną dolną:
 
<center><math>\displaystyle A\,=\,\widetilde{L}\, \widetilde{L}^T,
</math></center>
 
(oczywiście tym razem nie żądamy, aby <math>\displaystyle \widetilde{L}</math> miała na diagonali jedynki).
Jaka jest relacja między rozkładem <math>\displaystyle LDL^T</math> a <math>\displaystyle \widetilde{L}\widetilde{L}^T</math>?
</div></div>
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em"> 
Bez zmniejszenia ogólności rozpatrzymy
tylko pierwszy krok rozkładu <math>\displaystyle LDL^T</math>. W tym celu zauważmy najpierw, że
<math>\displaystyle a_{1,1}= e_1^TA e_1>0</math> (gdzie <math>\displaystyle  e_1</math> jest pierwszym
wersorem), a więc nie musimy przestawiać wierszy,
bo element na diagonali jest niezerowy. W pierwszym kroku mnożymy
macierz <math>\displaystyle A</math> z lewej strony przez odpowiednią macierz <math>\displaystyle L_1</math>, a potem
z prawej przez <math>\displaystyle L_1^T</math>. Kluczem do zrozumienia algorytmu jest uwaga,
że efektem mnożenia macierzy <math>\displaystyle L_1 A</math> z prawej strony przez <math>\displaystyle L_1^T</math>
jest wyzerowanie elementów pierwszego wiersza poza <math>\displaystyle a_{1,1}</math>
i pozostawienie niezmienionych pozostałych elementów. Ponadto
macierz <math>\displaystyle A^{(1)}=L_1 AL_1^T</math> jest symetryczna i dodatnio określona.
Rzeczywiście,
 
<center><math>\displaystyle \Big(A^{(1)}\Big)^T\,=\,(L_1AL_1^T)^T
    \,=\,(L_1^T)^TA^TL_1^T\,=\,L_1AL_1^T,
</math></center>
 
oraz dla <math>\displaystyle  x\ne 0</math>
 
<center><math>\displaystyle x^T A^{(1)} x\,=\, x^T L_1 A L_1^T  x
    \,=\,(L_1^T x)^T A(L_1^T x)\,>\,0,
</math></center>
 
bo <math>\displaystyle  x\ne 0</math> implikuje <math>\displaystyle L_1^T x\ne 0</math>. Stąd
<math>\displaystyle a^{(1)}_{2,2}= e_2^TA^{(1)} e_2>0</math>. Postępując tak
dalej otrzymujemy
 
<center><math>\displaystyle L_{n-1}L_{n-2}\cdots L_2L_1AL_1^TL_2^T\cdots L_{n-2}^TL_{n-1}^T
    \,=\,D,
</math></center>
 
przy czym macierz <math>\displaystyle D</math> jest diagonalna i dodatnio określona,
a więc wyrazy na diagonali są dodatnie. Oznaczając
 
<center><math>\displaystyle L\,=\,L_1^{-1}L_2^{-1}\cdots L_{n-1}^{-1}
</math></center>
 
dostajemy żądany rozkład.
 
Zauważmy, że przy praktycznej realizacji rozkładu <math>\displaystyle A=LDL^T</math>
wystarczy modyfikować jedynie wyrazy pod i na głównej przekątnej
macierzy wyjściowej ponieważ, jak zauważyliśmy, wszystkie kolejne
macierze <math>\displaystyle A^{(k)}</math> są symetryczne. Pozwala to zmniejszyć --- w stosunku do
rozkładu LU --- koszt
obliczeniowy o połowę, do <math>\displaystyle 2n^3/3</math> operacji arytmetycznych i zmieścić czynniki
rozkładu w pamięci przeznaczonej na przechowywanie istotnych elementów <math>\displaystyle A</math>
(czyli w dolnym trójkącie macierzy).
 
Oczywiście, <math>\displaystyle \widetilde{L} = L \sqrt{D}</math>.
 
</div></div></div>


<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
Linia 11: Linia 117:
W Octave układ równań <math>\displaystyle Ax=b</math> rozwiązujemy korzystając z "operatora
W Octave układ równań <math>\displaystyle Ax=b</math> rozwiązujemy korzystając z "operatora
rozwiązywania równania"
rozwiązywania równania"
<div class="code" style="background-color:#e8e8e8; padding:1em"><pre>
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>x = A \ b;
</pre></div>  
x = A \ b;
</pre></div>
W tym celu Octave oczywiście wewnętrznie wykorzystuje funkcję LAPACKa
<code>DGESV</code>. Ale w Octave jest także funkcja <code>inv</code> wyznaczająca macierz
odwrotną, niektóre więc (nie najlepsze, oględnie mówiąc) podręczniki zalecają
<div class="code" style="background-color:#e8e8e8; padding:1em"><pre>
   
   
x = inv(A)*b;
Ale w Octave jest także funkcja <code style="color: #006">inv</code>, wyznaczająca macierz
</pre></div>
odwrotną, więc niektóre (nie najlepsze, oględnie mówiąc) podręczniki zalecają
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>x = inv(A)*b;
</pre></div>  
   
   
Przedyskutuj, które podejście jest lepsze i dlaczego. Przeprowadź eksperymenty
Przedyskutuj, które podejście jest lepsze i dlaczego. Przeprowadź eksperymenty
Linia 33: Linia 134:
dodatkowo nie jest numerycznie poprawne.
dodatkowo nie jest numerycznie poprawne.


Przykładowy test mógłby wyglądać tak, jak poniżej. Duże macierze losowe są
Przykładowy test mógłby wyglądać jak poniżej. Duże macierze losowe są
zazwyczaj dobrze uwarunkowane i najczęściej nieosobliwe.
zazwyczaj dobrze uwarunkowane.


<div class="code" style="background-color:#e8e8e8; padding:1em"><pre>
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>N = 400; A = rand(N,N);x = rand(N,1); b = A*x;  
N = 400; A = rand(N,N);x = rand(N,1); b = A*x;  
tic; X = A \ b; tX = toc;  
tic; X = A \ b; tX = toc;  
tic; Y = inv(A)*b; tY = toc;
tic; Y = inv(A)*b; tY = toc;
[norm(X-x)/norm(x),  norm(Y-x)/norm(x); tX, tY]  
[norm(X-x)/norm(x),  norm(Y-x)/norm(x); tX, tY]  
</pre></div>
</pre></div>  
<div class="output" style="background-color:#e0e8e8; padding:1em"><pre>
   
   
ans =
<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;"><nowiki>ans =
   1.9861e-13  2.8687e-13
   1.9861e-13  2.8687e-13
   2.3644e-01  4.0609e-01
   2.3644e-01  4.0609e-01
</pre></div>
</nowiki></div>
   
   
Wyszło (z grubsza) dwa razy szybciej i dwa razy dokładniej.
Wyszło (z grubsza) dwa razy szybciej i dwa razy dokładniej.
Linia 69: Linia 166:
* w komputerze, w arytmetyce podwójnej precyzji, korzystając z rozkładu LU <strong>bez wyboru elementu głównego</strong>
* w komputerze, w arytmetyce podwójnej precyzji, korzystając z rozkładu LU <strong>bez wyboru elementu głównego</strong>
* jak poprzednio, ale <strong>z wyborem elementu głównego w kolumnie</strong>
* jak poprzednio, ale <strong>z wyborem elementu głównego w kolumnie</strong>
* w LAPACKu lub Octave.
* w [http://www.netlib.org/lapack  LAPACKu] lub Octave.
   
   
Porównaj uzyskane rozwiązania i wyciągnij wnioski.
Porównaj uzyskane rozwiązania i wyciągnij wnioski.
Linia 75: Linia 172:


<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em">   
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em">   
Rozwiązanie uzyskane bez wyboru elementu głównego jest bardzo mało precyzyjne <math>\displaystyle x_1</math> i jest obarczone bardzo dużym błędem! Prosta analiza pokazuje, że winę za to ponosi bardzo wielki współczynnik w macierzy <math>\displaystyle L</math>. Nie jest tak, gdy stosujemy wybór elementu: elementy <math>\displaystyle L</math> są zawsze mniejsze od 1. Dlatego inne rozwiązania numeryczne dają w przypadku naszej macierzy wynik będący bardzo dobrym przybliżeniem wyniku dokładnego. ''A propos:'' Octave korzysta z <code>DGESV</code> LAPACKa, a <code>DGESV</code> korzysta z wyboru w kolumnie...
Rozwiązanie uzyskane bez wyboru elementu głównego jest bardzo mało precyzyjne: <math>\displaystyle x_1</math> jest obarczone bardzo dużym błędem! Prosta analiza pokazuje, że winę za to ponosi bardzo wielki współczynnik w macierzy <math>\displaystyle L</math>. Tak nie jest, gdy stosujemy wybór elementu: elementy <math>\displaystyle L</math> są zawsze mniejsze od 1. Dlatego LAPACK i Octave dają w przypadku naszej macierzy rezultat będący bardzo dobrym przybliżeniem wyniku dokładnego.
</div></div></div>
</div></div></div>


Linia 85: Linia 182:


<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Wskazówka </span><div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Wskazówka </span><div class="mw-collapsible-content" style="display:none">
<div style="font-size:smaller; background-color:#efe"> Pamiętaj, pętle w Octave wykonują się bardzo wolno! </div>
<div style="font-size:smaller; background-color:#f9fff9; padding: 1em"> Pamiętaj, pętle w Octave wykonują się bardzo wolno! </div>
</div></div>
</div></div>


Linia 91: Linia 188:


Przetestuj tę funkcję na kilku macierzach i porównaj czas jego działania z czasem
Przetestuj tę funkcję na kilku macierzach i porównaj czas jego działania z czasem
wykonania operacji <code>x = A\b</code>.
wykonania operacji <code style="color: #006">x = A\b</code>.


Spróbuj zastosować swój algorytm do kilku specjalnych macierzy:
Spróbuj zastosować swój algorytm do kilku specjalnych macierzy:
Linia 102: Linia 199:
Najprostszy wariant to po prostu przepisanie algorytmu z wykładu, który już
Najprostszy wariant to po prostu przepisanie algorytmu z wykładu, który już
nawet trochę wykorzystywał notację dwukropkową MATLABa:
nawet trochę wykorzystywał notację dwukropkową MATLABa:
<div class="code" style="background-color:#e8e8e8; padding:1em"><pre>
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>function [L,U] = lufa(A)
function [L,U] = lufa(A)
# Na początku warto dodać trochę sprawdzania danych: czy A jest kwadratowa?  
# Na początku warto dodać trochę sprawdzania danych: czy A jest kwadratowa?  
# A w ogóle czy jest macierzą? Te linie pozostawiamy inwencji Czytelnika
# Czy jest macierzą? Te linie pozostawiamy twojej inwencji


N = size(A,1);
N = size(A,1);
Linia 125: Linia 220:
U = triu(A);
U = triu(A);
end
end
</pre></div>
</pre></div>  
   
   
Jednak w tej implementacji mamy do czynienia z nawet potrójnie zagnieżdżonymi
Jednak w tej implementacji mamy do czynienia z nawet potrójnie zagnieżdżonymi
Linia 132: Linia 227:
blokowych.
blokowych.


<div class="code" style="background-color:#e8e8e8; padding:1em"><pre>
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>function [L,U] = lufb(A)
function [L,U] = lufb(A)
# Na początku warto dodać trochę sprawdzania danych: czy A jest kwadratowa? a w ogóle,
# Na początku warto dodać trochę sprawdzania danych: czy A jest kwadratowa? a w ogóle,
# czy jest macierzą? te linie pozostawiamy inwencji Czytelnika
# czy jest macierzą? te linie pozostawiamy inwencji Czytelnika
Linia 151: Linia 244:
U = triu(A);
U = triu(A);
end
end
</pre></div>
</pre></div>  
   
   
<div class="code" style="background-color:#e8e8e8; padding:1em"><pre>
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>[Kod testujący lufa(0 i lufb()]
[Kod testujący lufa(0 i lufb()]
N = 100;  
N = 100;  
A = rand(N) + 1000*eye(N);
A = rand(N) + 1000*eye(N);
Linia 174: Linia 266:
disp("Błąd:");
disp("Błąd:");
[norm(L*U - A,inf), norm(La*Ua - A,inf), norm(Lb*Ub - A,inf)]
[norm(L*U - A,inf), norm(La*Ua - A,inf), norm(Lb*Ub - A,inf)]
</pre></div>
</pre></div>  
   
   
Dla macierzy wymiaru <math>\displaystyle N=100</math> dostaliśmy na Pentium 1.5GHz  
Dla macierzy losowych wymiaru <math>\displaystyle N=100</math> dostaliśmy na Pentium 1.5GHz  


{| border=1
{| border=1
Linia 190: Linia 282:


Zwróć uwagę na charakterystyczne fakty:
Zwróć uwagę na charakterystyczne fakty:
* Usunięcie wewnętrznych pętli stukrotnie(!) przyspieszyło <code>lufb</code> w
* Usunięcie wewnętrznych pętli stukrotnie(!) przyspieszyło <code style="color: #006">lufb</code> w
stosunku do <code>lufa</code>
stosunku do <code style="color: #006">lufa</code>
* Gotowa funkcja Octave wciąż jest stukrotnie(!) szybsza od mojej
* Gotowa funkcja Octave wciąż jest stukrotnie(!) szybsza od mojej
najszybszej funkcji.
najszybszej funkcji.
Linia 198: Linia 290:
nawet lepiej napisać implementację wykonującą więcej obliczeń, ale korzystającą
nawet lepiej napisać implementację wykonującą więcej obliczeń, ale korzystającą
w większm stopniu z wbudowanych funkcji Octave, niż napisać samemu implementację
w większm stopniu z wbudowanych funkcji Octave, niż napisać samemu implementację
liczącą mniej, ale korzystającą z licznych instrukcji <code>for...</code> i podobnych.
liczącą mniej, ale korzystającą z licznych instrukcji <code style="color: #006">for...</code> i podobnych.


Innym sposobem przyspieszenia działania funkcji Octave jest ich prekompilacja.
Innym sposobem przyspieszenia działania funkcji Octave jest ich prekompilacja.
Linia 206: Linia 298:
Funkcja wykorzystująca gotowy rozkład LU do rozwiązania układu <math>\displaystyle Ax=b</math> w MATLABie miałaby postać
Funkcja wykorzystująca gotowy rozkład LU do rozwiązania układu <math>\displaystyle Ax=b</math> w MATLABie miałaby postać


<div class="code" style="background-color:#e8e8e8; padding:1em"><pre>
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>function x = sol(L,U,b)
function x = sol(L,U,b)
x = U \ ( L \ b);
x = U \ ( L \ b);
end
end
</pre></div>
</pre></div>  
   
   
gdyż MATLAB prawidłowo rozpoznaje układy z macierzą trójkątną i stosuje szybszy
gdyż MATLAB prawidłowo rozpoznaje układy z macierzą trójkątną i stosuje szybszy
algorytm. Niestety, do tej pory w Octave nie jest to możliwe; nie ma też
algorytm.  
funkcji, która rozwiązywałaby układ z macierzą trójkątną (wykorzystując np.
LAPACKa). Zrób więc to i... wyślij do twórców Octave'a!
</div></div></div>


<div style="margin-top:1em; padding-top,padding-bottom:1em;">
Jeśli chodzi o popełnione błędy dla macierzy Hilberta, wyjaśnienie znajdziesz w wykładzie [[MN07|o uwarunkowaniu układu równań liniowych]].
<span  style="display: block; background-color:#fefeee; border-bottom: 1px solid #E5E5E5; line-height: 1.1em; padding-bottom: 0.2em; font-variant:small-caps; color:#1A6ABF;">Ćwiczenie: Czy Twoje programy działają <strong>naprawdę</strong> szybko?</span>
<div class="exercise">
 
Zrób zadanie poprzednie programując, niczym Zosia samosia, wszystko od początku do końca w czystym C. Porównaj czasy działania twojego programu i programu wywołującego po prostu procedurę biblioteczną LAPACKa
<code>DGESV</code>, najlepiej wspartą dobrze podrasowanymi BLASami.
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Wskazówka </span><div class="mw-collapsible-content" style="display:none">
<div style="font-size:smaller; background-color:#efe"> To zadanie to wyzwanie! </div>
</div></div>
 
</div></div>
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em"> 
No tak, namawiamy cię do zmierzenia się z LAPACKiem i ATLASem....
 
Prościutki program --- po prostu tłumaczący algorytm z wykładu bezpośrednio
na C --- u nas spisał
się, dla macierzy wymiaru <math>\displaystyle N=1\ldots 1024</math>, następująco:
 
[[Image:MNdegsvtiming.png|thumb|450px|center|Prosty solver w C kontra LAPACK i LAPACK z ATLASem.
Zwróć uwagę na piki zwykłego kodu dla <math>\displaystyle N</math> będących potęgami dwójki]]
[[Image:MNdegsvtiminglogscale.png|thumb|450px|center|Skala logarytmiczna pozwala lepiej ocenić
różnice pomiędzy czasami wykonania. Prosty program w C jest około dziesięć razy
wolniejszy od kodu korzystającego z LAPACKa i około 50 razy wolniejszy od kodu
korzystającego z LAPACKa i ATLASa.]]


</div></div></div>
</div></div></div>
Linia 259: Linia 320:
</math></center>
</math></center>


Układy równań z tą samą macierzą, ale ze zmieniającą się prawą stroną równania
Układy równań z tą samą macierzą, ale ze zmieniającą się prawą stroną równania
powstają często przy rozwiązywaniu np. równań różniczkowych cząstkowych,
powstają często na przykład przy rozwiązywaniu równań różniczkowych cząstkowych, gdzie  prawa strona układu odpowiada zmieniającym się warunkom  brzegowym.  
gdzie  prawa strona układu odpowiada zmieniającym się warunkom  brzegowym.  


<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Wskazówka </span><div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Wskazówka </span><div class="mw-collapsible-content" style="display:none">
<div style="font-size:smaller; background-color:#efe"> Wystarczy tylko <strong>jeden raz</strong> wyznaczyć rozkład LU macierzy <math>\displaystyle A</math> </div>
<div style="font-size:smaller; background-color:#f9fff9; padding: 1em"> Wystarczy tylko <strong>jeden raz</strong> wyznaczyć rozkład LU macierzy <math>\displaystyle A</math> </div>
</div></div>
</div></div>


Linia 270: Linia 330:


<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em">   
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em">   
No tak, oczywiście  
No tak, oczywiście wystarczy
wystarczy


{{algorytm|||
{{algorytm|||
<pre>
<pre>\EATWSZnajdź rozkład <math>\displaystyle PA = LU</math>;
Znajdź rozkład <math>\displaystyle PA = LU</math>;
Utwórz macierz <math>\displaystyle B = [b_1,\ldots,b_k]</math>;
Utwórz macierz <math>\displaystyle B = [b_1,\ldots,b_k]</math>;
Rozwiąż <math>\displaystyle LY = B</math>;
Rozwiąż <math>\displaystyle LY = B</math>;
Linia 282: Linia 340:


Rozwiązanie każdego z układów <math>\displaystyle LY = B</math> i <math>\displaystyle UX = Y</math> można przyspieszyć, tworząc
Rozwiązanie każdego z układów <math>\displaystyle LY = B</math> i <math>\displaystyle UX = Y</math> można przyspieszyć, tworząc
warianty algorytmów podstawienia w przód i w tył operujące od razu na wszystkich kolumnach macierzy <math>\displaystyle B</math>. Zaimplementowano je w LAPACKu, w tandemie
warianty algorytmów podstawienia w przód i w tył, operujące od razu na wszystkich kolumnach macierzy <math>\displaystyle B</math>. Zaimplementowano je w [http://www.netlib.org/lapack  LAPACKu], w tandemie
funkcji <code>DGETRF</code> (rozkład) i <code>DGETRS</code> (rozwiązanie) oraz w
funkcji <code style="color: #903">DGETRF</code> (rozkład) i <code style="color: #903">DGETRS</code> (rozwiązanie) oraz w
<code>DGESV</code>, łączącej funkcjonalność obu.
<code style="color: #903">DGESV</code>, łączącej funkcjonalność obu.
</div></div></div>
 
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<span  style="display: block; background-color:#fefeee; border-bottom: 1px solid #E5E5E5; line-height: 1.1em; padding-bottom: 0.2em; font-variant:small-caps; color:#1A6ABF;">Ćwiczenie: Metoda Cholesky'ego</span>
<div class="exercise">
 
Ważnym przykładem macierzy
szczególnej postaci są macierze symetryczne i dodatnio określone.
Są to macierze spełniające warunki:
 
<center><math>\displaystyle A=A^T \qquad  \mbox{oraz}  \qquad x^T A x\,>\,0,\quad\forall x\ne 0.
</math></center>
 
Dla takich macierzy można nieco zmniejszyć koszt kombinatoryczny
i zużycie pamięci, przeprowadzając trochę inny rozkład na macierze trójkątne:
tak, aby otrzymać
rozkład 
 
<center><math>\displaystyle A\,=\,L\cdot D\cdot L^T
</math></center>
 
zamiast <math>\displaystyle PA=LU</math>, przy czym <math>\displaystyle L</math> jest tu jak zwykle macierzą
trójkątną dolną z jedynkami na przekątnej, a <math>\displaystyle D</math> jest macierzą
diagonalną z dodatnimi elementami na diagonali. Opracuj taki algorytm. W jego
implementacji możesz porównywać się z procedurą LAPACKa <code>DPOSV</code>.
Inny wariant tego samego
rozkładu to tak zwany rozkład Cholesky'ego--Banachiewicza, w którym przy tych
samych założeniach na <math>\displaystyle A</math> szukamy rozkładu wykorzystującego tylko jedną macierz
trójkątną dolną:
 
<center><math>\displaystyle A\,=\,\widetilde{L}\cdot \widetilde{L}^T,
</math></center>
 
(oczywiście tym razem nie żądamy, aby <math>\displaystyle \widetilde{L}</math> miała na diagonali jedynki).
Jaka jest relacja między rozkładem <math>\displaystyle LDL^T</math> a <math>\displaystyle \widetilde{L}\widetilde{L}^T</math>?
</div></div>
 
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em"> 
Bez zmniejszenia ogólności rozpatrzymy
tylko pierwszy krok rozkładu <math>\displaystyle LDL^T</math>. W tym celu zauważmy najpierw, że
<math>\displaystyle a_{1,1}= e_1^TA e_1>0</math> (gdzie <math>\displaystyle  e_1</math> jest pierwszym
wersorem), nie musimy więc przestawiać wierszy,
bo element na diagonali jest niezerowy. W pierwszym kroku mnożymy
macierz <math>\displaystyle A</math> z lewej strony przez odpowiednią macierz <math>\displaystyle L_1</math>, a potem
z prawej przez <math>\displaystyle L_1^T</math>. Kluczem do zrozumienia algorytmu jest uwaga,
że efektem mnożenia macierzy <math>\displaystyle L_1 A</math> z prawej strony przez <math>\displaystyle L_1^T</math>
jest wyzerowanie elementów pierwszego wiersza poza <math>\displaystyle a_{1,1}</math>
i pozostawienie niezmienionych pozostałych elementów. Ponadto
macierz <math>\displaystyle A^{(1)}=L_1 AL_1^T</math> jest symetryczna i dodatnio określona.
Rzeczywiście,
 
<center><math>\displaystyle \Big(A^{(1)}\Big)^T\,=\,(L_1AL_1^T)^T
    \,=\,(L_1^T)^TA^TL_1^T\,=\,L_1AL_1^T,
</math></center>
 
oraz dla <math>\displaystyle  x\ne 0</math>
 
<center><math>\displaystyle x^T A^{(1)} x\,=\, x^T L_1 A L_1^T  x
    \,=\,(L_1^T x)^T A(L_1^T x)\,>\,0,
</math></center>
 
bo <math>\displaystyle  x\ne 0</math> implikuje <math>\displaystyle L_1^T x\ne 0</math>. Stąd
<math>\displaystyle a^{(1)}_{2,2}= e_2^TA^{(1)} e_2>0</math>. Postępując tak
dalej otrzymujemy
 
<center><math>\displaystyle L_{n-1}L_{n-2}\cdots L_2L_1AL_1^TL_2^T\cdots L_{n-2}^TL_{n-1}^T
    \,=\,D,
</math></center>
 
przy czym macierz <math>\displaystyle D</math> jest diagonalna i dodatnio określona,
a więc wyrazy na diagonali są dodatnie. Oznaczając
 
<center><math>\displaystyle L\,=\,L_1^{-1}L_2^{-1}\cdots L_{n-1}^{-1}
</math></center>
 
dostajemy żądany rozkład.
 
Zauważmy, że przy praktycznej realizacji rozkładu <math>\displaystyle A=LDL^T</math>
wystarczy modyfikować jedynie wyrazy pod i na głównej przekątnej
macierzy wyjściowej ponieważ, jak zauważyliśmy, wszystkie kolejne
macierze <math>\displaystyle A^{(k)}</math> są symetryczne. Pozwala to zmniejszyć --- w stosunku do
rozkładu LU --- koszt
obliczeniowy o połowę, do <math>\displaystyle 2n^3/3</math> operacji arytmetycznych i zmieścić czynniki
rozkładu w pamięci przeznaczonej na przechowywanie istotnych elementów <math>\displaystyle A</math>
(czyli w dolnym trójkącie macierzy).
 
Oczywiście, <math>\displaystyle \widetilde{L} = L \sqrt{D}</math>.
 
</div></div></div>
</div></div></div>


Linia 397: Linia 367:


<center><math>\displaystyle  \mbox{det}  (cA) = c^N A,
<center><math>\displaystyle  \mbox{det}  (cA) = c^N A,
</math></center>,
</math></center>


to dość łatwo będzie trafić (przy większych niż średnich wartościach <math>\displaystyle N</math>) na wartości
to dość łatwo będzie trafić (przy większych niż średnich wartościach <math>\displaystyle N</math>) na wartości wyznacznika, które będą albo potwornie duże, albo potwornie małe. Zresztą, zrób eksperyment:  
wyznacznika, które będą albo potwornie duże, albo potwornie małe. Zrób
eksperyment:  


Najpierw z losową macierzą <math>\displaystyle 100\times 100</math> i jej krotnościami...
Najpierw z losową macierzą <math>\displaystyle 100\times 100</math> i jej krotnościami...


<div class="output" style="background-color:#e0e8e8; padding:1em"><pre>
<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;"><nowiki>octave:26> A=rand(100);
octave:26> A=rand(100);
octave:27> det(A)
octave:27> det(A)
ans = -7.5240e+24
ans = -7.5240e+24
Linia 416: Linia 382:
octave:30> det(1000*A)
octave:30> det(1000*A)
ans = -Inf
ans = -Inf
</pre></div>
</nowiki></div>
   
   
oraz z coraz większymi macierzami losowymi:
oraz z coraz większymi macierzami losowymi:


<div class="output" style="background-color:#e0e8e8; padding:1em"><pre>
<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;"><nowiki>octave:32> det(rand(100))
octave:32> det(rand(100))
ans =  4.3751e+25
ans =  4.3751e+25
octave:33> det(rand(400))
octave:33> det(rand(400))
Linia 428: Linia 392:
octave:34> det(rand(800))
octave:34> det(rand(800))
ans = -Inf
ans = -Inf
</pre></div>
</nowiki></div>
   
   
Można sobie z
Można sobie z
Linia 435: Linia 399:
takich, że <math>\displaystyle  \mbox{det} (A) = m\cdot 10^c</math>, ale  
takich, że <math>\displaystyle  \mbox{det} (A) = m\cdot 10^c</math>, ale  
dopasowując obie tak, by nie nastąpił ani nadmiar, ani niedomiar.  
dopasowując obie tak, by nie nastąpił ani nadmiar, ani niedomiar.  
 
</div></div></div>
</div></div></div>

Wersja z 21:10, 29 wrz 2006


Rozwiązywanie układów równań liniowych

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

Oglądaj wskazówki i rozwiązania __SHOWALL__
Ukryj wskazówki i rozwiązania __HIDEALL__

Uwaga

Niektóre fragmenty zadań wymagają wykorzystania procedur LAPACKa; te części odłóż do momentu, gdy opanujesz następny wykład, dotyczący m.in. bibliotek algebry liniowej.

Ćwiczenie: Metoda Cholesky'ego

Ważnym przykładem macierzy szczególnej postaci są macierze symetryczne i dodatnio określone. Są to macierze spełniające warunki:

A=ATorazxTAx>0,x0.

Dla takich macierzy można nieco zmniejszyć koszt kombinatoryczny i zużycie pamięci, przeprowadzając trochę inny rozkład na macierze trójkątne: tak, aby otrzymać rozkład

A=LDLT

zamiast PA=LU, przy czym L jest tu jak zwykle macierzą trójkątną dolną z jedynkami na przekątnej, a D jest macierzą diagonalną z dodatnimi elementami na diagonali. Opracuj taki algorytm. W jego implementacji możesz porównywać się z procedurą LAPACKa DPOSV. Inny wariant tego samego rozkładu to tak zwany rozkład Cholesky'ego--Banachiewicza, w którym, przy tych samych założeniach na A, szukamy rozkładu wykorzystującego tylko jedną macierz trójkątną dolną:

A=L~L~T,

(oczywiście tym razem nie żądamy, aby L~ miała na diagonali jedynki). Jaka jest relacja między rozkładem LDLT a L~L~T?

Rozwiązanie

Ćwiczenie: Mnożyć przez odwrotność to nie zawsze to samo...

W Octave układ równań Ax=b rozwiązujemy korzystając z "operatora rozwiązywania równania"

x = A \ b;

Ale w Octave jest także funkcja inv, wyznaczająca macierz odwrotną, więc niektóre (nie najlepsze, oględnie mówiąc) podręczniki zalecają

x = inv(A)*b;

Przedyskutuj, które podejście jest lepsze i dlaczego. Przeprowadź eksperymenty numeryczne weryfikujące twoją tezę.

Rozwiązanie

Ćwiczenie: Wybór elementu głównego jest naprawdę ważny!

Rozwiąż prosty układ równań

Parser nie mógł rozpoznać (nieznana funkcja „\aligned”): {\displaystyle \displaystyle \aligned 10^{-18} x_1 + x_2 &= 1,\\ x_1 + 2x_2 &= 4, \endaligned}

czterema sposobami:

  • na kartce papieru (dokładnie!)
  • w komputerze, w arytmetyce podwójnej precyzji, korzystając z rozkładu LU bez wyboru elementu głównego
  • jak poprzednio, ale z wyborem elementu głównego w kolumnie
  • w LAPACKu lub Octave.

Porównaj uzyskane rozwiązania i wyciągnij wnioski.

Rozwiązanie

Ćwiczenie

Zapisz w Octave algorytm rozkładu LU macierzy (bez wyboru elementu głównego) działający in situ.

Wskazówka

Wykorzystaj go do napisania funkcji, która rozwiąże układ równań Ax=b.

Przetestuj tę funkcję na kilku macierzach i porównaj czas jego działania z czasem wykonania operacji x = A\b.

Spróbuj zastosować swój algorytm do kilku specjalnych macierzy:

  • Hilberta dużego wymiaru
  • diagonalnej z jednym elementem bardzo małym (a nawet równym zero)
Rozwiązanie

Ćwiczenie: Układy równań z wieloma prawymi stronami

Podaj sposób taniego wyznaczenia rozwiązania sekwencji k<N układów równań z tą samą macierzą ARN×N i różnymi prawymi stronami:

Axi=bi,i=1,,k.

Układy równań z tą samą macierzą, ale ze zmieniającą się prawą stroną równania powstają często na przykład przy rozwiązywaniu równań różniczkowych cząstkowych, gdzie prawa strona układu odpowiada zmieniającym się warunkom brzegowym.

Wskazówka
Rozwiązanie

Ćwiczenie: Obliczanie wyznacznika macierzy

Bardzo rzadko w praktyce numerycznej zdarza się potrzeba obliczenia wartości wyznacznika macierzy ARN×N. Zaproponuj metodę obliczania det(A) oraz wskaż, jakiego rodzaju problemy numeryczne możesz napotkać.

Rozwiązanie