Przykre testy: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Przykry (dyskusja | edycje)
Przykry (dyskusja | edycje)
mNie podano opisu zmian
Linia 1: Linia 1:


<!--  
<!--  
Linia 6: Linia 7:
-->
-->
   
   
=Wprowadzenie do metod numerycznych=
=Równania nieliniowe=
 
<strong>Metody numeryczne</strong> to dziedzina wiedzy zajmująca się
problemami obliczeniowymi i konstrukcją algorytmów rozwiązywania
zadań matematycznych. Najczęściej, zadania obliczeniowe postawione są w
dziedzinie rzeczywistej (lub zespolonej) i dlatego mówimy o zadaniach
obliczeniowych <strong>matematyki ciągłej</strong> (w odróżnieniu od
[[Matematyka_dyskretna|matematyki dyskretnej]]).
 
==Zadania metod numerycznych==


Aby w ogóle mówić w problemie obliczeniowym, musimy najpierw
W wielu zadaniach, m.in. matematyki stosowanej, spotykamy się z problemem
* określić <strong>dane problemu</strong> i <strong>cel obliczeń</strong>, czyli dokładnie
rozwiązania skalarnego równania nieliniowego postaci <math>\displaystyle f(x) = 0</math>. Oto kilka przykładów:
sformułować zadanie w języku matematyki,  
* określić <strong>środki obliczeniowe</strong> dzięki którym chcemy  osiągnąć cel,
* dla analizy zadania i sposobów jego rozwiązania wygodnie jest zdefiniować
<strong>klasę rozpatrywanych danych</strong> oraz <strong>model obliczeniowy</strong> w obrębie
którego będą działać nasze algorytmy.
 
Wbrew dość powszechnej opinii <strong>nie jest prawdą</strong>, że głównym przedmiotem metod
numerycznych jest badanie wpływu błędów zaokrągleń na wynik. Raczej, głównym
celem metod numerycznych jest konstrukcja optymalnych (w jasno określonym
sensie, np. pod względem wymaganej liczby operacji, lub pod względem ilości
niezbędnej informacji, czy też pod względem dokładności uzyskiwanego wyniku)
algorytmów rozwiązywania konkretnych zadań matematycznych.


<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<span  style="font-variant:small-caps;">Uwaga</span>  
<span  style="font-variant:small-caps;">Przykład</span>  
<div class="solution" style="margin-left,margin-right:3em;">
<div class="solution" style="margin-left,margin-right:3em;">


Nasz przedmiot ma różne wcielenia i z tego powodu czasem nosi inne nazwy, w
Równanie Keplera
zależności od tego, na jaki aspekt metod obliczeniowych jest położony największy
nacisk.
* '''metody numeryczne''' --- główny nacisk idzie na aspekty algorytmiczne;
* '''analiza numeryczna''' --- przede wszystkim badanie właściwości algorytmów, ich optymalności oraz wpływu arytmetyki zmiennopozycyjnej na jakość uzyskanych wyników;
* '''matematyka obliczeniowa''' --- głównie teoretyczna analiza możliwości taniej i dokładnej aproksymacji rozwiązań zadań matematycznych;
* '''obliczenia naukowe''' --- nacisk na praktyczne zastosowania metod numerycznych, symulacje, realizacje na komputerach o dużej mocy obliczeniowej.
Oczywiście, granice podziału nie są ostre i najczęściej typowy wykład z tego
przedmiotu stara się pokazać pełne spektrum zagadnień z nim związanych. Tak
będzie również i w naszym przypadku.


</div></div>
<center><math>\displaystyle f(x) \equiv x - \epsilon \sin(x) - M = 0
</math></center>


==Model obliczeniowy==
jest bardzo ważne w astronomii, jego rozwiązanie pozwala wyznaczyć przyszłe położenie planety. Parametr <math>\displaystyle \epsilon</math> odpowiada ekscentryczności orbity i przyjmuje wartości z przedziału <math>\displaystyle [0,1]</math>. Poza paru prostymi przypadkami, w ogólności równanie Keplera nie daje się rozwiązać w terminach funkcji elementarnych.


Tworząc i analizując algorytmy, jakie będą pojawiać w naszym wykładzie, będziemy
[[grafika:Kepler.jpg|thumb|right||Johann Kepler<br> [[Biografia Kepler|Zobacz biografię]]]]
posługiwać się pewnym uproszczonym modelem obliczeń, dzięki czemu będziemy mogli
skoncentrować się na <strong>esencji</strong> algorytmu, bez detali implementacyjnych ---
zostawiając je na inną okazję (dobra implementacja konkretnego algorytmu może być
sama w sobie interesującym wyzwaniem programistycznym; często bywa, że dobre
implementacje, nawet prostych algorytmów numerycznych, są mało czytelne).


Aby zdefiniować nasz model obliczeniowy, posłużymy się
</div></div>
pojęciem <strong>programu</strong>. Zastosujemy przy tym notację
podobną do tej z języka programowania <strong>C</strong>.


Program składa się z <strong>deklaracji</strong>, czyli opisu obiektów,
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
których będziemy używać, oraz z <strong>poleceń</strong> (<strong>instrukcji</strong>),
<span  style="font-variant:small-caps;">Przykład</span>  
czyli opisu akcji, które będziemy wykonywać.
<div class="solution" style="margin-left,margin-right:3em;">


Dostępnymi obiektami są <strong>stałe</strong> i <strong>zmienne</strong> typu
Znajdowanie miejsc zerowych wielomianu
całkowitego (<code>int</code>),
rzeczywistego (<code>float</code> i <code>double</code>). Typ logiczny symulujemy tak jak w
C wartościami zero-jedynkowymi typu całkowitego.
Zmienne jednego typu mogą być grupowane w wektory albo tablice.


Widzimy więc, że podstawowe algorytmy numeryczne będą bazować na mało
<center><math>\displaystyle f(x) \equiv a_nx^n + \ldots + a_1x + a_0 = 0.
skomplikowanych typach danych. Również nieskomplikowane będą instrukcje naszego
</math></center>
modelowego języka. Dążenie do prostoty (ale nie za wszelką cenę) jest charakterystyczne dla numeryki. Typowe jest zastępowanie skomplikowanych tablic rekordów prostszymi macierzami, eleganckich rekurencji --- zwykłymi pętlami działającymi na danych w miejscu. Jedną z myśli przewodnich numeryki jest bowiem <strong>szybkość</strong>, a rezygnacja z barokowych konstrukcji językowych jest tu analogiczna do sposobu konstrukcji architektury procesora RISC, pod hasłem: ''efektywność przez prostotę''.


(Na pewno zastanawiasz się teraz, jaka jest ''druga'' myśl przewodnia numeryki. To <strong>dokładność</strong>.)
Bardzo wiele modeli matematycznych wymaga rozwiązania równania z wielomianową nieliniowością. Piękne [[MN14|kwadratury]] (Gaussa) opierają się na węzłach będących zerami pewnego wielomianu. Wielomiany są bardzo szczególnymi funkcjami i dla nich istnieje szereg wyspecjalizowanych metod znajdowania ich pierwiastków, m.in. metoda Laguerre'a, metoda Bairstow'a (o nich tu nie będziemy mówić), a także zaskakujące metody sprowadzające zadanie poszukiwania miejsc zerowych wielomianu do zupełnie innego zadania matematycznego  --- o nich jednak będzie mowa dopiero w wykładzie dotyczącym [[MN13|znajdowania wartości własnych macierzy]].
</div></div>


===Podstawienie===
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<span  style="font-variant:small-caps;">Przykład</span>
<div class="solution" style="margin-left,margin-right:3em;">


Najprostsza z instrukcji, bez której nie można się obejść:
Obliczanie pierwiastka kwadratowego z zadanej liczby <math>\displaystyle a</math>, czyli sposób na implementację funkcji "<code>sqrt()</code>". Można to zadanie wyrazić, jako rozwiązywanie równania
<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>z = <math>\displaystyle {\cal W}</math>;
</pre></div>
Tutaj <math>\displaystyle z</math> jest zmienną, a <math>\displaystyle {\cal W}</math> jest <strong>wyrażeniem</strong>
o wartościach tego samego typu co <math>\displaystyle z</math>. Jest to polecenie proste.


Wyrażeniem jest pojedyncza stała lub zmienna, albo złożenie
<center><math>\displaystyle f(x) \equiv x^2 - a = 0.
skończonej liczby <strong>operacji elementarnych</strong> na wyrażeniach. 
</math></center>
Operacje elementarne to:


; arytmetyczno--arytmetyczne:
Szybkie algorytmy wyznaczania pierwiastka kwadratowego były znane już starożytnym. W wykładzie zrozumiemy, dlaczego <strong>metoda Herona</strong>,  
:  <math>\displaystyle x\mapsto -x</math>, <math>\displaystyle (x,y)\mapsto x+y</math>,
<math>\displaystyle (x,y)\mapsto x-y</math>, <math>\displaystyle (x,y)\mapsto x*y</math>,
<math>\displaystyle (x,y)\mapsto x/y, y\ne 0</math>, gdzie <math>\displaystyle x,y</math> są stałymi lub
zmiennymi liczbowymi,


; arytmetyczno--logiczne:
<center><math>\displaystyle  
<math>\displaystyle (x,y)\mapsto x<y</math>,
x_{k+1} = \frac{1}{2}\left(x_k + \frac{a}{x_k}\right)
<math>\displaystyle (x,y)\mapsto x\le y</math>, <math>\displaystyle (x,y)\mapsto x=y</math>, <math>\displaystyle (x,y)\mapsto x\ne y</math>,
</math></center>
gdzie <math>\displaystyle x,y</math> są stałymi lub zmiennymi liczbowymi,
daje bardzo dobre przybliżenie <math>\displaystyle \sqrt{a}</math> już po kilku iteracjach.
</div></div>


; logiczno--logiczne:
:  <math>\displaystyle p\mapsto\,\sim \,p</math>,
<math>\displaystyle (p,q)\mapsto p\, \wedge \,q</math>, <math>\displaystyle (p,q)\mapsto p\, \vee \,q</math>,
gdzie <math>\displaystyle p,q</math> są stałymi lub zmiennymi logicznymi.
Dla niektórych zadań wygodnie jest (a czasem konieczne)
uzupełnić ten zbiór o dodatkowe operacje, takie jak
obliczanie wartości niektórych standardowych funkcji matematycznych
(<math>\displaystyle \sqrt{\;}, \cos(), \sin(), \exp(), \log(),</math> itp.),
czy nawet funkcji bardziej skomplikowanych. Na przykład,
zastosowanie "szkolnych" wzorów na obliczanie pierwiatków
równania kwadratowego byłoby
niemożliwe, gdyby pierwiastkowanie było niemożliwe.
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<span  style="font-variant:small-caps;">Uwaga</span>  
<span  style="font-variant:small-caps;">Przykład</span>  
<div class="solution" style="margin-left,margin-right:3em;">
<div class="solution" style="margin-left,margin-right:3em;">


Należy pamiętać, że w praktyce funkcje  standardowe (o ile są
Implementacja wyznaczania odwrotności liczby <math>\displaystyle a</math> (<strong>bez</strong> dzielenia!) jest możliwa, gdy odpowiednią metodą będziemy poszukiwać rozwiązania równania
dopuszczalne) są obliczane używając  czterech podstawowych operacji
arytmetycznych.  Dokładniej, jednostka arytmetyczna procesora potrafi wykonywać
jedynie operacje <math>\displaystyle +,\,-,\,\times,\,\div</math>, przy czym dzielenie zajmuje kilka razy
więcej czasu niż pozostałe operacje arytmetyczne. Niektóre procesory (np. Itanium 2) są w
stanie wykonywać niejako jednocześnie operację dodawania i mnożenia (tzw. FMADD,
fused multiply and add). Praktycznie wszystkie współczesne procesory mają także
wbudowany koprocesor matematyczny, realizujący asemblerowe polecenia wyznaczenia
wartości standardowych funkcji matematycznych (<math>\displaystyle \sqrt{\;}, \cos(), \sin(),
\exp(), \log(),</math> itp.), jednak wykonanie takiej instrukcji wymaga mniej więcej
kilkadziesiąt razy więcej czasu
niż wykonanie operacji dodawania.
</div></div>


<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<center><math>\displaystyle f(x) \equiv \frac{1}{x} - a = 0.
<span  style="font-variant:small-caps;">Uwaga</span>  
</math></center>
<div class="solution" style="margin-left,margin-right:3em;">


Niestety, aby nasz model obliczeniowy wiernie odpowiadał rzeczywistości, musimy
To zadanie jest ważne praktycznie, np. tak można poprawić precyzję
w nim uwzględnić fakt, że działania matematyczne (i, tym bardziej,
[http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/21928.pdf funkcji wektorowych stosowanych w niektórych procesorach AMD]. Okazuje się, że instrukcja procesora służąca do równoległego obliczania odwrotności sekwencji liczb umieszczonych w 128-bitowym rejestrze wektorowym daje wynik z małą precyzją (oczywiście po to, by wykonywała się szybciej!). Jeśli taka dokładność wyniku nie odpowiada nam, możemy ją --- zgodnie z manualem procesora --- poprawić, rozwiązując właśnie takie równanie jak powyżej, metodą korzystającą wyłącznie z (wektorowych) operacji mnożenia i dodawania.
obliczanie wartości funkcji matematycznych) <strong>nie są</strong> wykonywane dokładnie.
Czasem
uwzględnienie tego faktu wiąże się ze znaczącym wzrostem komplikacji analizy
algorytmu i dlatego "w pierwszym przybliżeniu" często pomija się to
ograniczenie przyjmując model w którym wszystkie (lub prawie wszystkie)
działania arytmetyczne wykonują się dokładnie. Wiedza o tym, <strong>kiedy</strong> i
<strong>jak</strong> zrobić to tak, by wciąż wyciągać prawidłowe wnioski odnośnie faktycznej
realizacji algorytmów w obecności błędów zaokrągleń jest częścią sztuki i wymaga
intuicji numerycznej, popartej doświadczeniem.
</div></div>
</div></div>


Mamy trzy podstawowe polecenia złożone: warunkowe, powtarzania i kombinowane.
==Bisekcja==


===Warunkowe===
<strong>Metoda bisekcji</strong>, czyli <strong>połowienia</strong>, często stosowana w innych
działach informatyki, jest dość
naturalną metodą obliczania zer skalarnych funkcji
ciągłych określonych na danym przedziale <math>\displaystyle [a,b]</math>
i zmieniających znak. Dokładniej, rozpatrzmy klasę
funkcji


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>if(<math>\displaystyle \cal W</math>)
<center><math>\displaystyle  
<math>\displaystyle {\cal A}_1</math>;
  F\,=\,\{\,f\in C([a,b])\,:\;f(a)\cdot f(b) < 0\,\},
else
</math></center>
<math>\displaystyle {\cal A}_2</math>;
</pre></div>
gdzie <math>\displaystyle {\cal W}</math> jest wyrażeniem o wartościach całkowitych (0 odpowiada
logicznemu fałszowi, inne wartości --- logicznej prawdzie), a <math>\displaystyle {\cal A}_1</math>  
i <math>\displaystyle {\cal A}_2</math> są poleceniami, przy czym dopuszczamy polecenia puste.


===Powtarzane===
to znaczy <math>\displaystyle f \in F</math> przyjmują w krańcach przedziału wartości przeciwnego znaku. 
Oczywiście, każda funkcja <math>\displaystyle f\in F</math> ma, na mocy twierdzenia Darboux, co najmniej jedno zero w <math>\displaystyle [a,b]</math>. Startując z przedziału <math>\displaystyle [a,b]</math>, w kolejnych krokach metody bisekcji obliczamy informację o wartości <math>\displaystyle f</math> w środku przedziału, co pozwala nam w następnym kroku zmniejszyć o połowę przedział, w którym na pewno znajduje się zero funkcji.


  <div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>while(<math>\displaystyle {\cal W}</math>)
Bisekcję realizuje następujący ciąg
<math>\displaystyle {\cal A}</math>;
poleceń, po wykonaniu którego <math>\displaystyle x</math> jest przybliżeniem
zera funkcji <math>\displaystyle f</math> z zadaną dokładnością <math>\displaystyle \epsilon</math>.
  <div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>[Metoda bisekcji]
lewy = a; prawy = b;
flewy = f(lewy); fprawy = f(prawy);
x = (a+b)/2; /* przybliżenie rozwiązania */
e = (b-a)/2; /* przedział lokalizujący rozwiązanie dokładne */
while (e > <math>\displaystyle \epsilon</math>)  
{
fx = f(x);  /* reszta */
if ( signbit(fx) != signbit(flewy) )
{
prawy = x;
fprawy = fx;
}
else
{
lewy = x;
flewy = fx;
}
x = (lewy+prawy)/2; e = e/2;
}
</pre></div>
</pre></div>
   
   
gdzie <math>\displaystyle W</math> jest wyrażeniem o wartościach logicznych, a <math>\displaystyle \cal A</math>  
(funkcja języka C <code>signbit</code> bada znak liczby rzeczywistej).
jest poleceniem.
Z konstrukcji metody łatwo wynika, że po wykonaniu
<math>\displaystyle k</math> obrotów pętli <code>while</code> (czyli po obliczeniu <math>\displaystyle k+2</math> wartości funkcji)
otrzymujemy <math>\displaystyle x</math>, które odległe jest od pewnego rozwiązania <math>\displaystyle x^*</math> o co najwyżej


===Kombinowane===
<center><math>\displaystyle
  |x-x^*|\,\le\,\Big(\frac 12\Big)^k
                  \Big(\frac{b-a}{2}\Big).
</math></center>


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>{
Metoda bisekcji jest więc zbieżna <strong>liniowo</strong> z
<math>\displaystyle {\cal A}_1;\displaystyle {\cal A}_2;\displaystyle \ldots\displaystyle {\cal A}_n;</math>
ilorazem <math>\displaystyle 1/2</math>. Choć ta zbieżność nie jest
}
imponująca, bisekcja ma kilka istotnych zalet. Oprócz
</pre></div>
jej prostoty, należy podkreślić fakt, że bisekcja jest
w pewnym sensie uniwersalna. Jeśli tylko dysponujemy dwoma punktami <math>\displaystyle a</math> i <math>\displaystyle b</math>
gdzie <math>\displaystyle {\cal A}_j</math> są poleceniami.
takimi, że <math>\displaystyle f</math> przyjmuje w nich wartości przeciwnych znaków, to metoda bisekcji
z pewnością znajdzie miejsce zerowe funkcji, choćby początkowa długość
przedziału <math>\displaystyle |b-a|</math> była bardzo duża: zbieżność metody bisekcji jest <strong>globalna</strong>. Co ważniejsze, dla zbieżności metody bisekcji
wystarcza jedynie <strong>ciągłość</strong> funkcji. Poza tym
możemy łatwo kontrolować <strong>błąd bezwzględny aproksymacji miejsca zerowego</strong>. Konsekwencją
powyższego oszacowania błędu jest bowiem następujący wniosek.
 
{{wniosek|||
Dla znalezienia zera <math>\displaystyle x^*</math> z dokładnością
<math>\displaystyle \epsilon>0</math>, wystarczy obliczyć w metodzie bisekcji


Na podstawie tych trzech poleceń można tworzyć inne, takie
<center><math>\displaystyle k\,=\,k(\epsilon)\,=\,
jak pętle <code>for()</code>, czy <code>switch()</code>, itd.
    \Big\lceil{\log_2\frac{(b-a)}{\epsilon}}\Big\rceil - 1
</math></center>


Mamy też dwa szczególne polecenia, które odpowiadają
wartości funkcji.
za "wejście" i "wyjście".
}}


===Wprowadzanie danych===
==Iteracja prosta Banacha==


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre><math>\displaystyle {\cal IN}</math>(x,t);
Zupełnie inne, i jak się okaże --- przy odrobinie sprytu bardzo skuteczne ---
</pre></div>
podejście do wyznaczania miejsca zerowego jest oparte na <strong>metodzie Banacha</strong>. Dla większej ogólności, będziemy zakładać teraz, że <math>\displaystyle f: D\rightarrow R^N</math> i <math>\displaystyle D</math> jest otwartym, niepustym podzbiorem <math>\displaystyle R^N</math>.  
gdzie <math>\displaystyle x</math> jest zmienną rzeczywistą, a <math>\displaystyle t</math> "adresem" pewnego
funkcjonału <math>\displaystyle L:F\toR</math> należącym to pewnego zbioru <math>\displaystyle T</math>.
W wyniku wykonania tego polecenia w zmiennej <math>\displaystyle x</math> zostaje
umieszczona wartość <math>\displaystyle L_t(f)</math>.  


Polecenie to pozwala zdobyć <strong>informację</strong> o danej <math>\displaystyle f</math>.
Najpierw nasze równanie nieliniowe
Jeśli <math>\displaystyle F=R^n</math> to zwykle mamy <math>\displaystyle T=\{1,2,\ldots,n\}</math> i
<math>\displaystyle L_i(f)=f_i</math>, co w praktyce odpowiada wczytaniu <math>\displaystyle i</math>-tej
współrzędnej wektora danych. W szczególności, ciąg poleceń
<math>\displaystyle {\cal IN}(x[i],i)</math>, <math>\displaystyle i=1,2,\ldots,n</math>, pozwala uzyskać pełną
informację o <math>\displaystyle f</math>. Jeśli zaś <math>\displaystyle F</math> jest pewną klasą
funkcji <math>\displaystyle f:[a,b]\toR</math>, to możemy mieć np. <math>\displaystyle T=[a,b]</math> i <math>\displaystyle L_t(f)=f(t)</math>.
W tym przypadku, wykonanie polecenia <math>\displaystyle {\cal IN}(x,t)</math> odpowiada
w praktyce skorzystaniu ze specjalnej procedury (albo urządzenia
zewnętrznego) obliczającej (mierzącego) wartość funkcji <math>\displaystyle f</math>
w punkcie <math>\displaystyle t</math>.


===Wyprowadzanie wyników===
<center><math>\displaystyle
f(x) = 0
</math></center>


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre><math>\displaystyle {\cal OUT}</math>(<math>\displaystyle {\cal W}</math>);
przekształcamy (dobierając odpowiednią funkcję <math>\displaystyle \phi</math>) do równania równoważnego
</pre></div>
(tzn. mającego te same rozwiązania)
gdzie <math>\displaystyle {\cal W}</math> jest wyrażeniem o wartościach rzeczywistych.
Polecenie to pozwala "wskazać" kolejną współrzędną wyniku.  


Zakładamy, że na początku procesu obliczeniowego wartości
<center><math>\displaystyle  
wszystkich zmiennych są nieokreślone, oraz że dla dowolnych
  x\,=\,\phi( x).
danych wykonanie programu wymaga wykonania skończonej liczby
</math></center>
operacji elementarnych. Wynikiem obliczeń jest skończony ciąg
liczb rzeczywistych (albo <math>\displaystyle puste</math>), którego kolejne współrzędne
pokazywane są poleceniem <math>\displaystyle {\cal OUT}</math>.


==Środowisko obliczeniowe==
Taki <math>\displaystyle x</math>, dla którego zachodzi powyższa równość, nazywamy <strong>punktem stałym</strong> odwzorowania <math>\displaystyle \phi</math>.


Ze względu na swój utylitarny charakter, w metodach numerycznych niemal równie
Następnie, startując z pewnego przybliżenia
ważna jak dobór optymalnego algorytmu jest jego efektywna <strong>implementacja</strong> na
początkowego <math>\displaystyle x_0 \in D</math>, konstruujemy ciąg kolejnych
konkretnej architekturze.
przybliżeń <math>\displaystyle x_k</math> według wzoru


W praktyce mamy dwie możliwości:
<center><math>\displaystyle x_k\,=\,\phi( x_{k-1}),\qquad k\ge 1.
* wykorzystanie standardowych języków programowania (C, Fortran, być może ze wstawkami w asemblerze) oraz wyspecjalizowanych bibliotek
</math></center>
* użycie gotowego środowiska obliczeń numerycznych będącego wygodnym interfejsem do  specjalizowanych bibliotek numerycznych
Zaleta pierwszego podejścia to (zazwyczaj) szybko działający kod wynikowy, ale kosztem długotrwałego i żmudnego programowania. W drugim przypadku jest na odwrót: developerka i testowanie --- wyjątkowo ważne w przypadku programu numerycznego --- postępują bardzo szybko, ale czasem kosztem ogólnej efektywności uzyskanego produktu.


====Języki programowania: C i Fortran====
[[grafika:Banach.jpg|thumb|right||Stefan Banach<br>  [[Biografia Banach|Zobacz biografię]]]]


Programy numeryczne (a przynajmniej ich jądra obliczeniowe) są zazwyczaj niezbyt
{{twierdzenie|Banacha, o kontrakcji||
wymagające jeśli chodzi o struktury danych, co więcej, prostota struktur danych
szybko rewanżuje się efektywniejszym kodem. Dlatego, trawestując Einsteina, w
dobrym programie numerycznym należy


<blockquote  style="background-color: #fefeee; padding:1em;  margin-left,margin-right:2em;  margin-top,margin-bottom: 1em;">
Niech <math>\displaystyle D_0</math> będzie domkniętym
używać tak prostych struktur danych, jak to
podzbiorem dziedziny <math>\displaystyle D</math>,  
możliwe (ale nie prostszych!...)
</blockquote>  
Językami opartymi na prostych konstrukcjach programistycznych są: Fortran i C. Dlatego właśnie są to języki
dominujące współcześnie pisane programy numeryczne. O ile w przeszłości
hegemonia Fortranu była nie do podważenia, o tyle teraz coraz więcej
oprogramowania numerycznego powstaje w C.


W naszym wykładzie wybieramy C ze względu na jego uniwersalność,
<center><math>\displaystyle \overline D_0\,=\,D_0\subset D,  
doskonałą przenośność i całkiem dojrzałe kompilatory. Dodajmy,
</math></center>
że funkcje w C można mieszać np. z gotowymi bibliotekami napisanymi w
Fortranie. Fortran, język o bardzo długiej tradycji, wciąż żywy i  mający grono wiernych fanów, jest nadal wybierany przez numeryków na całym świecie między innymi ze względu na jego dopasowanie do zadań obliczeniowych (właśnie w tym celu powstał), a także ze względu na doskonałe kompilatory dostępne na superkomputerach, będące efektem wieloletniej ewolucji i coraz lepszego nie tylko dopasowania kompilatora do spotykanych konstrukcji językowych, ale także na odwrót --- coraz lepszego zrozumienia programistów, jak pisać programy, by wycisnąć jak najwięcej z kompilatora.


Inne popularne języki: [[Programowanie_obiektowe|Java]], Pascal (ten język, zdaje się, jest popularny już
w którym <math>\displaystyle \phi</math> jest odwzorowaniem zwężającym.  
tylko w obrębie [http://www.mimuw.edu.pl  wydziału MIM UW]...), VisualBasic i inne,  [http://www.cs.berkeley.edu/&nbsp;wkahan/JAVAhurt.pdf  nie są zbyt odpowiednie] dla
To znaczy, <math>\displaystyle \phi(D_0)\subset D_0</math>, oraz istnieje stała
obliczeń numerycznych. Mało tego, np. podstawowy typ numeryczny Pascala:
<math>\displaystyle 0\le L<1</math> taka, że
<code>real</code> nie jest zgodny z powszechnym standardem [[MN03|IEEE 754]]. Jednak, ze względu na coraz większą
komplikację kodów numerycznych służących np. do prowadzenia zaawansowanych
symulacji metodą elementu skończonego, coraz więcej kodów wykorzystuje
możliwości obiektowych języków C++ i Fortran90.


W przykładach będziemy najczęściej odnosić się do architektury x86, tzn. 32-bitowej IA-32
<center><math>\displaystyle \|\phi( x)-\phi( y)\|\,\le\,L\,\| x- y\|,
procesorów firmy [http://developer.intel.com  Intel] i [http://developer.amd.com  AMD], najczęściej spotykanej w obecnie używanych
    \qquad\forall x, y\in D_0.
komputerach. Należy jednak pamiętać, że obecnie następuje przejście na
</math></center>
architekturę 64-bitową. Ze względu jednak na brak pewności co do ostatecznie
przyjętych standardów w tym obszarze, ograniczymy się do procesorów
32-bitowych. W przykładach będziemy korzystać z kompilatora [http://gcc.gnu.org  GCC], który jest omówiony w wykładzie [[Środowisko_programisty|Środowisko programisty]].


====Prosty program numeryczny w C====
Wtedy równanie


Napiszemy teraz program obliczający (w niezbyt wyrafinowany sposób) <math>\displaystyle N</math>-tą sumę
częściową szeregu harmonicznego
<center><math>\displaystyle  
<center><math>\displaystyle  
x = \sum_{i=1}^N \frac{1}{i}.
  x\,=\,\phi( x).  
</math></center>
</math></center>


Przyjmijmy, że parametr <math>\displaystyle N</math> będzie miał wartość równą 2006. W pierwszym odruchu,
ma dokładnie jedno
prawie każdy początkujący student pisze program w rodzaju:
rozwiązanie <math>\displaystyle x^*</math>, oraz


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>#include <stdio.h>
<center><math>\displaystyle x^*\,=\,\lim_{k\to\infty} x_k,
#define N 2006
</math></center>


int main(void)
dla dowolnego przybliżenia początkowego
{
<math>\displaystyle  x_0\in D_0</math>.
float x;
}}
unsigned int i;
 
{{dowod|||
Wobec


x = 0.0;
<center><math>\displaystyle \aligned \| x_k- x_{k-1}\| &=  
for(i = 1; i <= N; i++)  
  \|\phi( x_{k-1})-\phi( x_{k-2})\|
x = x + 1/i;
  \,\le\,L\,\| x_{k-1}- x_{k-2}\| \\
printf("Wartość sumy x = 1 + 1/2 + ... + 1/&#37;d jest równa &#37;g\n", N, x);
  &\le &\cdots\;\le\;L^{k-1}\| x_1- x_0\|,
\endaligned</math></center>


return(0);
dla <math>\displaystyle k\ge s</math> mamy
}
</pre></div>
Sęk w tym, że ten program <strong>nie działa!</strong> To znaczy: owszem, kompiluje się i
uruchamia, ale twierdzi uparcie, że nasza suma wynosi... 1.


Winę za to ponosi linijka
<center><math>\displaystyle \aligned \| x_k- x_s\|
<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>x = x + 1/i;
  &\le & \sum_{j=s+1}^k\| x_j- x_{j-1}\| 
</pre></div>
    \,\le\,\sum_{j=s+1}^k L^{j-1}\| x_1- x_0\| \\
   
  &= L^s(1+L+\cdots+L^{k-s-1})\| x_1- x_0\|
w której wykonujemy dzielenie <code>1/i</code>. Obie liczby są typu <code>int</code> i
    \,\le\,\frac{L^s}{1-L}\| x_1- x_0\|.
dlatego, zgodnie z regułami C, wynik ich dzielenia także jest całkowity:
\endaligned</math></center>
dostajemy część całkowitą z dzielenia tych dwóch liczb, czyli, gdy tylko <math>\displaystyle i >
1</math>, po prostu zero.


Prawidłowy program musi więc wymusić potraktowanie choć jednej z tych liczb jako
Ciąg <math>\displaystyle \{ x_k\}_k</math> jest więc ciągiem Cauchy'ego.
liczby zmiennoprzecinkowej, co najprościej uzyskać albo przez
Stąd istnieje granica
<math>\displaystyle \alpha=\lim_{k\to\infty} x_k</math>, która należy do
<math>\displaystyle D_0</math>, wobec domkniętości tego zbioru. Ponieważ
lipschitzowskość <math>\displaystyle \phi</math> implikuje jej ciągłość,  
mamy też 


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>x = x + 1.0/i;
<center><math>\displaystyle \phi(\alpha)\,=\,\phi\Big(\lim_{k\to\infty} x_k\Big)
</pre></div>
  \,=\,\lim_{k\to\infty}\phi( x_k)
  \,=\,\lim_{k\to\infty} x_k\,=\,\alpha,
albo bardziej uniwersalnie, rzutując choć jedną z liczb na odpowiedni typ:
</math></center>


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>x = x + 1/((float) i);
tzn. <math>\displaystyle \alpha</math> jest punktem stałym odwzorowania <math>\displaystyle \phi</math>.
</pre></div>
Dla jednoznaczności zauważmy, że jeśliby istniał
drugi, różny od <math>\displaystyle \alpha</math>, punkt stały <math>\displaystyle \beta</math>,
Poprawny kod miałby więc postać
to mielibyśmy


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>#include <stdio.h>
<center><math>\displaystyle \|\alpha-\beta\|\,=\,
#define N 2006
  \|\phi(\alpha)-\phi(\beta)\|
  \,\le\,L\,\|\alpha-\beta\|.
</math></center>


int main(void)
Stąd <math>\displaystyle 1<L</math>, co jest sprzeczne z założeniem, że
{
<math>\displaystyle \phi</math> jest zwężająca. }}
float x;
unsigned int i;


x = 0.0;
Z powyższych rozważań otrzymujemy natychmiastowy
for(i = 1; i <= N; i++)
wniosek dotyczący zbieżności iteracji prostych.  
x = x + 1.0/i;
printf("Wartość sumy x = 1 + 1/2 + ... + 1/&#37;d jest równa %g\n",  N, x);


return(0);
{{wniosek|||
}
Przy założeniach [[#Banacha, o kontrakcji|twierdzenia Banacha]],
</pre></div>
metoda iteracji prostych jest zbieżna co
najmniej liniowo z ilorazem <math>\displaystyle L</math>, tzn.
====Typy numeryczne w C====


W języku C mamy dostępnych sześć typów numerycznych:
<center><math>\displaystyle \| x_k- x^*\|\,\le\,L^k\,\| x_0- x^*\|.
* stałoprzecinkowe, dla reprezentacji liczb całkowitych
</math></center>
** <code>int</code> oraz <code>long int</code>.  W realizacji [http://gcc.gnu.org  GCC] na komputery klasy PC, oba typy: <code>int</code> i <code>long int</code> są identyczne (32-bitowe) i ich zakres wynosi około <math>\displaystyle -2.1\cdot 10^9\ldots +2.1\cdot 10^9</math>. Typ <code>int</code> i jemu pokrewne  odnoszą się do liczb całkowitych ze znakiem (dodatnich lub ujemnych). Ich warianty bez znaku: <code>unsigned int</code>, itp. odnoszą się do liczb bez znaku (nieujemnych), dlatego np. zakresem <code>unsigned int</code> będzie w przybliżeniu <math>\displaystyle 0\ldots +4.2\cdot 10^9</math>.
** <code>long long int</code> (64-bitowy) o zakresie w przybliżeniu <math>\displaystyle -9.2\cdot 10^{18}\ldots +9.2\cdot 10^{18}</math>.
* zmiennopprzecinkowe, dla reprezentacji liczb rzeczywistych
** <code>float</code>, pojedynczej precyzji, 32-bitowy, gwarantuje precyzję około <math>\displaystyle 10^{-7}</math> i zakres liczb reprezentowalnych w przybliżeniu <math>\displaystyle 10^{-38}\ldots 10^{38}</math>;
** <code>double</code>, podwójnej precyzji, 64-bitowy, ma precyzję na poziomie <math>\displaystyle 10^{-16}</math> przy orientacyjnym zakresie <math>\displaystyle 10^{-308}\ldots 10^{308}</math>;
** <code>long double</code>, rozszerzonej podwójnej precyzji, na pecetach 80-bitowy, ale w pamięci zajmuje on 12 bajtów) o precyzji rzędu <math>\displaystyle 10^{-20}</math> i odpowiadający standardowi double extended IEEE 754.
Powyższe typy zmiennoprzecinkowe w realizacji na PC odpowiadają
[http://www.cs.berkeley.edu/&nbsp;wkahan/ieee754status/754story.html  standardowi IEEE 754]. Standardowo, operacje arytmetyczne na obu typach <code>float</code> i <code>double</code> są tak samo pracochłonne, gdyż wszystkie obliczenia w C wykonywane są z maksymalną dostępną precyzją (czyli, na procesorach architektury IA-32 Intela i AMD: w precyzji oferowanej przez typ <code>long double</code>), a następnie dopiero wynik zapisywany do zmiennej reprezentowany jest w stosownym typie . Jednakże typ pojedynczej precyzji <code>float</code> oferuje znacznie większe możliwości optymalizacji uzyskanego kodu a ponadto, obiekty typu <code>float</code> zajmują dwukrotnie mniej miejsca w pamięci niż <code>double</code>, dając możliwość lepszego wykorzystania pamięci podręcznej (''cache'')  i przetwarzania wektorowego.


====Stałe matematyczne i podstawowa biblioteka matematyczna====
}}


Język C jest językiem "małym" i, jak wiadomo, nawet proste operacje
wejścia-wyjścia są w istocie nie częścią języka, ale funkcjami (makrami?)
bibliotecznymi. Z drugiej strony jednak, jak zorientowaliśmy się, nie stwarza to
programiście żadnych specjalnych niedogodności. Podobnie rzecz ma się z
funkcjami matematycznymi.  Podstawowe funkcje matematyczne (<math>\displaystyle \sin, \cos, \exp,
\ldots</math>, itp.) nie są składnikami języka C, lecz w zamian są zaimplementowane w
tzw. standardowej bibliotece matematycznej <code style="color: #666">libm.a</code>; prototypy tych
funkcji oraz definicje rozmaitych stałych matematycznych: <math>\displaystyle \pi, e, \ldots</math> itp.
znajdują się w pliku nagłówkowym <code style="color: #666">math.h</code>. Aby więc skorzystać z tych
funkcji w programie, należy 
* w nagłówku pliku, w którym korzystamy z funkcji lub stałych matematycznych,  umieścić linię <code>#include <math.h></code>
* przy linkowaniu dołączyć bibliotekę matematyczną za pomocą opcji <code style="color: #666">-lm</code>
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<div style="margin-top:1em; padding-top,padding-bottom:1em;">
<span  style="font-variant:small-caps;">Przykład</span>  
<span  style="font-variant:small-caps;">Przykład</span>  
<div class="solution" style="margin-left,margin-right:3em;">
<div class="solution" style="margin-left,margin-right:3em;">


Oto przykładowy prosty program numeryczny w C; drukuje on tablicę wartości
Dla ilustracji, rozpatrzmy
sinusów losowo wybranych liczb z przedziału <math>\displaystyle [0,2\pi]</math>.
równanie Keplera, gdy <math>\displaystyle \epsilon < 1</math>:


<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>[{Tablica losowych sinusów}]
<center><math>\displaystyle
#include <math.h>
  x\,=\,M+\epsilon\sin(x), \qquad \mbox{dla} \qquad x\in R.
#include <stdio.h>  
</math></center>
#include <stdlib.h> /* zawiera definicję funkcji rand() i stałej RAND_MAX */
#define N 15 /* ile liczb wydrukować */


int main(void)
[[Image:MNrownaniekeplera.png|thumb|550px|center|Graficzna ilustracja równania Keplera dla <math>\displaystyle M=1</math> i <math>\displaystyle \epsilon = \frac{1}{4}</math>.]]
{
int i;
double x,y;


for( i = 0; i < N; i++)
W tym przypadku <math>\displaystyle \phi(x)=M+\epsilon\,\sin(x)</math>. Zauważmy, że w  
{
funkcja <math>\displaystyle \phi</math> jest zwężająca ze stałą
x = rand()/(double)RAND_MAX;
x *= 2.0*M_PI;
/* oczywiście, wystarczyłoby x =(2.0*M_PI*rand())/RAND_MAX; */
y = sin(x);
fprintf(stderr, "(&#37;3d) x = &#37;10.5e sin(x) = &#37;10.5e\n", i, x, y);
}
return(0);
}
</pre></div>
Zwróćmy uwagę na linię <code>x = rand()/(double)RAND_MAX;</code> Funkcja <code>rand()</code>
zwraca losową liczbę całkowitą z przedziału [0,<code>RAND_MAX</code>], więc iloraz z
predefiniowaną stałą <code>RAND_MAX</code> będzie liczbą z przedziału <math>\displaystyle [0,1]</math>. Dla
prawidłowego uzyskania losowej liczby z przedziału <math>\displaystyle [0,1]</math> kluczowe jest jednak
zrzutowanie jednej z dzielonych liczb na typ <code>double</code>! Gdyby tego nie zrobić,
uzyskalibyśmy zawsze <math>\displaystyle x=0</math> lub sporadycznie <math>\displaystyle x=1</math>, zgodnie z regułą C: "<strong>typ
wyniku jest zgodny z typem argumentów</strong>". Rzeczywiście, w naszym wypadku,
(błędna) linia <code>x = rand()/RAND_MAX;</code> zostałaby wykonana tak: ponieważ
wynikiem funkcji <code>rand()</code> jest <code>int</code> i stała <code>RAND_MAX</code> jest także typu
<code>int</code>, to wynik ma być również typu <code>int</code> -- zostanie więc wykonane dzielenie
całkowite; ponieważ mamy <code>rand() <math>\displaystyle \leq</math> RAND_MAX</code>, to wynikiem będzie
albo 0, albo 1, i taki rezultat, po zamianie na typ <code>double</code>, zostanie przypisany
zmiennej <math>\displaystyle x</math>, co oczywiście nie jest naszym zamiarem. Natomiast, gdy
przynajmniej jedna z dzielonych liczb jest typu <code>double</code>, to oczywiście wynikiem
też musi być liczba typu <code>double</code>, zostanie więc wykonane zwyczajne dzielenie
dwóch liczb zmiennoprzecinkowych (wynik <code>rand()</code> automatycznie zostanie
zrzutowany na typ <code>double</code>).


Kompilujemy ten program, zgodnie z uwagami uczynionymi na początku, poleceniem
<center><math>\displaystyle L \leq \max_{x} |\phi'(x)| \leq \epsilon < 1.
</math></center>


<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:#fcfcfc;"><nowiki>gcc -o sinusy sinusy.c -lm
Ponieważ obrazem prostej przy przekształceniu <math>\displaystyle \phi</math> jest odcinek <math>\displaystyle D = [M-\epsilon, M+\epsilon]</math>, to znaczy, że <math>\displaystyle \phi</math> --- ograniczona do <math>\displaystyle D</math> --- spełnia założenia [[#Banacha, o kontrakcji|twierdzenia Banacha o kontrakcji]].
</nowiki></div>
Stąd istnieje dokładnie jedno rozwiązanie naszego równania
w przedziale <math>\displaystyle D</math>. Rozwiązanie to może
być aproksymowane z dowolnie małym błędem przy pomocy
iteracji prostych, startując z dowolnego przybliżenia
początkowego <math>\displaystyle x_0\in D</math>. Jednak, gdy <math>\displaystyle \epsilon \approx 1</math>, zbieżność może być bardzo powolna... (Wkrótce przekonasz się, że są szybsze metody).
</div></div>
</div></div>


====Środowisko obliczeń numerycznych: MATLAB i jego klony====
Zaletą iteracji prostych jest fakt, że zbieżność
nie zależy od wymiaru <math>\displaystyle n</math> zadania, ale tylko od stałej
Lipschitza <math>\displaystyle L</math> (jednak w praktyce czasem sama stała Lipschitza może zależeć od
wymiaru zadania...). Metoda Banacha ma szczególne zastosowanie w
przypadku, gdy funkcja <math>\displaystyle \phi</math> jest zwężająca na całym
zbiorze <math>\displaystyle D</math>, tzn. <math>\displaystyle D_0=D</math>. Jeśli ponadto <math>\displaystyle D</math> ma
skończoną średnicę, <math>\displaystyle  \mbox{diam} (D) < +\infty</math>, to dla
osiągnięcia <math>\displaystyle \epsilon</math>-aproksymacji zera funkcji <math>\displaystyle f</math>
wystarczy wykonać


W końcu lat siedemdziesiątych ubiegłego wieku, Cleve Moler wpadł na pomysł
<center><math>\displaystyle k\,=\,k(\epsilon)\,=\,\Big\lceil\frac
stworzenia prostego interfejsu do ówcześnie istniejących bibliotek numerycznych
  {\log(\| x_0- x^*\|/\epsilon)}{\log(1/L)}\Big\rceil
algebry liniowej: pakietów LINPACK  i EISPACK
  \,=\,\Big\lceil\frac
  {\log( \mbox{diam} (D)/\epsilon)}{\log(1/L)}\Big\rceil
(których był współautorem). Stworzone przez niego:
</math></center>
język skryptów i łatwe w użyciu narzędzia manipulacji macierzami, zdobyły
 
wielką popularność w środowisku naukowym. W 1984 roku Moler skomercjalizował swe
iteracji, niezależnie od <math>\displaystyle x_0</math>. Metody zbieżne dla
dzieło pod nazwą [http://www.mathworks.com  MATLAB] (od:
dowolnego przybliżenia początkowego nazywamy
"MATrix LABoratory").
<strong>zbieżnymi globalnie</strong>. Obie przedstawione dotychczas metody: bisekcji i
Banacha, przy rozsądnych
założeniach, są zbieżne globalnie.
 
Okazuje się, że metoda iteracji prostej może być --- w bardzo szczególnych
przypadkach --- zbieżna szybciej niż liniowo. Z taką sytuacją będziemy mieli do czynienia, gdy rozpatrzymy metodę Newtona.
 
==Metoda Newtona==


[[Image:MNmatlab-screenshot.png|thumb|550px|center|Typowa sesja MATLABa. Zwróć uwagę na edytor kodu źródłowego na
Zarówno metoda Banacha, jak i bisekcja, są zbieżnie liniowo, co w praktyce może
bieżąco interpretujący go i wychwytujący potencjalne błędy]]
okazać się zbieżnością dość powolną (np. dla metody zbieżnej liniowo z ilorazem
<math>\displaystyle 1/2</math> dopiero po piątej iteracji dostajemy kolejną
dokładną cyfrę wyniku). Wykorzystując więcej informacji o funkcji <math>\displaystyle f</math>, której
miejsca zerowego poszukujemy, możemy istotnie przyspieszyć zbieżność metody.
Ceną, jaką przyjdzie nam zapłacić, będzie utrata globalnej zbieżności.


MATLAB wkrótce rozrósł się potężnie, implementując (lub wykorzystując) wiele
W dalszych rozważaniach będziemy zakładać dla
najlepszych z istniejących algorytmów numerycznych, a także oferując bogate
uproszczenia, że dziedzina <math>\displaystyle D=R</math>.  
możliwości wizualizacji wyników. Dzięki swemu interfejsowi, składającemu się z
prostych, niemal intuicyjnych funkcji, oraz ogromnym możliwościom
jest jednym z powszechniej używanych pakietów do prowadzenia symulacji
komputerowych w naukach przyrodniczych (i nie tylko).


Interpreter MATLABa jest dosyć wolny, dlatego podstawową regułą pisania
Idea <strong>metody Newtona</strong> opiera się na popularnym wśród inżynierów pomyśle <strong>linearyzacji</strong>: zamiast szukać miejsca zerowego skomplikowanej <math>\displaystyle f</math>, przybliżmy ją
efektywnych kodów w MATLABie jest maksymalne wykorzystanie gotowych
linią prostą, a dla niej już umiemy znaleźć miejsce zerowe!
(prekompilowanych) funkcji MATLABa oraz --- zredukowanie do minimum
obecności takich struktur programistycznych jak pętle.


Kierując się podobnymi przesłankami co C.&nbsp;Moler, oraz bazując na wielkim
[[grafika:Newton.jpg|thumb|right||Isaac Newton<br> Przypisywanie metody
sukcesie MATLABa, John W.&nbsp;Eaton z Wydziału Inżynierii Chemicznej Uniwersytetu
stycznych Newtonowi jest pewną przesadą. Metodę Newtona taką, jaką znamy (z
Wisconsin w USA, zaczął w 1994 roku opracowywać darmowe (udostępniane na tzw.
pochodną w mianowniku), zaproponował w 1740 roku Simpson (ten od kwadratury), kilknaście lat po śmierci Newtona. Żeby było jeszcze zabawniej, odkrywcą
licencji GPL) oprogramowanie o funkcjonalności maksymalnie
metody siecznych zdaje się być... Newton! Więcej na ten temat przeczytasz w
zbliżonej do MATLABa: [http://www.octave.org  Octave]. Wersja 1.0 pakietu Octave ukazała się w 1996 roku i jest
artykule T.Ypma w SIAM Review 37, 1995. [[Biografia Newton|Zobacz biografię]]]]
intensywnie rozwijana do dziś.


[[Image:MNoctave-screenshot.png|thumb|550px|center|Screenshot Octave. W terminalu otwarta sesja w
Startując z pewnego przybliżenia
ascetycznym
początkowego <math>\displaystyle x_0</math>, w kolejnych krokach metody, <math>\displaystyle k</math>-te
trybie tekstowym, grafika wyświetlana z wykorzystaniem [http://www.gnuplot.info  Gnuplota]]]
przybliżenie <math>\displaystyle x_k</math> jest punktem przecięcia stycznej do
wykresu <math>\displaystyle f</math> w punkcie <math>\displaystyle x_{k-1}</math>. Ponieważ równanie
stycznej wynosi <math>\displaystyle y(x)=f(x_{k-1})+f'(x_{k-1})(x-x_{k-1})</math>,
otrzymujemy wzór


Drugim udanym klonem MATLABa jest francuski [http://www.scilab.org  Scilab],
{{algorytm|Metoda Newtona (stycznych)||
opracowany w laboratoriach INRIA i wciąż doskonalony. W
<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>for k = 1,2,...
subiektywnej ocenie autorów niniejszych notatek, nie dorównuje on elegancji i
<math>\displaystyle x_k\,=\,x_{k-1}\,-\,\frac{f(x_{k-1})}{f'(x_{k-1})}</math>;
szlachetnej prostocie Octave. Na plus tego pakietu należy zaliczyć m.in.
end
znacznie bardziej rozbudowany zestaw funkcji podstawowych, na minus --- przede
</pre></div>}}
wszystkim znacznie mniejszy stopień zgodności z MATLABem, a poza tym niewygodny
system pomocy oraz "toporną" (choć o dużym potencjale) grafikę.


[[Image:MNscilab-screenshot.png|thumb|550px|center|Screenshot Scilaba.]]
Oczywiście, aby metoda Newtona była dobrze zdefiniowana,
musimy założyć, że <math>\displaystyle f'(x_{k-1})</math> istnieje i nie
jest zerem.


Korzystając z dowolnego z omówionych powyżej pakietów, otrzymujemy:
[[Image:MNnewtononestep.png|thumb|550px|center|Metoda Newtona: pierwszy krok]]
* możliwość obliczania funkcji matematycznych (nawet dość egzotycznych), finansowych, analizy sygnałów, itp.;
* bardzo szeroki zakres nowoczesnych narzędzi umożliwiających wykonywanie podstawowych zadań numerycznych, takich jak: rozwiązywanie równań, obliczanie całek, itd.;
* efektowną wizualizację wyników w postaci wykresów dwu- i trójwymiarowych, a także funkcje do manipulacji obrazem i dźwiękiem;
* możliwość nieograniczonych rozszerzeń przy użyciu funkcji i skryptów tworzonych przez osoby trzecie (lub własnych).
Jeśli uwzględnić dodatki wielce ułatwiające życie, w tym rozbudowane funkcje
wejścia-wyjścia, to narzędzie robi się rzeczywiście intrygujące, jako środowisko
obliczeń numerycznych "dla każdego" (nie tylko dla profesjonalnego numeryka).
Stąd wielka popularność MATLABa w środowiskach inżynierskich, gdyż umożliwia
szybką implementację rozmaitych --- zwłaszcza testowych --- wersji algorytmu, np. przeprowadzania skomplikowanej symulacji. Po zweryfikowaniu wyników można ewentualnie rozpocząć implementację w docelowym środowisku (np. masywnie
równoległym komputerze, w Fortranie 95, z wykorzystaniem komercyjnych bibliotek i specyficznymi optymalizacjami kodu), choć bardzo często wyniki uzyskane w
MATLABie w zupełności wystarczą.


Zarówno MATLAB, jak Octave i Scilab, opierają się w dużym zakresie na darmowych
<div class="thumb tright"><div><flash>file=Newton.swf|width=550|height=300</flash> <div.thumbcaption>Postęp iteracji Newtona</div></div></div>
--- acz profesjonalnych --- zewnętrznych bibliotekach numerycznych (BLAS, LAPACK, FFTW, itp.)


MATLAB jest wciąż niedościgniony jeśli chodzi o narzędzia programistyczne (np.
Zauważmy, że metodę Newtona można traktować jako
ma wbudowany --- działający <strong>na bieżąco</strong> w czasie edycji kodu źródłowego! --- debugger, a także kompilator funkcji JIT) oraz możliwości wizualizacji danych i wyników obliczeń. Jeśli zaniedbać koszt zakupu dodatkowych modułów, ma także najbogatszy zestaw funkcji numerycznych. W chwili pisania niniejszego tekstu, (stosunkowo tanie) wersje studenckie MATLABa, nie są oferowane w Polsce przez producenta, choć z drugiej
szczególny przypadek iteracji prostych, gdzie
strony są często dostępne (nawet w wersji profesjonalnej) w komputerowych
laboratoriach akademickich w Polsce.


Zarówno w MATLABie, jak i w Octave, jest możliwe pisanie funkcji, które będą
<center><math>\displaystyle \phi(x)\,=\,x-\frac{f(x)}{f'(x)}.  
prekompilowane, dając tym samym znaczące przyspieszenie działania. Najnowsze wersje MATLABa (od wersji 6.5) wykorzystują zresztą domyślnie kompilację JIT --- w trakcie pisania kodu źródłowego funkcji.
</math></center>


Wybór pomiędzy darmowymi klonami MATLABa: Octave i Scilabem, jest właściwie kwestią osobistych preferencji. W ninijeszym kursie będziemy posługiwać się
Widać też, że nie jest ona zbieżna globalnie.
Octave, który jest bardzo wiernym klonem MATLABa, przez co daje zaskakująco wysoki stopień kompatybilności z programami napisanymi w MATLABie. To jest bardzo ważne w praktyce, ponieważ jest dostępnych wiele (nawet darmowych) pakietów numerycznych opracowanych właśnie w języku poleceń MATLABa. Umiejąc posługiwać się Octave, bez trudu można "przesiąść się" na MATLABa i korzystać z jego bogatszych możliwości, droga w przeciwną stronę też jest nietrudna.
Inną zaletą Octave jest swobodniejsza niż w MATLABie składnia.


Octave jest dołączany do większości popularnych dystrybucji Linuxa, najczęściej
Metoda Newtona i jej podobne należą do  
jednak użytkownik musi samodzielnie doinstalować go z płytki instalacyjnej.
grupy metod <strong>zbieżnych lokalnie</strong>. Znaczy to, że
Ponadto, kody źródłowe najświeższej wersji Octave są dostępne na stronie
zbieżność ciągu <math>\displaystyle \{x_k\}_k</math> do zera danej funkcji <math>\displaystyle f</math>
http://www.octave.org.  Dodatkowo, pod http://octave.sourceforge.net znajdziemy pakiet rozszerzeń do Octave, pod nazwą Octave-forge. Wymaga on od użytkowników Linuxa samodzielnej (przebiegającej bezproblemowo) instalacji. Octave można także zainstalować pod Windows, korzystając z programu instalacyjnego dostępnego pod adresem internetowym http://octave.sourceforge.net: w Windowsowej wersji Octave, pakiet Octave-forge jest standardowo dołączony.  
jest zapewniona jedynie wtedy, gdy przybliżenia początkowe
zostały wybrane dostatecznie blisko <math>\displaystyle x^*</math>.  


====Pierwsze kroki w Octave====
Nawet jeśli pochodna w <math>\displaystyle x_{k-1}</math> się nie zeruje,
ciąg <math>\displaystyle \{x_k\}_k</math> może nie zbiegać do zera funkcji <math>\displaystyle f</math>.
Okazuje się jednak, że jeśli
wystartujemy dostatecznie blisko rozwiązania <math>\displaystyle x^*</math>, to
metoda Newtona jest zbieżna. Dokładniej, załóżmy
najpierw, że <math>\displaystyle f(x^*)=0</math> oraz


Octave uruchamiamy poleceniem <code style="color: #666">octave</code>,
<center><math>\displaystyle f'(x^*)\,\ne\,0.
</math></center>


<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>[przykry@bit MN] octave
Ponadto załóżmy, że <math>\displaystyle f</math> jest dwukrotnie
GNU Octave, version 2.9.5 (i686-redhat-linux-gnu).
różniczkowalna w sposób ciągły, <math>\displaystyle f\in C^2(D)</math>.  
Copyright (C) 2006 John W. Eaton.
Rozwijając <math>\displaystyle \phi</math> w szereg Taylora w punkcie <math>\displaystyle x^*</math>,
This is free software; see the source code for copying conditions.
otrzymujemy
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTIBILITY or
FITNESS FOR A PARTICULAR PURPOSE.  For details, type `warranty'.


Additional information about Octave is available at http://www.octave.org.
<center><math>\displaystyle x_k-x^*\,=\,\phi(x_{k-1})-\phi(x^*)\,=\,
  (x_{k-1}-x^*)\phi'(x^*)+(x_{k-1}-x^*)^2\phi''(\xi_k)/2,
</math></center>


Please contribute if you find this software useful.
gdzie <math>\displaystyle \min(x^*,x_{k-1})\le\xi_k\le\max(x^*,x_{k-1})</math>.  
For more information, visit http://www.octave.org/help-wanted.html
Wobec tego, że <math>\displaystyle \phi'(x^*)=f(x)f''(x)/(f'(x))^2=0</math> i
<math>\displaystyle \phi''(\xi_k)=f''(\xi_k)/f'(\xi_k)</math>, mamy


Report bugs to <bug@octave.org> (but first, please read
<center><math>\displaystyle
http://www.octave.org/bugs.html to learn how to write a helpful report).
  x_k-x^*\,=\,(x_{k-1}-x^*)^2\frac{f''(\xi_k)}{2f'(\xi_k)}.
</math></center>


octave:1>
Zdefiniujmy liczbę
</nowiki></div>
 
Naturalnie, Octave zachowuje wszystkie możliwości kalkulatora naukowego,
wykonując podstawowe operacje arytmetyczne i obliczając wartości wielu funkcji
matematycznych; oto zapis takiej sesji Octave:


<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:1> 1900/2000
<center><math>\displaystyle R_f\,=\,\sup_{r\ge 0}\sup_{\{x:|x-x^*|\le r\}}
ans = 0.95000
  \Big|\frac{2(x-x^*)f''(x)}{f'(x)}\Big|\,<\,1.  
octave:2> sin(pi)
</math></center>
ans =  1.2246e-16
octave:3> e^(i*pi)
ans = -1.0000e+00 + 1.2246e-16i
</nowiki></div>
Ostatnie dwa wyniki dają nam namacalny dowód, że obliczenia wykonywane są
<strong>numerycznie</strong>, ze skończoną precyzją: w Octave niektóre tożsamości matematyczne
są spełnione <strong>jedynie w przybliżeniu</strong>, np. <math>\displaystyle \sin(\pi) \approx 0</math> oraz
<math>\displaystyle e^{i\pi} \approx -1</math>. Przy okazji widzimy, że Octave dysponuje podstawowymi
stałymi matematycznymi (oczywiście, są to także wartości przybliżone!):
<code style="color: #006">e <math>\displaystyle \approx</math> 2.71828182845905</code>, <code style="color: #006">pi <math>\displaystyle \approx</math> 3.14159265358979</code>
oraz jednostką urojoną <code style="color: #006">i</code> <math>\displaystyle = \sqrt{-1}</math>.


Octave ma dobrą dokumentację, dostępną w trakcie sesji
Oczywiście <math>\displaystyle R_f>0</math>. Dla <math>\displaystyle x_{k-1}</math> spełniającego  
Octave; dla każdej funkcji, stałej itpmożna uzyskać jej dobry opis przy
<math>\displaystyle |x_{k-1}-x^*|\le R<R_f</math>, mamy z poprzedniej równości
użyciu polecenia <code style="color: #006">help</code>.


Podstawowym obiektem, z jakim mamy do czynienia w Octave, jest macierz
<center><math>\displaystyle |x_k-x^*|\,\le\,q\,|x_{k-1}-x^*|,
dwuwymiarowa <math>\displaystyle n\times m</math>, o elementach rzeczywistych lub zespolonych,
<center><math>\displaystyle  
\begin{pmatrix}
a_{11} & \cdots & a_{1m}\\
\vdots &        & \vdots\\
a_{n1} & \cdots & a_{nm}
\end{pmatrix} .
</math></center>
</math></center>


W szczególności, biorąc <math>\displaystyle m=1</math> albo <math>\displaystyle n=1</math>, dostajemy wektor (kolumnowy lub
gdzie <math>\displaystyle q<1</math> i <math>\displaystyle q</math> zależy tylko od <math>\displaystyle R</math>.
wierszowy
 
<center><math>\displaystyle  
Niech teraz <math>\displaystyle x^*</math> będzie zerem <math>\displaystyle m</math>-krotnym,
\begin{pmatrix}
 
a_{1} \\
<center><math>\displaystyle f(x^*)=f'(x^*)=\cdots =f^{(m-1)}(x^*)=0\ne f^{(m)}(x^*),
\vdots\\
a_{n}
\end{pmatrix} , \qquad\text{lub}\qquad
\begin{pmatrix}
a_{1} & \cdots & a_{m}
\end{pmatrix} .
</math></center>
</math></center>


Z kolei dla <math>\displaystyle m=n=1</math> mamy do czynienia ze "zwykłymi" liczbami rzeczywistymi lub
gdzie <math>\displaystyle m\ge 2</math>, oraz niech <math>\displaystyle f</math> będzie <math>\displaystyle m</math>-krotnie
zespolonymi.
różniczkowalna w sposób ciągły. Wtedy


Octave umożliwia, za swoim pierwowzorem --- MATLABem, bardzo wygodną
<center><math>\displaystyle \aligned x_k-x^* &= (x_{k-1}-x^*)\,-\,\frac{(x_{k-1}-x^*)^m
pracę z macierzami.
  \frac{f^{(m)}  (\eta_k^{(1)})}{m!}}{(x_{k-1}-x^*)^{m-1}
  \frac{f^{(m-1)}(\eta_k^{(2)})}{(m-1)!}} \nonumber \\
  &= (x_{k-1}-x^*)\left(1-\frac 1m\frac
      {f^{(m)}(\eta_k^{(1)})}{f^{(m)}(\eta_k^{(2)})}
      \right) \nonumber \\
  &\approx & (x_{k-1}-x^*)\Big( 1-\frac 1m\Big),
\endaligned</math></center>


Wprowadzanie macierzy do Octave jest bardzo intuicyjne i możemy zrobić to na
o ile <math>\displaystyle x_{k-1}</math> jest "blisko" <math>\displaystyle x^*</math>.  
kilka sposobów, w zależności od potrzeb. Można robić to interakcyjnie, podając
elementy macierzy z linii komend, na przykład:


<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:6> A = [2 -1 0; -1 3 -2; 2 2.71828 3.14] 
Metoda Newtona jest więc zbieżna lokalnie. Gdy <math>\displaystyle x_0</math> jest zbyt daleko od rozwiązania może zdarzyć się, że iteracja Newtona zacznie nas oddalać od miejsca zerowego, co ilustruje poniższy przykład:
A =


  2.00000  -1.00000  0.00000
[[Image:MNnewtononestepdiv.png|thumb|550px|center|Metoda Newtona: jeśli startujemy zbyt daleko od miejsca zerowego <math>\displaystyle f</math>, zamiast przybliżać się do niego, zaczynamy się oddalać! (gdzie będzie <math>\displaystyle x_3</math>?...)]]
  -1.00000  3.00000  -2.00000
  2.00000  2.71828  3.14000


</nowiki></div>
Z powyższego można też wywnioskować,
jaki jest charakter zbieżności metody Newtona. Dla zera
definiuje macierz kwadratową <math>\displaystyle 3\times 3</math> o elementach równych
jednokrotnego <math>\displaystyle x^*</math> oraz <math>\displaystyle f''(x^*)\ne 0</math> mamy bowiem
<center><math>\displaystyle  
 
\begin{pmatrix}
<center><math>\displaystyle |x_k-x^*|\, \approx \,|x-x_{k-1}|^2 \frac{|f''(x^*)|}{2|f'(x^*)|}.
2 & -1 & 0\\ -1 & 3 & -2\\ 2 & 2.71828 & 3.14
\end{pmatrix} .
</math></center>
</math></center>


Tak
Mówimy, że zbieżność metody Newtona, gdy <math>\displaystyle f(x^*)\neq 0</math> jest <strong>kwadratowa</strong>.  
więc, macierz w Octave definiujemy przez podanie jej elementów. Elementy
wprowadzamy kolejno wierszami, elementy w wierszu oddzielamy spacjami (lub
ewentualnie przecinkami), natomiast kolejne wiersze oddzielamy średnikami.


Macierz możemy także tworzyć stopniowo, wprowadzając jeden za drugim jej
{{stwierdzenie|||
kolejne elementy:
Jeśli <math>\displaystyle f(x^*)\neq 0</math> oraz
<math>\displaystyle f''(x^*)=0</math> to zbieżność jest nawet szybsza. Z kolei dla
zera <math>\displaystyle m</math>-krotnego (tzn. <math>\displaystyle f(x^*) = f'(x^*)= \ldots f^{(m)}(x^*)= 0</math>, <math>\displaystyle m>1</math>)
zbieżność jest liniowa z ilorazem <math>\displaystyle (1-\frac{1}{m})</math>.
}}


<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:3> B(1,1) = 4
Metoda Newtona jest pierwszą poznaną tutaj metodą
B = 4
iteracyjną, która jest (dla zer jednokrotnych) zbieżna
szybciej niż liniowo. Dla takich metod wprowadza się
pojęcie <strong>wykładnika zbieżności</strong>, który jest
zdefiniowany następująco.


octave:4> B(2,1) = 3 + B(1,1)
[[Image:MNstycznebisekcja.png|thumb|550px|center|Porównanie zbieżności metody bisekcji i stycznych
B =
dla równania <math>\displaystyle e^x - 1 = 0</math>. Błąd kolejnych przybliżeń wyświetlany jest w skali
logarytmicznej, dzięki czemu lepiej widać różnicę między zbieżnością liniową a
kwadratową.]]


  4
Powiemy, że metoda iteracyjna <math>\displaystyle \phi</math> jest w klasie funkcji <math>\displaystyle F</math>
  7
<strong>rzędu co najmniej <math>\displaystyle p\ge 1</math></strong>, gdy spełniony jest następujący
warunek. Niech <math>\displaystyle f\in F</math> i <math>\displaystyle f(x^*)=0</math>. Wtedy istnieje stała
<math>\displaystyle C<\infty</math> taka, że dla dowolnych przybliżeń początkowych 
<math>\displaystyle x_0,\ldots,x_{s-1}</math> dostatecznie bliskich <math>\displaystyle x^*</math>, kolejne
przybliżenia <math>\displaystyle x_k=\phi(x_{k-1},\ldots,x_{k-s})</math> generowane
tą metodą spełniają


octave:5> B(3,2) = 28
<center><math>\displaystyle |x_k-x^*|\,\le\,C\,|x_{k-1}-x^*|^p.
B =
</math></center>


  4  0
Ponadto, jeśli <math>\displaystyle p=1</math> to dodatkowo żąda się, aby <math>\displaystyle C<1</math>.
  7  0
 
  0  28
{{definicja|||
</nowiki></div>
<strong>Wykładnikiem zbieżności</strong> metody
iteracyjnej <math>\displaystyle \phi</math> w klasie <math>\displaystyle F</math> nazywamy liczbę <math>\displaystyle p^*</math>  
Pierwsza komenda określa <code style="color: #006">B</code> jako macierz <math>\displaystyle 1\times 1</math>, czyli zwykłą
zdefiniowaną równością
liczbę, druga -- rozszerza nasz obiekt do wektora, natomiast trzecia --
 
powoduje, że <code style="color: #006">B</code> zostaje rozszerzony do macierzy <math>\displaystyle 3\times 2</math>. Zauważmy, że
<center><math>\displaystyle p^*\,=\,\sup\,\{\,p\ge 1:\,\phi
elementom <code style="color: #006">B</code> o nieokreślonych przez nas wartościach zostaje przypisana domyślna
    \mbox{ jest rzędu co najmniej  }  p\,\}.
wartość zero.
</math></center>
 
}}


<nowiki>
Możemy teraz sformułować następujące twierdzenie,  
W przypadku, gdy wypełniana macierz jest
które natychmiast wynika z poprzednich rozważań.
duża, znacznie korzystniej jest prealokować macierz, ustawiając na samym
początku wszystkie elementy macierzy <strong>docelowego wymiaru</strong> (u nas to była
macierz <math>\displaystyle 3\times 2</math>) na zero; potem możemy już działać jak poprzednio:
</nowiki>


<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>B = zeros(3,2)
{{twierdzenie|O rzędzie zbieżności metody Newtona||
B(1,1) = 3 + B(1,1)
B(2,1) = pi
B(3,2) = 28
</pre></div>
   
   
Jeśli nie chcemy za każdym razem obserwować echa prowadzonych przez nas działań
Wykładnik zbieżności metody Newtona
na macierzy <code style="color: #006">B</code>, komendy powinniśmy kończyć średnikiem: średnik po
(stycznych) wynosi <math>\displaystyle p^*=2</math> w klasie funkcji o zerach
komendzie Octave blokuje echo wyniku jej wykonania. Tak więc, znacznie
jednokrotnych, oraz <math>\displaystyle p^*=1</math> w klasie funkcji o zerach
wygodniej będzie nam napisać
wielokrotnych.
}}


<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>B = zeros(3,2);
[[Image:MNmultiplezeros.png|thumb|550px|center|Zbieżność metody Newtona dla zer wielokrotnych <math>\displaystyle f(x)
B(1,1) = 3 + B(1,1);
= (x-1)^5</math> jest liniowa z ilorazem <math>\displaystyle \frac{4}{5}</math> (końcowe załamanie wykresu
B(2,1) = pi;
spowodowane jest przypadkowym trafieniem w dokładne miejsce zerowe). Metoda bisekcji nie jest na to czuła i dalej zbiega z ilorazem
B(3,2) = 28;
<math>\displaystyle \frac{1}{2}</math>.]]
B
</pre></div>
Ostatnie polecenie -- <code style="color: #006">B</code> <strong>bez</strong> średnika na końcu -- spowoduje
wypisanie zawartości <code style="color: #006">B</code> na ekran.  


Tak utworzoną macierz możemy zapisać do pliku, wykorzystując jeden z kilku
==Metoda siecznych==
formatów:
* tekstowy
* binarny
* HDF5
Zapis w formacie tekstowym to po prostu zapis do formatu ASCII. Ze względu na późniejszą [[WDP_Reprezentacja_liczb|konwersję z zapisu w
postaci dziesiętnej do dwójkowej]], wiąże się z drobnymi
niedokładnościami w odczycie.
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>save macierzb.dat B
</pre></div>
W wyniku dostajemy
<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># Created by Octave 2.9.5, Mon Jul 31 17:15:25 2006 CEST <przykry@bit>
# name: B
# type: matrix
# rows: 3
# columns: 2
3 0
3.14159265358979 0
0 28
</nowiki></div>
Zapis binarny to prostu zrzut stanu pamięci
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>save -binary  macierzb.dat B
</pre></div>
Format [http://hdf.ncsa.uiuc.edu/HDF5  HDF5] to format gwarantujący pełną przenośność danych pomiędzy różnymi
architekturami, akceptowany przez wiele zewnętrznych aplikacji, m.in.
[http://www.opendx.org  OpenDX].


<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>save -hdf5  macierzb.dat B
Inną znaną i często używaną metodą iteracyjną, opartą na podobnym pomyśle
</pre></div>  
linearyzacyjnym co metoda Newtona,
jest <strong>metoda siecznych</strong>, w której zamiast przybliżenia wykresu <math>\displaystyle f</math> przez
styczną,  stosuje się  przybliżenie sieczną.
   
   
Dane z pliku wczytujemy z powrotem poleceniem
Metoda ta
wykorzystuje więc do konstrukcji <math>\displaystyle x_k</math> przybliżenia
<math>\displaystyle x_{k-1}</math> i <math>\displaystyle x_{k-2}</math>. Musimy również wybrać dwa różne
punkty startowe <math>\displaystyle x_0</math> i <math>\displaystyle x_1</math>. Ponieważ sieczna dla
<math>\displaystyle f</math> w punktach <math>\displaystyle x_{k-1}</math> i <math>\displaystyle x_{k-2}</math> ma wzór


<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>load macierzb.dat
<center><math>\displaystyle y(x)\,=\,\frac{x-x_{k-2}}{x_{k-1}-x_{k-2}}f(x_{k-1})+
</pre></div>
          \frac{x-x_{k-1}}{x_{k-2}-x_{k-1}}f(x_{k-2}),
</math></center>
Sesję Octave kończymy poleceniem <code style="color: #006">quit</code>.


====Notacja dwukropkowa Molera====
otrzymujemy


Do istniejącej macierzy możemy odwoływać się tradycyjnie, czyli do pojedynczych
{{algorytm|Metoda siecznych||
elementów, np. <code style="color: #006">alfa = D(2,2)</code>, lub do jej większych bloków, używając popularnej tzw. <strong>notacji dwukropkowej</strong> Molera.
<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>for k = 1,2,...
<math>\displaystyle x_k\,=\,x_{k-1}\,-\,\frac{x_{k-1}-x_{k-2}} {f(x_{k-1})-f(x_{k-2})}\,f(x_{k-1})</math>;
end
</pre></div>}}


[[Image:MNcolon-matrix-1-color.png|thumb|300px|Wybór bloku w macierzy <code style="color: #006">D</code> przy użyciu notacji dwukropkowej]]
Zauważmy, że jeśli <math>\displaystyle x_{k-1}</math> i <math>\displaystyle x_{k-2}</math> są blisko
siebie, to <math>\displaystyle x_k</math> jest podobny do tego z metody Newtona,
bowiem wtedy iloraz różnicowy [[MN14|przybliża pochodną]] <math>\displaystyle f</math>,
<center><math>\displaystyle
\frac{f(x_{k-1})-f(x_{k-2})}{x_{k-1}-x_{k-2}} \approx f'(x_{k-1}).
</math></center>
Nie wystarcza to jednak, aby osiągnąć zbieżność z wykładnikiem
<math>\displaystyle 2</math>. Można pokazać, że przy podobnych założeniach o funkcji, wykładnik zbieżności metody siecznych dla zer jednokrotnych i dostatecznie gładkich funkcji wynosi <math>\displaystyle p^*=\frac{1+\sqrt{5}}{2}=1.618\ldots</math>. Jako wariant metody Newtona, metoda siecznych jest również zbieżna lokalnie.


Jest ona
[[Image:MNstycznesiecznebisekcja.png|thumb|550px|center|Porównanie zbieżności metody bisekcji,
szalenie intuicyjna, a mianowicie: pisząc na przykład <code style="color: #006">v = D(2:5,2)</code>
stycznych i siecznych
definiujemy (wektor) <math>\displaystyle v</math>, który zawiera wiersze macierzy <math>\displaystyle D</math> od 2 do 5 wybrane z
dla równania <math>\displaystyle e^x - 1 = 0</math>. Błąd kolejnych przybliżeń wyświetlany jest w skali
drugiej kolumny. Podobnie, pisząc <code style="color: #006">W = D(2:3,5:7)</code> definiujemy macierz
logarytmicznej.]]
<math>\displaystyle W</math> wymiaru
<math>\displaystyle 2\times 3</math>, o elementach, które zostały wybrane z przecięcia wierszy od 2 do 3 z
kolumnami od 5 do 7 macierzy <math>\displaystyle D</math>.


[[Image:MNcolon-matrix-2-color.png|thumb|300px|Wybór zestawu kolumn w macierzy <code style="color: #006">D</code>]]
Niewątpliwą zaletą metody siecznych jest jednak to, że <strong>nie wymaga obliczania pochodnej funkcji</strong> (bywa, że dokładne wyznaczenie pochodnej jest niemożliwe, gdy np. funkcja jest zadana zewnętrzną procedurą, do której kodu źródłowego nie mamy dostępu; zwykle też koszt obliczenia wartości pochodnej jest wyższy od kosztu obliczenia wartości funkcji). Jest to również istotne w pakietach numerycznych, gdzie czasem nie chcemy wymagać od użytkownika czegokolwiek, oprócz podania wzoru na funkcję i przybliżonej lokalizacji miejsca zerowego.


Dodatkowo, aby odwołać się do całego 4. wiersza (odpowiednio: 5. kolumny)
Ponadto, często zdarza się, że wyznaczenie wartości pochodnej, <math>\displaystyle f'(x_k)</math>, jest
macierzy <math>\displaystyle D</math>, można użyć skrótowej notacji <code style="color: #006">D(4,:)</code> (odpowiednio:
tak samo, albo i bardziej kosztowne od wyznaczenia wartości <math>\displaystyle f(x_k)</math>. W takim
<code style="color: #006">D(:,5)</code>).  
wypadku okazuje się, że metoda stycznych --- choć wolniej zbieżna niż metoda
stycznych --- dzięki temu, że jej iteracja wymaga jedynie wyznaczenia jednej wartości <math>\displaystyle f</math>, jest <strong>bardziej efektywna</strong> od metody Newtona: koszt osiągnięcia zadanej dokładności jest w takim przypadku mniejszy od analogicznego kosztu dla metody Newtona.


Odwołanie się do całej macierzy jest także możliwe (przez użycie jej nazwy, np.
Jednak gdy żądane przez użytkownika dokładności są bardzo wielkie, a sama
<code style="color: #006">A = 2*D</code> lub <code style="color: #006">G = log(abs(D))</code>).
funkcja "złośliwa", metoda siecznych może cierpieć z powodu redukcji cyfr
przy odejmowaniu.


Notację dwukropkową można także wykorzystać do wygenerowania samodzielnych zbiorów indeksów:
==Metoda Brenta==


<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:1> N = 10;
Naturalnie, uważny student zaczyna zadawać sobie pytanie, czy nie można w jakiś
octave:2> idx = 1:N
sposób połączyć globalnej zbieżności metody bisekcji z szybką zbieżnością
idx =
metody siecznych tak, by uzyskać metodę zbieżną globalnie, a jednocześnie
  1  2  3  4  5  6  7  8  9  10
istotnie szybciej niż liniowo.


octave:3> idx2 = 1:2:N
Okazuje się, że można to zrobić, wprowadzając metodę opartą na <strong>trzech</strong> punktach lokalizujących miejsce zerowe: dwóch odcinających zero tak jak
idx2 =
w metodzie bisekcji i trzecim, konstruowanym np. jak w metodzie stycznych. W
  1  3  5  7  9
kolejnej iteracji wymieniamy jeden z punktów albo wedle metody
siecznych (i wtedy zapewne szybciej zbliżamy się do zera), albo wykonując bisekcję (aby zagwarantować sobie, że w wiadomym przedziale miejsce zerowe rzeczywiście się znajduje).


octave:4> nidx = N:-1:1
Ten prosty pomysł <strong>metody hybrydowej</strong> wymaga jednak subtelnego dopracowania. Zostało to zrobione w 1973 roku przez [http://www.rpbrent.com  Richarda Brenta]. Funkcja MATLABa (i Octave'a) <code style="color: #006">fzero</code> implementują właśnie metodę Brenta.
nidx =
Autorem implementacji w Octave jest ówczesny student [http://www.mimuw.edu.pl matematyki] na Uniwersytecie Warszawskim, Łukasz Bodzon. Fortranowski kod metody Brenta można znaleźć także w [http://www.netlib.org/go/zeroin.f  Netlibie]. Inną funkcją Octave'a służącą rozwiązywaniu równań nieliniowych jest <code style="color: #006">fsolve</code>:
  10  9  8  7  6  5  4  3  2  1
</nowiki></div>
   
i dlatego pętle, które w C zapisywalibyśmy


  <div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>for(i=0; i<=N; i++)
  <div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>octave:1> [X, MSG, INFO] = fsolve ('cos', 1)
{
X = 1.5708
...instrukcje...
MSG = 1
}
INFO = solution converged within specified tolerance
for(i=N; i>=1; i--)
{
...instrukcje...
}
</pre></div>
   
w Octave zapiszemy


<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>for i=0:N
octave:2> cos(X)
...instrukcje...
ans = 6.1230e-17
end
for i=N:-1:1
...instrukcje...
end
</pre></div>  
</pre></div>  
   
   
Za chwilę przekonamy się, jak można w ogóle pozbyć się potrzeby stosowania większości pętli w kodach Octave i MATLABa.
==Metody dla układów równań nieliniowych==


====Wektoryzacja w Octave====
Niektóre z poznanych metod można łatwo rozszerzyć na przypadek układu <math>\displaystyle N</math> równań z <math>\displaystyle N</math> niewiadomymi, to znaczy


Ponieważ podstawowymi obiektami w Octave są wektory i macierze, predefiniowane
<center><math>\displaystyle F(x) = 0,
operacje matematyczne wykonują się od razu na całej macierzy. Bez żadnej
</math></center>
przesady możena stwierdzić, że umiejętność  <strong>wektoryzacji</strong> i
<strong>blokowania</strong> algorytmów jest podstawą pisania efektywnych implementacji
algorytmów w Octave.


Zobaczmy kilka prostych przykładów zawartych w tabeli poniżej. W
gdzie <math>\displaystyle F: R^N \rightarrow R^N</math>.
pierwszej kolumnie tabeli, dane zadanie zaimplementujemy w Octave przy użyciu
pętli (zwróćmy przy okazji uwagę na to, jak programuje się pętle w Octave, one
nie są zupełnie bezużyteczne...). W drugiej kolumnie zobaczymy, jak to samo
zadanie można wykonać korzystając z operatorów lub funkcji macierzowych.


{| border=1
====Metoda Banacha====
|+ <span style="font-variant:small-caps"> </span>
|-
|
  ||  Tradycyjny kod w Octave, używający pętli  ||  Efektywny kod wektorowy (macierzowy) w Octave
|-
|


Alg 1  ||
Jak pamiętamy, [[#Banacha, o kontrakcji|metodę Banacha]] sformułowaliśmy od razu dla zagadnienia wielowymiarowego. Analiza i własności metody są zatem już [[#Banacha, o kontrakcji|omówione]].
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>s = 0;
for i = 1:size(x,1)
s = s + abs(x(i));
end
</pre></div>
   
   
||
====Wielowymiarowa metoda Newtona====
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>s = sum(abs(x));
 
</pre></div>  
Okazuje się, że metodę Newtona można uogólnić na przypadek układu <math>\displaystyle N</math> równań nieliniowych z <math>\displaystyle N</math> niewiadomymi. Zapiszmy wzór na skalarną metodę Newtona odrobinę inaczej:
 
|-
<center><math>\displaystyle x_{k+1} = x_k - [F'(x_k)]^{-1}\, F(x_k).
|
</math></center>
 
Niezwykłe jest, że taki wzór nie tylko ma sens w przypadku, gdy <math>\displaystyle F: R^N \rightarrow R^N</math> (wtedy <math>\displaystyle F'(x_k)</math> jest macierzą Jakobianu <math>\displaystyle F</math> w punkcie <math>\displaystyle x_k</math>), ale dodatkowo ta metoda zachowuje wszystkie własności metody stycznych dla przypadku skalarnego:


Alg 2  ||  
{{twierdzenie|O zbieżności wielowymiarowej metody Newtona||
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>N = 500; h = (b-a)/(N-1);
for i = 1:N
x(i) = a + (i-1)*h;
y(i) = sin(x(i));
end
plot(x,y);
</pre></div>
||
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>N = 500;
x = linspace(a,b,N);
y = sin(x);
plot(x,y);
</pre></div>
|-
|
Alg 3a  ||
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>for i = 1:size(C,1),
for j = 1:size(C,2),
for k = 1:size(A,2),
C(i,j) = C(i,j) + A(i,k)*B(k,j);
end
end
end
</pre></div>
||
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>C = C + A*B;
</pre></div>
|-
|  


Alg 3b  ||
Załóżmy, że <math>\displaystyle F: R^N \rightarrow R^N</math> i istnieje <math>\displaystyle x^* \in R^N</math> taki, że
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>for i = 1:size(C,1),
for j = 1:size(C,2),
C(i,j) = C(i,j) + A(i,:)*B(:,j);
end
end
</pre></div>
||
<div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>C = C + A*B;
</pre></div>
|-
|


|}
<center><math>\displaystyle F(x^*) = 0.
</math></center>


Zwróćmy uwagę na to, że kod wektorowy Octave oraz, w jeszcze większym stopniu,  
Załóżmy ponadto, że <math>\displaystyle F</math> jest różniczkowalna, a jej pochodna <math>\displaystyle F': R^N \rightarrow R^{N\times N}</math> jest lipschitzowska i dodatkowo
kod macierzowy jest znacznie bardziej elegancki i czytelniejszy od
tradycyjnego. Widać to dobrze na wszystkich powyższych przykładach.


Pierwszy przykład pokazuje też, że kod macierzowy jest elastyczniejszy: albowiem
<center><math>\displaystyle F'(x^*)  \mbox{ jest nieosobliwa} .
obliczy sumę modułów wszystkich elementów <code style="color: #006">x</code> nie tylko gdy <code style="color: #006">x</code>
</math></center>
jest wektorem (co musieliśmy milcząco założyć w kodzie w lewej kolumnie), ale
tak samo dobrze zadziała, gdy <code style="color: #006">x</code> jest macierzą!


Szczególnie wiele na czytelności i wygodzie zyskujemy w drugim przykładzie,
Wówczas, jeśli tylko <math>\displaystyle x_0</math> jest dostatecznie blisko rozwiązania <math>\displaystyle x^*</math>, to ciąg kolejnych przybliżeń <math>\displaystyle x_k</math>, generowany wielowymiarową metodą Newtona, jest zbieżny do <math>\displaystyle x^*</math>. Co więcej, szybkość zbieżności jest kwadratowa.
gdzie najpierw funkcja <code style="color: #006">linspace</code> pozwala nam uniknąć żmudnego
}}
wyznaczania <math>\displaystyle N</math> równoodległych węzłów <math>\displaystyle x_i</math> w odcinku <math>\displaystyle [a,b]</math>, a następnie
funkcja <code style="color: #006">sin</code> zaaplikowana do całego wektora <math>\displaystyle x</math> daje wartość sinusa w
zadanych przez nas węzłach.


Kod wektorowy lub (zwłaszcza) macierzowy jest też znacznie szybszy. Spójrzmy
====Implementacja wielowymiarowej metody Newtona====
teraz na przykłady: (3a) i (3b), które pokażą nam prawdziwą moc funkcji
macierzowych, unikających wielokrotnie zagnieżdżonych pętli. Oba dotyczą
operacji mnożenia dwóch macierzy. Przykład (3a) w wersji z potrójną pętlą
<code style="color: #006">for</code> naśladuje sposób programowania znany nam z C lub Pascala,
natomiast przykład (3b) zdaje się być napisany odrobinę w duchu wektorowym
(brak trzeciej, wewnętrznej pętli, zastąpionej operacją wektorową:  iloczynem
skalarnym <math>\displaystyle i</math>-tego wiersza <math>\displaystyle A</math> i <math>\displaystyle j</math>-tej kolumny <math>\displaystyle B</math>). Poniżej porównanie
czasów działania tych trzech implementacji w przypadku macierzy <math>\displaystyle 64\times 64</math>
(czasy dla PC z procesorem Celeron 1GHz):
* Dla pętli postaci <code style="color: #006">C(i,j)=C(i,j)+A(i,k)*B(k,j)</code> uzyskano czas 21.6s,
* Dla pętli postaci  <code style="color: #006">C(i,j)=C(i,j)+A(i,:)*B(:,j)</code> --- 0.371s
* Dla pętli postaci <code style="color: #006">C=C+A*B</code> kod działał jedynie 0.00288s!
Widzimy, jak beznadziejnie wolny jest kod oparty na trzech zagnieżdżonych
pętlach: jest on kilka tysięcy razy wolniejszy od implementacji macierzowej
<code style="color: #006">C = C + A*B</code>. Po wektoryzacji wewnętrznej pętli, program doznaje
kilkudziesięciokrotnego przyspieszenia, lecz nadal jest ponadstukrotnie
wolniejszy od kodu macierzowego!


=Literatura=
Implementując wielowymiarową metodę Newtona, musimy dysponować nie tylko funkcją obliczającą <math>\displaystyle N</math> współrzędnych wektora wartości <math>\displaystyle F</math>, ale także funkcją wyznaczającą <math>\displaystyle N^2</math> elementów macierzy pochodnej <math>\displaystyle F</math> w zadanym punkcie <math>\displaystyle x \in R^N</math>. Zwróćmy uwagę na to, że w implementacji metody nie trzeba wyznaczać <math>\displaystyle F'(x_k)^{-1}</math>, tylko rozwiązać układ równań:


Materiały zgromadzone w kolejnych wykładach są przewodnikiem po omawianych w nich zagadnieniach. Prawdziwe studia wymagają wszelako <strong>studiowania</strong> --- także dobrej literatury fachowej! Dlatego pod koniec każdego wykładu umieszczamy informację o tym, gdzie szukać ''pogłębionych'' informacji na temat wykładanego materiału.
{{algorytm|Wielowymiarowa metoda Newtona||
<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>while (!stop)
{
rozwiąż (względem <math>\displaystyle s</math>) układ równań liniowych <math>\displaystyle F'(x_k)\, s = -F(x_k)</math>;
<math>\displaystyle x_{k+1}</math> = <math>\displaystyle x_k</math> + <math>\displaystyle s</math>;
}
</pre></div>}}


Naszym podstawowym źródłem będzie nowoczesny podręcznik
O tym, [[MN05|jak skutecznie rozwiązywać układy równań liniowych]], dowiesz się z kolejnych wykładów. Dowiesz się także, dlaczego ''nie należy'' w implementacji korzystać z wyznaczonej ''explicite'' macierzy odwrotnej do macierzy Jakobianu.
* <span style="font-variant:small-caps">D. Kincaid, W. Cheney</span>, <cite>Analiza numeryczna</cite>, Wydawnictwa Naukowo-Techniczne, Warszawa, 2006.


Oprócz niego, w języku polskim było wydanych kilka innych podręczników i monografii (niestety, większość z nich dość dawno temu)
==Literatura==
*<span style="font-variant:small-caps">M. Dryja, J. i M. Jankowscy</span>, <cite>Przegląd metod i algorytmów numerycznych</cite>, Wydawnictwa Naukowo-Techniczne, Warszawa, 1988,
*<span style="font-variant:small-caps">A. Bjorck, G. Dahlquist</span>, <cite>Metody numeryczne</cite>, PWN, Warszawa, XXXX,
*<span style="font-variant:small-caps">Stoer, Bulirsch</span>, <cite>Wstęp do analizy numerycznej</cite>, PWN, Warszawa, XXXX,
*<span style="font-variant:small-caps">A.Kiełbasiński, H. Schwetlick</span>, <cite>Numeryczna algebra liniowa</cite>, Wydawnictwa Naukowo-Techniczne, Warszawa, 1992.
*<span style="font-variant:small-caps">A. Ralston</span>, <cite>XXXX</cite>, PWN, Warszawa, XXXX,
*<span style="font-variant:small-caps">Fortuna, Macukow, </span>, <cite></cite>,


Do tego warto wspomnieć o kilku wartościowych podręcznikach i monografiach w języku angielskim
W celu dogłębnego zapoznania się z omawianym na wykładzie materiałem, przeczytaj dodatkowo <b>rozdzia� 3</b> w
*<span style="font-variant:small-caps">A. Quarteroni, Valli, Saleri</span>, <cite></cite>, ,
* D. Kincaid, W. Cheney <cite>Analiza numeryczna</cite>, Wydawnictwa Naukowo-Techniczne, Warszawa 2006, ISBN 83-204-3078-X.
*<span style="font-variant:small-caps">P. Deuflhard, A. Hohmann</span>, <cite>Numerical Analysis in Modern Scientific Computing. An Introduction</cite>, Springer, 2003, ISBN 0-387-95410-4,
Rozdziały 3.5 i 3.6 nie są obowiązkowe.
*<span style="font-variant:small-caps">K. Atkinson</span>, <cite>An Introduction to Numerical Analysis</cite>, Wiley, 1989,
*<span style="font-variant:small-caps"></span>, <cite></cite>, ,
*<span style="font-variant:small-caps"></span>, <cite></cite>, ,
*<span style="font-variant:small-caps"></span>, <cite></cite>, ,
*<span style="font-variant:small-caps"></span>, <cite></cite>, ,
*<span style="font-variant:small-caps"></span>, <cite></cite>, ,
*<span style="font-variant:small-caps"></span>, <cite></cite>, ,


Zachęcamy gorąco do zapoznania się z nimi i sztuką obliczeń numerycznych, gdyż jej prawdziwy urok, niuanse i tajemnice --- podobnie jak w przypadku każdej innej ''sztuki'' --- może docenić jedynie ''wyrobiony'' odbiorca.
Wiele wariantów metod rozwiązywania <strong>układów</strong> równań nieliniowych jest przedstawionych w znakomitej monografii
* <span style="font-variant:small-caps">C.T.Kelley</span>, <cite>Iterative Solution of Systems of Linear and Nonlinear Equations</cite>, SIAM, 1995.


Jeśli chodzi o programowanie w Octave lub w MATLABie, warto skorzystać z  [http://www.gnu.org/software/octave/doc/interpreter/  internetowego podręcznika Octave] lub krótkiego omówienia możliwości w skrypcie  [http://www.cs.berkeley.edu/&nbsp;demmel/ma221/primer4_0_1.pdf  MATLAB Primer].
Opis metody Brenta znajdziesz w książce
* <span style="font-variant:small-caps">R. Brent</span>, <cite>Algorithms for Minimization Without Derivatives</cite>, Prentice-Hall, 1973.

Wersja z 18:29, 28 wrz 2006


Równania nieliniowe

W wielu zadaniach, m.in. matematyki stosowanej, spotykamy się z problemem rozwiązania skalarnego równania nieliniowego postaci f(x)=0. Oto kilka przykładów:

Przykład

Równanie Keplera

f(x)xϵsin(x)M=0

jest bardzo ważne w astronomii, jego rozwiązanie pozwala wyznaczyć przyszłe położenie planety. Parametr ϵ odpowiada ekscentryczności orbity i przyjmuje wartości z przedziału [0,1]. Poza paru prostymi przypadkami, w ogólności równanie Keplera nie daje się rozwiązać w terminach funkcji elementarnych.

Johann Kepler
Zobacz biografię

Przykład

Znajdowanie miejsc zerowych wielomianu

f(x)anxn++a1x+a0=0.

Bardzo wiele modeli matematycznych wymaga rozwiązania równania z wielomianową nieliniowością. Piękne kwadratury (Gaussa) opierają się na węzłach będących zerami pewnego wielomianu. Wielomiany są bardzo szczególnymi funkcjami i dla nich istnieje szereg wyspecjalizowanych metod znajdowania ich pierwiastków, m.in. metoda Laguerre'a, metoda Bairstow'a (o nich tu nie będziemy mówić), a także zaskakujące metody sprowadzające zadanie poszukiwania miejsc zerowych wielomianu do zupełnie innego zadania matematycznego --- o nich jednak będzie mowa dopiero w wykładzie dotyczącym znajdowania wartości własnych macierzy.

Przykład

Obliczanie pierwiastka kwadratowego z zadanej liczby a, czyli sposób na implementację funkcji "sqrt()". Można to zadanie wyrazić, jako rozwiązywanie równania

f(x)x2a=0.

Szybkie algorytmy wyznaczania pierwiastka kwadratowego były znane już starożytnym. W wykładzie zrozumiemy, dlaczego metoda Herona,

xk+1=12(xk+axk)

daje bardzo dobre przybliżenie a już po kilku iteracjach.

Przykład

Implementacja wyznaczania odwrotności liczby a (bez dzielenia!) jest możliwa, gdy odpowiednią metodą będziemy poszukiwać rozwiązania równania

f(x)1xa=0.

To zadanie jest ważne praktycznie, np. tak można poprawić precyzję funkcji wektorowych stosowanych w niektórych procesorach AMD. Okazuje się, że instrukcja procesora służąca do równoległego obliczania odwrotności sekwencji liczb umieszczonych w 128-bitowym rejestrze wektorowym daje wynik z małą precyzją (oczywiście po to, by wykonywała się szybciej!). Jeśli taka dokładność wyniku nie odpowiada nam, możemy ją --- zgodnie z manualem procesora --- poprawić, rozwiązując właśnie takie równanie jak powyżej, metodą korzystającą wyłącznie z (wektorowych) operacji mnożenia i dodawania.

Bisekcja

Metoda bisekcji, czyli połowienia, często stosowana w innych działach informatyki, jest dość naturalną metodą obliczania zer skalarnych funkcji ciągłych określonych na danym przedziale [a,b] i zmieniających znak. Dokładniej, rozpatrzmy klasę funkcji

F={fC([a,b]):f(a)f(b)<0},

to znaczy fF przyjmują w krańcach przedziału wartości przeciwnego znaku. Oczywiście, każda funkcja fF ma, na mocy twierdzenia Darboux, co najmniej jedno zero w [a,b]. Startując z przedziału [a,b], w kolejnych krokach metody bisekcji obliczamy informację o wartości f w środku przedziału, co pozwala nam w następnym kroku zmniejszyć o połowę przedział, w którym na pewno znajduje się zero funkcji.

Bisekcję realizuje następujący ciąg poleceń, po wykonaniu którego x jest przybliżeniem zera funkcji f z zadaną dokładnością ϵ.

[Metoda bisekcji]
lewy = a; prawy = b;
flewy = f(lewy); fprawy = f(prawy);
x = (a+b)/2; 	/* przybliżenie rozwiązania */
e = (b-a)/2; 	/* przedział lokalizujący rozwiązanie dokładne */
while (e > <math>\displaystyle \epsilon</math>) 
{
	fx = f(x);   	/* reszta */
	if ( signbit(fx) != signbit(flewy) )
	{
		prawy = x;
		fprawy = fx;
	}
	else
	{
		lewy = x;
		flewy = fx;
	}
	x = (lewy+prawy)/2; e = e/2;
} 

(funkcja języka C signbit bada znak liczby rzeczywistej).

Z konstrukcji metody łatwo wynika, że po wykonaniu k obrotów pętli while (czyli po obliczeniu k+2 wartości funkcji) otrzymujemy x, które odległe jest od pewnego rozwiązania x* o co najwyżej

|xx*|(12)k(ba2).

Metoda bisekcji jest więc zbieżna liniowo z ilorazem 1/2. Choć ta zbieżność nie jest imponująca, bisekcja ma kilka istotnych zalet. Oprócz jej prostoty, należy podkreślić fakt, że bisekcja jest w pewnym sensie uniwersalna. Jeśli tylko dysponujemy dwoma punktami a i b takimi, że f przyjmuje w nich wartości przeciwnych znaków, to metoda bisekcji z pewnością znajdzie miejsce zerowe funkcji, choćby początkowa długość przedziału |ba| była bardzo duża: zbieżność metody bisekcji jest globalna. Co ważniejsze, dla zbieżności metody bisekcji wystarcza jedynie ciągłość funkcji. Poza tym możemy łatwo kontrolować błąd bezwzględny aproksymacji miejsca zerowego. Konsekwencją powyższego oszacowania błędu jest bowiem następujący wniosek.

Wniosek

Dla znalezienia zera x* z dokładnością ϵ>0, wystarczy obliczyć w metodzie bisekcji

k=k(ϵ)=log2(ba)ϵ1

wartości funkcji.

Iteracja prosta Banacha

Zupełnie inne, i jak się okaże --- przy odrobinie sprytu bardzo skuteczne --- podejście do wyznaczania miejsca zerowego jest oparte na metodzie Banacha. Dla większej ogólności, będziemy zakładać teraz, że f:DRN i D jest otwartym, niepustym podzbiorem RN.

Najpierw nasze równanie nieliniowe

f(x)=0

przekształcamy (dobierając odpowiednią funkcję ϕ) do równania równoważnego (tzn. mającego te same rozwiązania)

x=ϕ(x).

Taki x, dla którego zachodzi powyższa równość, nazywamy punktem stałym odwzorowania ϕ.

Następnie, startując z pewnego przybliżenia początkowego x0D, konstruujemy ciąg kolejnych przybliżeń xk według wzoru

xk=ϕ(xk1),k1.
Stefan Banach
Zobacz biografię

Twierdzenie Banacha, o kontrakcji

Niech D0 będzie domkniętym podzbiorem dziedziny D,

D0=D0D,

w którym ϕ jest odwzorowaniem zwężającym. To znaczy, ϕ(D0)D0, oraz istnieje stała 0L<1 taka, że

ϕ(x)ϕ(y)Lxy,x,yD0.

Wtedy równanie

x=ϕ(x).

ma dokładnie jedno rozwiązanie x*, oraz

x*=limkxk,

dla dowolnego przybliżenia początkowego x0D0.

Dowód

Wobec

Parser nie mógł rozpoznać (nieznana funkcja „\aligned”): {\displaystyle \displaystyle \aligned \| x_k- x_{k-1}\| &= \|\phi( x_{k-1})-\phi( x_{k-2})\| \,\le\,L\,\| x_{k-1}- x_{k-2}\| \\ &\le &\cdots\;\le\;L^{k-1}\| x_1- x_0\|, \endaligned}

dla ks mamy

Parser nie mógł rozpoznać (nieznana funkcja „\aligned”): {\displaystyle \displaystyle \aligned \| x_k- x_s\| &\le & \sum_{j=s+1}^k\| x_j- x_{j-1}\| \,\le\,\sum_{j=s+1}^k L^{j-1}\| x_1- x_0\| \\ &= L^s(1+L+\cdots+L^{k-s-1})\| x_1- x_0\| \,\le\,\frac{L^s}{1-L}\| x_1- x_0\|. \endaligned}

Ciąg {xk}k jest więc ciągiem Cauchy'ego. Stąd istnieje granica α=limkxk, która należy do D0, wobec domkniętości tego zbioru. Ponieważ lipschitzowskość ϕ implikuje jej ciągłość, mamy też

ϕ(α)=ϕ(limkxk)=limkϕ(xk)=limkxk=α,

tzn. α jest punktem stałym odwzorowania ϕ. Dla jednoznaczności zauważmy, że jeśliby istniał drugi, różny od α, punkt stały β, to mielibyśmy

αβ=ϕ(α)ϕ(β)Lαβ.

Stąd 1<L, co jest sprzeczne z założeniem, że

ϕ jest zwężająca.

Z powyższych rozważań otrzymujemy natychmiastowy wniosek dotyczący zbieżności iteracji prostych.

Wniosek

Przy założeniach twierdzenia Banacha, metoda iteracji prostych jest zbieżna co najmniej liniowo z ilorazem L, tzn.

xkx*Lkx0x*.

Przykład

Dla ilustracji, rozpatrzmy równanie Keplera, gdy ϵ<1:

x=M+ϵsin(x),dlaxR.
Graficzna ilustracja równania Keplera dla M=1 i ϵ=14.

W tym przypadku ϕ(x)=M+ϵsin(x). Zauważmy, że w funkcja ϕ jest zwężająca ze stałą

Lmaxx|ϕ(x)|ϵ<1.

Ponieważ obrazem prostej przy przekształceniu ϕ jest odcinek D=[Mϵ,M+ϵ], to znaczy, że ϕ --- ograniczona do D --- spełnia założenia twierdzenia Banacha o kontrakcji. Stąd istnieje dokładnie jedno rozwiązanie naszego równania w przedziale D. Rozwiązanie to może być aproksymowane z dowolnie małym błędem przy pomocy iteracji prostych, startując z dowolnego przybliżenia początkowego x0D. Jednak, gdy ϵ1, zbieżność może być bardzo powolna... (Wkrótce przekonasz się, że są szybsze metody).

Zaletą iteracji prostych jest fakt, że zbieżność nie zależy od wymiaru n zadania, ale tylko od stałej Lipschitza L (jednak w praktyce czasem sama stała Lipschitza może zależeć od wymiaru zadania...). Metoda Banacha ma szczególne zastosowanie w przypadku, gdy funkcja ϕ jest zwężająca na całym zbiorze D, tzn. D0=D. Jeśli ponadto D ma skończoną średnicę, diam(D)<+, to dla osiągnięcia ϵ-aproksymacji zera funkcji f wystarczy wykonać

k=k(ϵ)=log(x0x*/ϵ)log(1/L)=log(diam(D)/ϵ)log(1/L)

iteracji, niezależnie od x0. Metody zbieżne dla dowolnego przybliżenia początkowego nazywamy zbieżnymi globalnie. Obie przedstawione dotychczas metody: bisekcji i Banacha, przy rozsądnych założeniach, są zbieżne globalnie.

Okazuje się, że metoda iteracji prostej może być --- w bardzo szczególnych przypadkach --- zbieżna szybciej niż liniowo. Z taką sytuacją będziemy mieli do czynienia, gdy rozpatrzymy metodę Newtona.

Metoda Newtona

Zarówno metoda Banacha, jak i bisekcja, są zbieżnie liniowo, co w praktyce może okazać się zbieżnością dość powolną (np. dla metody zbieżnej liniowo z ilorazem 1/2 dopiero po piątej iteracji dostajemy kolejną dokładną cyfrę wyniku). Wykorzystując więcej informacji o funkcji f, której miejsca zerowego poszukujemy, możemy istotnie przyspieszyć zbieżność metody. Ceną, jaką przyjdzie nam zapłacić, będzie utrata globalnej zbieżności.

W dalszych rozważaniach będziemy zakładać dla uproszczenia, że dziedzina D=R.

Idea metody Newtona opiera się na popularnym wśród inżynierów pomyśle linearyzacji: zamiast szukać miejsca zerowego skomplikowanej f, przybliżmy ją linią prostą, a dla niej już umiemy znaleźć miejsce zerowe!

Plik:Newton.jpg
Isaac Newton
Przypisywanie metody stycznych Newtonowi jest pewną przesadą. Metodę Newtona taką, jaką znamy (z pochodną w mianowniku), zaproponował w 1740 roku Simpson (ten od kwadratury), kilknaście lat po śmierci Newtona. Żeby było jeszcze zabawniej, odkrywcą metody siecznych zdaje się być... Newton! Więcej na ten temat przeczytasz w artykule T.Ypma w SIAM Review 37, 1995. Zobacz biografię

Startując z pewnego przybliżenia początkowego x0, w kolejnych krokach metody, k-te przybliżenie xk jest punktem przecięcia stycznej do wykresu f w punkcie xk1. Ponieważ równanie stycznej wynosi y(x)=f(xk1)+f(xk1)(xxk1), otrzymujemy wzór

Algorytm Metoda Newtona (stycznych)


 {{{3}}}

Oczywiście, aby metoda Newtona była dobrze zdefiniowana, musimy założyć, że f(xk1) istnieje i nie jest zerem.

Metoda Newtona: pierwszy krok
<flash>file=Newton.swf|width=550|height=300</flash> <div.thumbcaption>Postęp iteracji Newtona

Zauważmy, że metodę Newtona można traktować jako szczególny przypadek iteracji prostych, gdzie

ϕ(x)=xf(x)f(x).

Widać też, że nie jest ona zbieżna globalnie.

Metoda Newtona i jej podobne należą do grupy metod zbieżnych lokalnie. Znaczy to, że zbieżność ciągu {xk}k do zera danej funkcji f jest zapewniona jedynie wtedy, gdy przybliżenia początkowe zostały wybrane dostatecznie blisko x*.

Nawet jeśli pochodna w xk1 się nie zeruje, ciąg {xk}k może nie zbiegać do zera funkcji f. Okazuje się jednak, że jeśli wystartujemy dostatecznie blisko rozwiązania x*, to metoda Newtona jest zbieżna. Dokładniej, załóżmy najpierw, że f(x*)=0 oraz

f(x*)0.

Ponadto załóżmy, że f jest dwukrotnie różniczkowalna w sposób ciągły, fC2(D). Rozwijając ϕ w szereg Taylora w punkcie x*, otrzymujemy

xkx*=ϕ(xk1)ϕ(x*)=(xk1x*)ϕ(x*)+(xk1x*)2ϕ(ξk)/2,

gdzie min(x*,xk1)ξkmax(x*,xk1). Wobec tego, że ϕ(x*)=f(x)f(x)/(f(x))2=0 i ϕ(ξk)=f(ξk)/f(ξk), mamy

xkx*=(xk1x*)2f(ξk)2f(ξk).

Zdefiniujmy liczbę

Rf=supr0sup{x:|xx*|r}|2(xx*)f(x)f(x)|<1.

Oczywiście Rf>0. Dla xk1 spełniającego |xk1x*|R<Rf, mamy z poprzedniej równości

|xkx*|q|xk1x*|,

gdzie q<1 i q zależy tylko od R.

Niech teraz x* będzie zerem m-krotnym,

f(x*)=f(x*)==f(m1)(x*)=0f(m)(x*),

gdzie m2, oraz niech f będzie m-krotnie różniczkowalna w sposób ciągły. Wtedy

Parser nie mógł rozpoznać (nieznana funkcja „\aligned”): {\displaystyle \displaystyle \aligned x_k-x^* &= (x_{k-1}-x^*)\,-\,\frac{(x_{k-1}-x^*)^m \frac{f^{(m)} (\eta_k^{(1)})}{m!}}{(x_{k-1}-x^*)^{m-1} \frac{f^{(m-1)}(\eta_k^{(2)})}{(m-1)!}} \nonumber \\ &= (x_{k-1}-x^*)\left(1-\frac 1m\frac {f^{(m)}(\eta_k^{(1)})}{f^{(m)}(\eta_k^{(2)})} \right) \nonumber \\ &\approx & (x_{k-1}-x^*)\Big( 1-\frac 1m\Big), \endaligned}

o ile xk1 jest "blisko" x*.

Metoda Newtona jest więc zbieżna lokalnie. Gdy x0 jest zbyt daleko od rozwiązania może zdarzyć się, że iteracja Newtona zacznie nas oddalać od miejsca zerowego, co ilustruje poniższy przykład:

Plik:MNnewtononestepdiv.png
Metoda Newtona: jeśli startujemy zbyt daleko od miejsca zerowego f, zamiast przybliżać się do niego, zaczynamy się oddalać! (gdzie będzie x3?...)

Z powyższego można też wywnioskować, jaki jest charakter zbieżności metody Newtona. Dla zera jednokrotnego x* oraz f(x*)0 mamy bowiem

|xkx*||xxk1|2|f(x*)|2|f(x*)|.

Mówimy, że zbieżność metody Newtona, gdy f(x*)0 jest kwadratowa.

Stwierdzenie

Jeśli f(x*)0 oraz f(x*)=0 to zbieżność jest nawet szybsza. Z kolei dla zera m-krotnego (tzn. f(x*)=f(x*)=f(m)(x*)=0, m>1) zbieżność jest liniowa z ilorazem (11m).

Metoda Newtona jest pierwszą poznaną tutaj metodą iteracyjną, która jest (dla zer jednokrotnych) zbieżna szybciej niż liniowo. Dla takich metod wprowadza się pojęcie wykładnika zbieżności, który jest zdefiniowany następująco.

Porównanie zbieżności metody bisekcji i stycznych dla równania ex1=0. Błąd kolejnych przybliżeń wyświetlany jest w skali logarytmicznej, dzięki czemu lepiej widać różnicę między zbieżnością liniową a kwadratową.

Powiemy, że metoda iteracyjna ϕ jest w klasie funkcji F rzędu co najmniej p1, gdy spełniony jest następujący warunek. Niech fF i f(x*)=0. Wtedy istnieje stała C< taka, że dla dowolnych przybliżeń początkowych x0,,xs1 dostatecznie bliskich x*, kolejne przybliżenia xk=ϕ(xk1,,xks) generowane tą metodą spełniają

|xkx*|C|xk1x*|p.

Ponadto, jeśli p=1 to dodatkowo żąda się, aby C<1.

Definicja

Wykładnikiem zbieżności metody iteracyjnej ϕ w klasie F nazywamy liczbę p* zdefiniowaną równością

p*=sup{p1:ϕ jest rzędu co najmniej p}.

Możemy teraz sformułować następujące twierdzenie, które natychmiast wynika z poprzednich rozważań.

Twierdzenie O rzędzie zbieżności metody Newtona

Wykładnik zbieżności metody Newtona (stycznych) wynosi p*=2 w klasie funkcji o zerach jednokrotnych, oraz p*=1 w klasie funkcji o zerach wielokrotnych.

Zbieżność metody Newtona dla zer wielokrotnych f(x)=(x1)5 jest liniowa z ilorazem 45 (końcowe załamanie wykresu spowodowane jest przypadkowym trafieniem w dokładne miejsce zerowe). Metoda bisekcji nie jest na to czuła i dalej zbiega z ilorazem 12.

Metoda siecznych

Inną znaną i często używaną metodą iteracyjną, opartą na podobnym pomyśle linearyzacyjnym co metoda Newtona, jest metoda siecznych, w której zamiast przybliżenia wykresu f przez styczną, stosuje się przybliżenie sieczną.

Metoda ta wykorzystuje więc do konstrukcji xk przybliżenia xk1 i xk2. Musimy również wybrać dwa różne punkty startowe x0 i x1. Ponieważ sieczna dla f w punktach xk1 i xk2 ma wzór

y(x)=xxk2xk1xk2f(xk1)+xxk1xk2xk1f(xk2),

otrzymujemy

Algorytm Metoda siecznych


 {{{3}}}

Zauważmy, że jeśli xk1 i xk2 są blisko siebie, to xk jest podobny do tego z metody Newtona, bowiem wtedy iloraz różnicowy przybliża pochodną f,

f(xk1)f(xk2)xk1xk2f(xk1).

Nie wystarcza to jednak, aby osiągnąć zbieżność z wykładnikiem 2. Można pokazać, że przy podobnych założeniach o funkcji, wykładnik zbieżności metody siecznych dla zer jednokrotnych i dostatecznie gładkich funkcji wynosi p*=1+52=1.618. Jako wariant metody Newtona, metoda siecznych jest również zbieżna lokalnie.

Porównanie zbieżności metody bisekcji, stycznych i siecznych dla równania ex1=0. Błąd kolejnych przybliżeń wyświetlany jest w skali logarytmicznej.

Niewątpliwą zaletą metody siecznych jest jednak to, że nie wymaga obliczania pochodnej funkcji (bywa, że dokładne wyznaczenie pochodnej jest niemożliwe, gdy np. funkcja jest zadana zewnętrzną procedurą, do której kodu źródłowego nie mamy dostępu; zwykle też koszt obliczenia wartości pochodnej jest wyższy od kosztu obliczenia wartości funkcji). Jest to również istotne w pakietach numerycznych, gdzie czasem nie chcemy wymagać od użytkownika czegokolwiek, oprócz podania wzoru na funkcję i przybliżonej lokalizacji miejsca zerowego.

Ponadto, często zdarza się, że wyznaczenie wartości pochodnej, f(xk), jest tak samo, albo i bardziej kosztowne od wyznaczenia wartości f(xk). W takim wypadku okazuje się, że metoda stycznych --- choć wolniej zbieżna niż metoda stycznych --- dzięki temu, że jej iteracja wymaga jedynie wyznaczenia jednej wartości f, jest bardziej efektywna od metody Newtona: koszt osiągnięcia zadanej dokładności jest w takim przypadku mniejszy od analogicznego kosztu dla metody Newtona.

Jednak gdy żądane przez użytkownika dokładności są bardzo wielkie, a sama funkcja "złośliwa", metoda siecznych może cierpieć z powodu redukcji cyfr przy odejmowaniu.

Metoda Brenta

Naturalnie, uważny student zaczyna zadawać sobie pytanie, czy nie można w jakiś sposób połączyć globalnej zbieżności metody bisekcji z szybką zbieżnością metody siecznych tak, by uzyskać metodę zbieżną globalnie, a jednocześnie istotnie szybciej niż liniowo.

Okazuje się, że można to zrobić, wprowadzając metodę opartą na trzech punktach lokalizujących miejsce zerowe: dwóch odcinających zero tak jak w metodzie bisekcji i trzecim, konstruowanym np. jak w metodzie stycznych. W kolejnej iteracji wymieniamy jeden z punktów albo wedle metody siecznych (i wtedy zapewne szybciej zbliżamy się do zera), albo wykonując bisekcję (aby zagwarantować sobie, że w wiadomym przedziale miejsce zerowe rzeczywiście się znajduje).

Ten prosty pomysł metody hybrydowej wymaga jednak subtelnego dopracowania. Zostało to zrobione w 1973 roku przez Richarda Brenta. Funkcja MATLABa (i Octave'a) fzero implementują właśnie metodę Brenta. Autorem implementacji w Octave jest ówczesny student matematyki na Uniwersytecie Warszawskim, Łukasz Bodzon. Fortranowski kod metody Brenta można znaleźć także w Netlibie. Inną funkcją Octave'a służącą rozwiązywaniu równań nieliniowych jest fsolve:

octave:1> [X, MSG, INFO] = fsolve ('cos', 1)
X =  1.5708
MSG =  1
INFO = solution converged within specified tolerance

octave:2> cos(X)
ans =  6.1230e-17

Metody dla układów równań nieliniowych

Niektóre z poznanych metod można łatwo rozszerzyć na przypadek układu N równań z N niewiadomymi, to znaczy

F(x)=0,

gdzie F:RNRN.

Metoda Banacha

Jak pamiętamy, metodę Banacha sformułowaliśmy od razu dla zagadnienia wielowymiarowego. Analiza i własności metody są zatem już omówione.

Wielowymiarowa metoda Newtona

Okazuje się, że metodę Newtona można uogólnić na przypadek układu N równań nieliniowych z N niewiadomymi. Zapiszmy wzór na skalarną metodę Newtona odrobinę inaczej:

xk+1=xk[F(xk)]1F(xk).

Niezwykłe jest, że taki wzór nie tylko ma sens w przypadku, gdy F:RNRN (wtedy F(xk) jest macierzą Jakobianu F w punkcie xk), ale dodatkowo ta metoda zachowuje wszystkie własności metody stycznych dla przypadku skalarnego:

Twierdzenie O zbieżności wielowymiarowej metody Newtona

Załóżmy, że F:RNRN i istnieje x*RN taki, że

F(x*)=0.

Załóżmy ponadto, że F jest różniczkowalna, a jej pochodna F:RNRN×N jest lipschitzowska i dodatkowo

F(x*) jest nieosobliwa.

Wówczas, jeśli tylko x0 jest dostatecznie blisko rozwiązania x*, to ciąg kolejnych przybliżeń xk, generowany wielowymiarową metodą Newtona, jest zbieżny do x*. Co więcej, szybkość zbieżności jest kwadratowa.

Implementacja wielowymiarowej metody Newtona

Implementując wielowymiarową metodę Newtona, musimy dysponować nie tylko funkcją obliczającą N współrzędnych wektora wartości F, ale także funkcją wyznaczającą N2 elementów macierzy pochodnej F w zadanym punkcie xRN. Zwróćmy uwagę na to, że w implementacji metody nie trzeba wyznaczać F(xk)1, tylko rozwiązać układ równań:

Algorytm Wielowymiarowa metoda Newtona


 {{{3}}}

O tym, jak skutecznie rozwiązywać układy równań liniowych, dowiesz się z kolejnych wykładów. Dowiesz się także, dlaczego nie należy w implementacji korzystać z wyznaczonej explicite macierzy odwrotnej do macierzy Jakobianu.

Literatura

W celu dogłębnego zapoznania się z omawianym na wykładzie materiałem, przeczytaj dodatkowo rozdzia� 3 w

  • D. Kincaid, W. Cheney Analiza numeryczna, Wydawnictwa Naukowo-Techniczne, Warszawa 2006, ISBN 83-204-3078-X.
Rozdziały 3.5 i 3.6 nie są obowiązkowe.

Wiele wariantów metod rozwiązywania układów równań nieliniowych jest przedstawionych w znakomitej monografii

  • C.T.Kelley, Iterative Solution of Systems of Linear and Nonlinear Equations, SIAM, 1995.

Opis metody Brenta znajdziesz w książce

  • R. Brent, Algorithms for Minimization Without Derivatives, Prentice-Hall, 1973.