Analiza numeryczna

Spis treści

1. Cel

Znaleźć rozwiązanie problemu zdefiniowanego modelem matematycznym za pomocą modelu obliczeniowego w postaci programu komputerowego.

Naszym nadrzędnym modelem matematycznym będzie układ autonomicznych równań różniczkowych zwyczajnych np. równania oscylatora van der Pola. Ten nadrzędny model składa się z wielu podrzędnych modeli np. układu równań nieliniowych. \[\ddot x + \epsilon \dot x (x^2-1) + x = 0\] \[\dot x = y\] \[\dot y = -\epsilon y(x^2-1) + x\]

Oscylator przedstawiliśmy w dwóch równoważnych postaciach: za pomocą równania różniczkowego drugiego rzędu oraz dwuwymiarowego układu równań różniczkowych pierwszego rzędu.

Problem sprowadza się do znalezienia rozwiązania tych równań, czyli znalezienia x(t) i y(t), dla dowolnych warunków początkowych x(0) i y(0) oraz każdego t. W przypadku układów nieliniowych równań różniczkowych rozwiązanie tego problemu analitycznie jest zazwyczaj trudne bądź niemożliwe.

Lista podrzędnych problemów:

  1. Znajdowanie pierwiastków układu równań liniowych i nieliniowych – to odpowiada szukaniu punktów równowagi w układzie dynamicznym.
  2. Znajdowanie pochodnych pierwszego i wyższych rzędów – ta informacja umożliwia określenie stabilności tych punktów równowagi lub znalezieniu macierzy Jacobiego.
  3. Znajdowanie wyznacznika i śladu macierzy Jacobiego – umożliwia klasyfikacje punktów równowagi, a w tym określenie ich stabilności
  4. Znajdowanie wektorów i wartości własnych – ta informacje umożliwia znalezienie ogólnego rozwiązania równania liniowego, a w przypadku nieliniowego pełni dodatkową informację o zachowaniu się układu.
  5. Całkowanie – umożliwia znalezienie rozwiązania równania różniczkowego
  6. Rozwiązywanie układu nieliniowych równań różniczkowych zwyczajnych – polega na badaniu zachowania się układu przechodząc wyżej wymienione punkty, a w szczególności na znalezieniu x(t) i y(t) dla dowolnych warunków początkowych x(0) i y(0) oraz każdego t.

Na naszych zajęciach do tego typu problemów będziemy podchodzili tworząc model obliczeniowy w postaci programu. Program to zestaw poleceń realizujących nasz cel. Do implementacji programu wykorzystamy język julka

2. Arytmetyka komputerowa

Polecenia w języku działają na obiektach, które mogą być różnego typu. Najbardziej podstawowe typy to te, które są realizowane sprzętowo - taki typami są liczby całkowite (ang. integers) i zmiennoprzecinkowe (ang. floating-point numbers). Jednostka arytmetyczno-logiczna procesora (ang. arithmetic logic unit, ALU) umożliwia wykonanie operacji arytmetycznych +,-, oraz logicznych |, &, xor, ! na liczbach całkowitych, a jednostka zmiennoprzecinkowa (ang. floating-point unit, FPU) umożliwia wykonanie operacji +,-,*, /. na liczbach zmiennoprzecinkowych.

Dodatkowe informacje o procesorze można znaleźć korzystając z poleceń powłoki:

cat /proc/cpuinfo
cpuid -1

Tylko wymienione wyżej typy mają reprezentacje sprzętową, pozostałe typy są realizowane oprogramowaniem. Każdemu literałowi numerycznemu (znak reprezentujący w kodzie źródłowym liczbę) możemy nadać wartość (reprezentacja binarna w pamięci podręcznej komputera). Zmienne przechowują te wartości po wykonaniu polecenia przypisania. Każde wyrażenie umieszczone w kodzie źródłowym jest ewaluowane, by ostatecznie uzyskać wartość całkowitą bądź zmiennoprzecinkową. Tylko takie wartości procesor „rozumie“.

x=1 # --> wyrażenie; typ Expr (1)
x=1 == eval(Expr(:(=), :x, 1)) # --> true (2)
1+2 # --> wyrażenie; typ Expr (3)
1+2 == eval(Expr(:call, :+, 1, 2)) # --> true (4)
1 # ---> wbudowany typ Int64 (dla komputera 64 bit) (5)
1.0 # ---> wbudowany typ Float64 (dla komputera 64 bit) (6)

2.1. Liczby typu całkowitego

Liczby całkowite bez znaku reprezentowane są kodem szesnastkowym, binarnym i ósemkowym. Rozmiar reprezentacji w bajtach jest określony przez długość tej reprezentacji.

# kod szesnastkowy
typeof(0xff) # --> UInt8
sizeof(0xff) # --> 1
typeof(0x0ff) # --> UInt16
sizeof(0x0ff) # --> 2
typeof(0x0ffff) # --> UInt32
sizeof(0x0ffff) # --> 4
typeof(0x0ffffffff) # --> UInt64
sizeof(0x0ffffffff) # --> 8

# kod binarny
typeof(0b1) # --> UInt8
# kod ósemkowy
typeof(0o1) # --> UInt8

Zakresy możliwych wartości reprezentowanych przez typ całkowity

for T in [Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128]
        println("$(lpad(T,7)): [$(typemin(T)),$(typemax(T))]")
end

Przekroczenie zakresu liczb całkowitych (ang. integer overflow)

typemax(Int64)
typemax(Int64)+1
# Liczby całkowite podlegają arytmetyce modularnej: 
# po przekroczeniu zakresu max przyjmują wartość min i odwrotnie
typemin(Int64) == typemax(Int64)+1
typemin(Int64)-1 == typemax(Int64)

2.2. Liczby typu zmiennoprzecinkowego

Notacja standardowa - notacja naukowa - znormalizowana notacja naukowa. Komputer pracuje ze znormalizowaną notacją liczb zmiennoprzecinkowych (sprawdź w sekcji: wewnątrzmaszynowe przedstawienie liczb)

100.0 == 10e1 == 1e2# --> true
# w znormalizowanej notacji naukowej dla liczb mniejszych od 1 wykładnik jest ujemny
0.5 == 5e-1 # --> true
Cyfry znajdujące się po lewej stronie litery 'e' nazywany znaczącymi (eng. /significand/).

Literał typu Float32 zawiera literę 'f'

typeof(100.0) # --> Float64
typeof(1f2) == typeof(Float32(100)) # --> Float32
typeof(1.1f2) == typeof(Float32(110)) # --> Float32

Literał typu Float16 jest realizowany przez oprogramowanie, a sprzętowo jest realizowany przez Float32

Float16(7.0)
dump(Float16(7.)) # --> Float16 Float16(7.0)
dump(Float32(7.)) # --> Float32 4.0f0
dump(Float64(7.)) # --> Float64 4.0

Zakresy możliwych wartości reprezentowanych przez typ zmiennoprzecinkowy

for T in [Float16, Float32, Float64]
        println("$(lpad(T,7)): [$(typemin(T)),$(typemax(T))]")
end

Epsilon maszynowy (ang. machine epsilon): najmniejsza wartość rozróżniania przez typy zmiennoprzecinkowe.

for T in [Float16, Float32, Float64]
        println("$(lpad(T,7)): $(eps(T))")
end

Utrata cyfr znaczących (ang. loss of significance)

0.0

Float16(1.001) - Float16(1.0014) # --> (-0.0004)->Float16(0.0) zaokrągla w dół
bitstring(Float16(1.001)) # --> "0011110000000001"
bitstring(Float16(1.0014))# --> "0011110000000001"
Float16(1.001) == Float16(1.0014) # --> true


Float16(1.001) - Float16(1.0015) # --> (-0.0005)->Float16(-0.000977) zaokrągla w górę
bitstring(Float16(1.001)) # --> "0011110000000001"
bitstring(Float16(1.0015)) # --> "0011110000000010" (w pamięci podręcznej tuż
# obok, co Float16(1.001))
Float16(1.001) == Float16(1.0015) # --> false

eps(Float16) # --> Float16(0.000977)

Float16(1.001) - Float16(1.0024) # --> (-0.0014)->Float16(-0.000977) zaokrągla w dół
bitstring(Float16(1.0024)) # --> "0011110000000010"
Float16(1.001) - Float16(1.0025) # --> (-0.0015)->Float16(-0.001953) zaokrągla w górę
bitstring(Float16(1.0025)) # --> "0011110000000011" (w pamięci podręcznej tuż
# obok, co Float16(1.0024))

2*eps(Float16) # --> Float16(0.001953)

Float16(1.001) - Float16(1.0034) # --> (-0.0024)->Float16(-0.001953) zaokrągla w dół
bitstring(Float16(1.0034)) # --> "0011110000000011"
Float16(1.001) - Float16(1.0035) # --> (-0.0025)->Float16(-0.00293) zaokrągla w górę
bitstring(Float16(1.0035)) # --> "0011110000000100" (w pamięci podręcznej tuż
# obok, co Float16(1.0034))

3*eps(Float16) # --> Float16(0.00293)


Błąd bezwzględny i względy:

err = [ abs((Float64(1.001) - Float64(i))-(Float16(1.001) - Float16(i)))
        for i=1.001:0.0001:1.01 ]

errrel = [ abs((Float64(1.001) - Float64(i))-(Float16(1.001) - Float16(i)))/abs(Float64(1.001) - Float64(i))*100
           for i=1.001:0.0001:1.01 ]

xFloat64 = [Float64(1.001) - Float64(i) for i=1.001:0.0001:1.01 ];
xFloat16 = [Float16(1.001) - Float16(i) for i=1.001:0.0001:1.01 ];

Reprezentacja graficzna ilustrująca zaokrąglanie liczb typu Flaot16. Widzimy, że pierwsze pięć wyników jest zaokrąglana w dół do wartości 0; następne pięć jest zaokrąglane w górę do wartości -0.000977; następne pięć jest zaokrąglane w dół do wartości -0.000977; kolejne analogicznie. Jaki błąd popełniamy w obliczeniach z wykorzystaniem typu Float16 w odniesieniu do typu referencyjnego Float64?

using Pkg; Pkg.add("Plots") # zainstalowanie pakietu do wizualizacji danych
using Plots # załadowanie pakietu
plot([xFloat16 xFloat64],seriestype=:scatter)
savefig("rounding_float16.png")

rounding_float16.png

Reprezentacja graficzna ilustrująca błąd bezwzględny. Pierwsze pięć wyników to wartości referencyjne, gdyż dla tych wyników badany typ Float16 ma wartość 0. Dla szóstego wyniku mamy wartość abs(-0.0005-(-0.000977))= 0.000477; kolejny abs(-0.0006-(-0.000977)=0.000377; kolejne analogicznie. Na wykresie widzimy, że czwarty wynik ma wartość 0.0003, a szósty wynik od końca ma wartość błędu około 0.0003. Co do wartości bezwzględniej nie różnią się znacznie. Czy więc w taki samym stopniu wpływają na dokładność naszych obliczeń?

using Plots # załadowanie pakietu
plot(err;seriestype=:scatter)
savefig("absolute_error.png")

absolute_error.png

Reprezentacja graficzna ilustrująca błąd względny w skali procentowej. Pierwszy wynik z poprzednich wykresów nie jest pokazywany, gdyż jest to wartość 0/0. Widzimy, że błąd względny pierwszych czterech obliczeń to 100%, gdyż obliczenia z wykorzystaniem typu Float16 zaokrągliły nam się do 0. Wynik trzeci na wykresie jet tym, o którego pytam w poprzednim akapicie – ma więc wartość błędu 100%, a wynik szósty od końca ma wartość zaledwie 3.4%!

using Plots # załadowanie pakietu
plot(errrel;seriestype=:scatter)
savefig("relative_error.png")

relative_error.png

2.3. Wewnątrzmaszynowe przedstawienie liczb

Przedstawienie liczb całkowitych za pomocą: Two's complement representaion

intmax = typemax(Int64)-5
for i = 1:10
    println(bitstring(Int64(intmax)+i))
end

Przedstawienie liczb zmiennoprzecinkowych za pomocą: IEEE 754 double-precision binary floating-point format: binary64 ('fraction' odnosi się do cyfr znaczących (significand) w znormalizowanej notacji naukowej)

IEEE_754_Double_Floating_Point_Format.png

bitstring(0.5)
"0 01111111110 0000000000000000000000000000000000000000000000000000"
2.0^(1022-1023)==2.0^(-1) # dla abs(number)<1 wykładnik jest ujemny
2.0^(Int64(0b01111111110) - 1023) == 2.0^(-1)
# (-1)^sign*(1.b51b50..b0) x 2^0 = 1
(-1)^0*(1 + 0) * 2^(-1) # --> 0.5

bitstring(1.0)
"0 01111111111 0000000000000000000000000000000000000000000000000000"
2^(1023-1023)==2^0
2^(Int64(0b01111111111) - 1023) == 2^0
# (-1)^sign*(1.b51b50..b0) x 2^0 = 1
(-1)^0*(1 + 0) * 2^0 # --> 1.0

bitstring(1.0000000000000002)
"0 01111111111 0000000000000000000000000000000000000000000000000001"
2^(Int64(0b01111111111) - 1023) == 2^0
(-1)^0*(1 + 2^(-52)) * 2^0 # --> 1.0000000000000002

bitstring(2.0)
#                       1.b51b50..b0 --> significand
"0 10000000000 |0000000000000000000000000000000000000000000000000000|"
2^(1024-1023)==2^1
2^(Int64(0b10000000000)-1023) == 2^1
(-1)^0 * (1+0) * 2^1 # --> 2.0

bitstring(3.0)
"0 10000000000 1000000000000000000000000000000000000000000000000000"
2^(Int64(0b10000000000)-1023) == 2^1
(-1)^0 * (1 + 2^(-1)) * 2^1 #--> 3.0

bitstring(4.0)
"0 10000000001 0000000000000000000000000000000000000000000000000000"
2^(Int64(0b10000000001)-1023) == 2^2
(-1)^0 * (1 + 0) * 2^2 # --> 4.0

bitstring(5.0)
"0 10000000001 0100000000000000000000000000000000000000000000000000"
2^(Int64(0b10000000001)-1023) == 2^2
(-1)^0 * (1 + 2^(-2)) * 2^2 #--> 5.0

bitstring(5.1)
"0 10000000001 0100011001100110011001100110011001100110011001100110"
2^(Int64(0b10000000001)-1023) == 2^2
# (-1)^0 * (1 + 2^-2 + 2^-6 + 2^-7 +...+ 2^-51) * 2^2 # --> 5.1

# Brodacasting 
index = parse.(Int,split("0100011001100110011001100110011001100110011001100110",""))

# Generator Expressions
(-1)^0 * (1 + sum(index[i]*2.0^(-i) for i=1:52)) * 2^2 # --> 5.1

Różnice między epsilonem maszynowym a ulp (eng. unit in the last place)

bitstring(1.0)
"0 01111111111 0000000000000000000000000000000000000000000000000000"
2^(1023-1023)==2^0
2^(Int64(0b01111111111) - 1023) == 2^0
# (-1)^sign*(1.b51b50..b0) x 2^0 = 1
(-1)^0*(1 + 0) * 2^0 # --> 1.0

{cyfry znaczące: 1 } * 2^{ wykładnik: 0 }

bitstring(1.0000000000000002)
"0 01111111111 0000000000000000000000000000000000000000000000000001"
2^(Int64(0b01111111111) - 1023) == 2^0
(-1)^0*(1 + 2^(-52)) * 2^0 # --> 1.0000000000000002

{cyfry znaczące: 1.0000000000000002 } * 2^{ wykładnik: 0 }

Najmniejszą wartością jest tutaj eps(Float64), gdyż
1+eps(Float64) == 1.0000000000000002 #--> true

Kolejne możliwe liczby to wielokrotności epsilona:
bitstring(1.0000000000000004)
"0011111111110000000000000000000000000000000000000000000000000010"
bitstring(1.0000000000000006)
"0011111111110000000000000000000000000000000000000000000000000011"
bitstring(1.0000000000000008)
"0011111111110000000000000000000000000000000000000000000000000100"

# itd.

# aż dochodzimy do największej możliwej liczby z precyzją eps(Float64):
bitstring(1.9999999999999998)
"0011111111111111111111111111111111111111111111111111111111111111"

# Wyszystkie powyższe reprezentacje charakeryzują się tym, że wykładnik jest
# równy 0: 2^(Int64(0b01111111111) - 1023) == 2^0; a to oznacza, że cyfry
# znaczące są zawsze przemnażane przez wartość 1. 


# Natomiast liczba 1.9999999999999999 jest już w rzeczywistości liczbą 2.0
1.9999999999999999 == 2.0 # --> true
bitstring(1.9999999999999999)
"0100000000000000000000000000000000000000000000000000000000000000"
bitstring(2.0)
"0100000000000000000000000000000000000000000000000000000000000000"

# Tutaj zmniejsza się precyzja, gdyż wykładnik jest równy 1 a nie 0:
# 2^(Int64(0b10000000000) - 1023) = 2^1; a to oznacza, że cyfry znaczące są
# przemnażane przez 2 a nie przez 1:
bitstring(2.0000000000000000)
"0100000000000000000000000000000000000000000000000000000000000000"
2.0 == 2.0000000000000002 #--> true
bitstring(2.0000000000000002)
"0100000000000000000000000000000000000000000000000000000000000000"
bitstring(2.0000000000000004)
"0100000000000000000000000000000000000000000000000000000000000001"

# Zatem prezycja wynosi 2*eps(Float64).

# Precyzja liczba zmiennoprzecinkowych zmniejsza się wraz ze wzrostem
# wykładnika, gdyż z każdym jego wzrostem cyfry znaczące są przemnażane przez
# większą liczbę

eps(1.0) #--> 2.220446049250313e-16 ulp=eps(Float64)
eps(2.0) #--> 4.440892098500626e-16 ulp=2*eps(Float64)
eps(3.0) #--> 4.440892098500626e-16 ulp=2*eps(Float64) 
eps(4.0) #--> 8.881784197001252e-16 ulp=4*eps(Float64)
eps(5.0) #--> 8.881784197001252e-16 ulp=4*eps(Float64)
eps(6.0) #--> 8.881784197001252e-16 ulp=4*eps(Float64)
eps(7.0) #--> 8.881784197001252e-16 ulp=4*eps(Float64)
eps(8.0) #--> 1.7763568394002505e-15 ulp=8*eps(Float64) 

2.4. Arytmetyka dowolnej precyzji (ang. arbitrary precision arithmetic)

Gdy binarna reprezentacja liczby jest dłuższa niż słowo maszynowe

typeof(1) # --> Int64 (dla komputera 64 bit)
typeof(10000000000000000000) # --> Int128
typeof(1000000000000000000000000000000000000000) # --> BigInt
Meta.parse("1") # --> 1
Meta.parse("10000000000000000000") # --> :(10000000000000000000)
Meta.parse("1000000000000000000000000000000000000000") # -->
# :(1000000000000000000000000000000000000000)
dump(1) # --> Int64 1
dump(Int128(1)) # --> Int128 1
dump(BigInt(1)) # -->
"""BigInt
alloc: Int32 1
size: Int32 1
d: Ptr{UInt64} @0x0000000003eb6870
"""
dump(12323223434354534534534523434564557568568907) # -->
"""
BigInt
alloc: Int32 4
size: Int32 3
d: Ptr{UInt64} @0x0000000003c024a0

3. Znajdowanie pierwiastków równań (miejsc zerowych funkcji)

Mamy trzy modele układów dynamicznych określone nieliniowymi równaniami różniczkowymi pierwszego rzędu:
\[\dot x = x^2\] \[\dot x = x-\tan(x)\] \[\dot x = \frac{1}{(1-x)^6}\]

W celu zbadania zachowania się tych układów chcemy się dowiedzieć, jakie są ich punkty równowagi. Ten problem sprowadza się do rozwiązania równania: f(x)=0, gdzie badana funkcja znajduje się po prawej stronie naszego równania różniczkowego. Jeśli będziemy znać te punkty, to na podstawie ich analizy stabilności, będziemy w stanie powiedzieć, czy dany punkt jest na przykład ściekiem lub źródłem tj., czy jest stabilny bądź niestabilny.

Do znalezienia punktów równowagi zastosujemy trzy metody:

  1. Równego podziału (zwana także metodą bisekcji, ang. bisection method)
  2. Newtona (zwana także metodą stycznych)
  3. Siecznych (ang. secant method)

3.1. Metoda równego podziału

Znajduje zastosowanie do analizy funkcji ciągłych w przedziale domkniętym [a,b], gdzie f(a)f(b)<0. Jeśli funkcja ciągła zmienia znak w zadanym przedziale, to na pewno tam istnieje minimum jedno miejsce zerowe.

Procedura:

  1. Wybieramy przedział funkcji do analizy np. [a,b].
  2. Jeśli f(a)f(b)<0, to istnieje minimum jeden punkt.
  3. Znajdujemy punkt dzielący przedział na pół: \(c=\frac{a+b}{2}\).
  4. Sprawdzamy, czy f(a)f(c)<0 lub f(c)f(b)<0.
  5. Przechodzimy do punktu 3, zastępując odpowiednio symbole.
  6. Jeśli \(f(a)-f(b) < \epsilon\) (dopuszczalny błąd), to punkt c jest naszym miejscem zerowym.

Pytania:

  1. Jak się zachowa metoda w zastosowaniu dla funkcji \(f(x)=x^3\) i \(f(x)=\frac{1}{x}\)?
  2. Czy obliczanie punktu c w taki sposób: \(c=\frac{a+b}{2}\) – jest numerycznie bezpieczne?
  3. Od czego zależy dobór wartości dopuszczalnego błędu metody \(\epsilon\)
  4. Czy metoda ma działać w nieskończoność, czy warto ustawić limit iteracji?
  5. Jeśli metoda nie znajduje miejsca zerowego, to jak powinna się zachować?
  6. Jakie cechy funkcji, oprócz ciągłości, sprawiają, że metoda zawsze znajduje miejsce zerowe dla dowolnie dużego przedziału [a,b]?

Błąd metody jest określony przez nierówność: \[|r-c_n| \leq 2^{-(n+1)}(b_0 - a_0)\] \[|r-c_n| \leq C(b_0 - a_0)\] \[c_n = \frac{a_n+b_n}{2}\] \[r = \lim\limits_{n\rightarrow\infty}(a_{n}) = \lim\limits_{n\rightarrow\infty}(b_n)\]

Metoda charakteryzuje się liniowym rzędem zbieżności

3.2. Metoda Newtona

Metodę Newtona możemy znaleźć poprzez rozwinięcie w szereg Taylora badanej funkcji \(f(x)\). Rozwijamy ją tylko do pochodnej pierwszego rzędu, gdyż szukamy metody iteracyjnej tj. takiej, która umożliwia znalezienie miejsc zerowych funkcji po pewnej liczbie kroków n. Nasz krok definiujemy jako różnicę pomiędzy krokiem \(x_{n+1}\) a \(x_n\): \(x_{n+1}-x_{n}=h\). Taki krok uzyskamy, jak rozwiniemy w szereg Taylora funkcję \(f(x_{n+1})\) do pierwszej pochodnej w punkcie \(x_n\) : \[f(x_{n+1})=f(x_n+h)=f(x_n)+\frac{x_{n+1}-x_n}{1!}f'(x_n)\]

Ponieważ interesują nas miejsca zerowe, więc nasz problem sprowadza się do rozwiązania równania: \(f(x_{n+1})=0\).

\[0=f(x_{n+1})=f(x_n+h)=f(x_n)+\frac{x_{n+1}-x_n}{1!}f'(x_n)\] \[-f(x_n)=hf'(x_n)\] \[h=-\frac{f(x_n)}{f'(x_n)}\] Wiemy już ile wynosi nasz krok, więc znajdźmy metodę Newtona: \[-f(x_n)=(x_{n+1}-x_n)f'(x_n)\] \[-f(x_n)=x_{n+1}f'(x_n)-x_nf'(x_n)\] \[x_{n+1}f'(x_n)=x_nf'(x_n)-f(x_n)\] \[x_{n+1} = x_{n} - \frac{f(x_n)}{f'(x_n)}\] \[x_{n+1} = x_{n} + h,n\geq0\]

Błąd metody: \[e_n = x_n - r\] \[e_{n+1} = x_{n+1} - r\] \[|x_{n+1}-r| \leq C(x_n - r)^2\] \[\text{r - simple zero:}f(r)=0 \neq f'(r),\text{C - stała}\]

Metoda charakteryzuje się kwadratowym rzędem zbieżności

3.2.1. Przykład dla x2-1

Rozwinięcie w szereg Taylora dla xn=2 do drugiej pochodnej: \[f(x_{n+1})=f(x_n+h)=f(x_n)+\frac{x_{n+1}-x_n}{1!}f'(x_n)+\frac{(x_{n+1}-x_n)^2}{2!}f''(x_n)\] \[f(x_{n+1})= 3 + (x_{n+1}-2)4 + \frac{(x_{n+1}-2)^2}{2}2\] \[f(x_{n+1})= 3 + 4x_{n+1}-8 + (x_{n+1}-2)^2\] \[f(x_{n+1})= 4x_{n+1}-5 + x_{n+1}^2-4x_{n+1}+4\] \[f(x_{n+1})= x_{n+1}^2-1\]

Każdą funkcję możemy rozwinąć w punkcie w szereg Taylora z dowolną precyzją, ale naszym celem jest stworzenie metody iteracyjnej – dlatego rozwijamy tylko do pierwszej pochodnej. \[f(x_{n+1})=f(x_n+h)=f(x_n)+\frac{x_{n+1}-x_n}{1!}f'(x_n)\] \[h=-\frac{f(x_n)}{f'(x_n)}=-\frac{3}{4}\]

using Plots
f(x)=x^2-1
g(x)=3+(x-2)*4 # rozwinięcie w szerg Taylora w punkcie 2
v1 = [f(i) for i in 0:0.01:3]
v2 = [g(i) for i in 0:0.01:3]
plot(0:0.01:3,v1)
plot!(0:0.01:3,v2)
vline!([1,1.25,2])
hline!([0])
savefig("newton_steps.png")

newton_steps.png

3.3. Metoda siecznych

\[x_{n+1} = x_{n} - \frac{f(x_n)}{f'(x_n)},n\geq0\] \[f'(x_n) \approx = \frac{f(x_{n})-f(x_{n-1})}{x_n - x_{n-1}}\] \[x_{n+1} = x_{n} - f(x_n) \frac{x_n - x_{n-1}}{f(x_{n})-f(x_{n-1})}\]

Wyjaśnienie, że xn+1 jest punktem przecięcia osi x

  1. Równanie prostej przechodzącej przez dwa punkty Punkty: \(P_{1}(x_{n-1}, f(x_{n-1}))\) i \(P_{2}(x_n, f(x_n))\) Równanie prostej:

    \[f(x) - f(x_n) = m (x - x_n)\]

    \[f(x_n) - f(x_{n-1}) = m (x_n - x_{n-1})\]

    \[m=\tan(\alpha)=\frac{f(x_n) - f(x_{n-1})}{x_n - x_{n-1}}\]

    \[f(x) - f(x_n) = \frac{f(x_n) - f(x_{n-1})}{x_n - x_{n-1}} (x - x_n)\]

  2. Punkt przecięcia z osią x (gdzie \(y=0\)) Podstawiamy \(y=0\):

    \[0 - f(x_n) = \frac{f(x_n) - f(x_{n-1})}{x_n - x_{n-1}} (x_{n+1} - x_n)\]

    Rozwiązując względem \(x_{n+1}\), otrzymujemy:

    \[x_{n+1} = x_n - f(x_n) \cdot \frac{x_n - x_{n-1}}{f(x_n) - f(x_{n-1})}\]

Analiza błędu:

\[e_n = x_n - r\] \[e_{n+1} = x_{n+1} - r\] \[|e_{n+1}|\approx C|e_n|^{\frac{1+\sqrt{5}}{2}}\]

\[\frac{1+\sqrt{5}}{2}=1.62 < 2\]

Metoda charakteryzuje się superliniowyn rzędem zbieżności

3.4. Metoda iteracji funkcyjnych

Jest to algorytm postaci: \(x_{n+1} = F(x_n)\), gdzie \(F(x_n)\) jest funkcją bądź złożeniem funkcji i jej pochodnych. Metoda stycznych jest metodą iteracji funkcyjnych, gdyż \(F(x_n) = x - \frac{f(x)}{f'(x)}\). Celem zastosowania tej metody jest znalezienie punktów stałych funkcji tj. takich punktów \(F(s)=s\), dla których spełniony jest warunek: \[\lim\limits_{n\rightarrow\infty}(x_n)=s\] \[F(s)=F(\lim\limits_{n\rightarrow\infty}(x_n))=\lim\limits_{n\rightarrow\infty}F(x_n)=\lim\limits_{n\rightarrow\infty}(x_{n+1})=s\]

Na przykład dla funkcji \(f(x) = \sqrt{x}\) punktem stałym jest 1

using Plots
f(x)=√x
g(x)=x
plot(f;xlims=(0,2))
plot!(g)
savefig("fixed_point_sqrt.png")

fixed_point_sqrt.png

W celu określenia istnienia punktu stałego zdefiniujemy pojęcie mapowania kontrakcyjnego: F jest mapowaniem kontrakcyjnym, jeśli spełnia poniżysz warunek dla \(\lambda < 1\). \[|F(x)-F(y)|\leq\lambda|x-y|\]

To równanie mogę też zapisać jako: \[\frac{|F(x)-F(y)|}{|x-y|}\leq\lambda\]

Zatem odległość między х i y jest odwzorowana na mniejszą odległość między F(x) i F(y) przez funkcję kontrakcyjną F.

\(\lambda\) jest więc nachyleniem stycznej do krzywej funkcji. Funkcja pierwiastkowa jest mapowaniem kontrakcyjnym dla każdego x spełniającego warunek: \(f'(x)=0.5x^{-0.5}<1\), \(0.5\frac{1}{\sqrt{x}} < 1\), \(0.5 < \sqrt{x}\), \(x > 0.25\)

Mapowaniem kontrakcyjnym jest zatem też funkcja \(f(x)=x^3+1\) dla każdego \(-\sqrt{\frac{1}{3}}< x < \sqrt{\frac{1}{3}}\), ale nie ma punktu stałego w tym zakresie. Jakie warunek musi zostać spełniony, aby istniał punkt stały?

Twierdzenie o mapowaniu kontrakcyjnym:

Dla podzbioru domkniętego C liczb rzeczywistych na linii, jeśli F jest mapowaniem kontrakcyjnym C w siebie tj. F jest iniekcją, to F ma jeden i tylko jeden punkt stały. Ten punkt jest granicą każdej sekwencji liczb uzyskanej z równania \(x_{n+1}=F(x_n)\), gdy \(x_0 \in C\)

Naszym zadaniem jest znalezienie miejsc zerowych funkcji a nie punktów stałych. Musimy zatem wykonać kilka przekształceń, aby wykorzystać metodę iteracji prostych do naszych celów. Badaną funkcje f(x)=0 sprowadzamy do postaci x=g(x) na przykład: \[f(x)= \sqrt(x) \rightarrow x=0\] \[f(x)= x^3-x \rightarrow x=x^3; x+x=x+x^3 \rightarrow x=\frac{x+x^3}{2}\] \[f(x)= x^2-2 \rightarrow x=x^2+x-2; x=\frac{2}{x}; x+x=\frac{2}{x}+x\] \[f(x) = e^x-\sin(x)-1 \rightarrow x=\ln(\sin(x)+1)\text{?}\] \[f(x) = e^x-1.5-\arctan(x) \rightarrow x=\ln(1.5+\arctan{x})\]

Błąd metody: \[e_n=x_n-s\] \[e_{n+1}=\frac{1}{q!}e_n^qF^{(q)}(\xi_{n})\] \(\xi_{n}\) jest punktem pomiędzy xn a s; q nazywamy rzędem zbieżności, jeśli ta granica istnieje: \(\lim\limits_{n\rightarrow\infty}\frac{|e_{n+1}|}{|e_n|^q}=\frac{1}{q!}F^{(q)}(s)\)

Na przykład dla metody Newtona: \[F(x)=1-\frac{f(x)}{f'(x)}\] \[F'(x)=1-\frac{f'(x)f'(x)-f(x)f''(x)}{[f'(x)]^2}\] \[F'(x)=\frac{f(x)f''(x)}{[f'(x)]^2}\]

Ponieważ punkt stały funkcji F jest zerem funkcji f, mamy F'(s) = О.

\[F''(x)=\frac{(f'(x)f''(x)+f(x)f'''(x))[f'(x)]^2-f(x)f''(x)2f'(x)f''(x)}{[f'(x)]^4}\]

Dla f(x)=0

\[F''(x)=\frac{(f'(x)f''(x))[f'(x)]^2}{[f'(x)]^4}\]

zazwyczaj dostaniem \(F''(s)\neq0\)

\[F''(s)=\frac{f''(s)}{f'(s)} \neq0\]

Zatem rząd zbieżności jest równy q=2,

a błąd wynosi: \[e_{n+1}=\frac{1}{2}e_n^2F''(\xi_{n})\]

3.5. Równania nieliniowe

Równania możemy podzielić na algebraiczne i niealgebraiczne. Układ \(\dot x = x^2\) jest określony wielomianem drugiego stopnia – jest to więc równanie algebraiczne. Drugi \(\dot x = x-\tan(x)\) zawiera funkcję przestępną – tan(x), więc jest to równanie przestępne. Trzeci \(\dot x = \frac{1}{(1-x)^6}\) jest równaniem algebraicznym.

3.5.1. Wpływ precyzji liczby zmiennoprzecinkowej na ilość mijesc zerowych

using Plots
iter = 0.975:0.001:1.035
wielomian(x::Float64) = x^4-4x^3+6x^2-4x+1
wielomian(x::Float32) = x^4-4x^3+6x^2-4x+1

sum(wielomian.(Float64.(iter)) .== 0) # 1 pierwiastek
sum(wielomian.(Float32.(iter)) .== 0) # 21 pierwiastków

plot(wielomian.(Float64.(iter));seriestype=:scatter)
plot!(wielomian.(Float32.(iter));seriestype=:scatter)
savefig("precision_float32.png")

precision_float32.png

Zmniejszenie precyzji liczby zmiennoprzecinkowej do Float32 spowodowało, że zamiast jednego pierwiastka pojawiły się 21 pierwiastki w naszym równaniu.

iter = 0.999999:0.0000001:1.000001
sum(wielomian.(Float64.(iter)) .== 0) # 11 pierwiastków

plot(wielomian.(Float64.(iter));seriestype=:scatter)
savefig("precision_float64.png")

precision_float64.png

3.5.2. Zasady tworzenia algorytmu do znajdowania pierwiastków równania

  1. Obliczanie wartości z wyrażenia jest bezpieczniejsze z punktu widzenia odporności na błędy, jeśli jest ona ewaluowana w trakcie działania algorytmu poprzez dodawanie do niej wartości korygującej, aniżeli obliczana bez niej np. wyrażenie \(c=\frac{a+b}{2}\) należałoby zmodyfikować do równoważnej postaci \(c=a+\frac{b-a}{2}\)
  2. Operacje bardziej obciążające procesor i pamięć zastępujemy lżejszymi np. w metodzie równego podziału zamiast sprawdzać znak funkcji za pomocą mnożenia f(a)f(b)<0, to należałoby porównać znak dwóch wartości signbit(f(a)) == signbit(f(b)).
  3. Działanie algorytmu kończy się, gdy:
    • przekroczymy dozwoloną ilość iteracji. Program wtedy powinien zwrócić informacje o błędzie zbieżności.
    • błąd pomiędzy kolejnymi dwiema wartościami zmiennej zależnej jest mniejszy od założonego błędu. Program zwraca informację o znalezieniu pierwiastka.

4. Znajdowanie pierwiastków układu równań

4.1. Układy równań nieliniowych

W celu znalezienie pierwiastków układu równań rozwiązujemy równania: \[f_1(x_{1*},x_{2*})=0\] \[f_2(x_{1*},x_{2*})=0\]

Jeśli mamy przybliżone rozwiązanie naszego układu równań, czyli dla pewnego x1 i x2, to dokładność przybliżenia (czynnik korygujący) możemy określić jako \(x_{1*}-x_1=h_1\), \(x_{2*}-x_2=h_2\). Problem definiujemy za pomocą rozwinięcia naszego układu równań w szereg Taylora dla punktu (x1,x2)

\[0 = f_1(x_1 + h_1, x_2 + h_2) \approx f_1(x_1,x_2)+ h_1\frac{\partial f_1}{\partial x_1}+h_2\frac{\partial f_1}{\partial x_2}\] \[0 = f_2(x_1 + h_1, x_2 + h_2) \approx f_2(x_1,x_2) + h_1\frac{\partial f_2}{\partial x_1}+h_2\frac{\partial f_2}{\partial x_2}\]

Dokonaliśmy linearyzacji w punkcie (x1,x2) poprzez zbudowanie układu równań liniowych z niewiadomymi h1 i h2. Pochodne cząstkowe tworzą współczynniki w równaniu, a macierz tych współczynników nazywamy Jakobianem:

\[J = \begin{pmatrix}\frac{\partial f_1}{\partial x_1} & \frac{\partial f_1}{\partial x_2}\\\frac{\partial f_2}{\partial x_1} & \frac{\partial f_2}{\partial x_2}\end{pmatrix}\]

Aby rozwiązanie układu równań istniało, to musi istnieć macierz odwrotna \(J^{-1}\), tj. wyznacznik macierzy musi być różny od zera.

\[-\begin{pmatrix}f_1(x_1,x_2)\\ f_2(x_1,x_2)\end{pmatrix}=J\begin{pmatrix}h_1\\ h_2\end{pmatrix}\]

\[\begin{pmatrix}h_1\\ h_2\end{pmatrix}=-J^{-1}\begin{pmatrix}f_1(x_1,x_2)\\ f_2(x_1,x_2)\end{pmatrix}\]

Metoda Newtona dla układu dwuwymiarowego ma postać: \[\begin{pmatrix}x_1^{(k+1)}\\ x_2^{(k+1)}\end{pmatrix}=\begin{pmatrix}x_1^{(k)}\\ x_2^{(k)}\end{pmatrix} + \begin{pmatrix}h_1^{(k)}\\ h_2^{(k)}\end{pmatrix}\]

Dla dowolnego układu n-wymiarowego nasz problem zapisujemy za pomocą notacji macierzowej: \[F(X)=0\] \[X = (x1, x2, ... , x_n)^{T}\] \[F = (f_1,f_2, ... , f_n)^{T}\] \[H=(h_1,h_2,...,h_n)^{T}\]

\[0=F(X+H) \approx F(X) + J(X)H \]

\[H=-J(X)^{-1}F(X)\]

Metoda Newtona: \[X^{(k+1)}=X^{(k)}+H^{(k)}\]

4.1.1. Przykład: dwuwymiarowe równanie wielomianowe

\[x_1 - x_2 - x_1^3 = 0\] \[x_1 + 6x_2 - x_2^3 = 0\]

\[F(X)=\begin{pmatrix}x_1 - x_2 - x_1^3\\ x_1 + 6x_2 - x_2^3\end{pmatrix}\]

\[J(X)=\begin{pmatrix}1-3x_1^2 & -1\\ 1 & 6-3x_2^2\end{pmatrix}\]

dla x1=0 i x2=-3

X0=[0;-3]
f₁(x1,x2)=x1-x2-x1^3
f₂(x1,x2)=x1+6*x2-x2^3
F(x1,x2)=[f₁(x1,x2);f₂(x1,x2)]
J(x1,x2)=[1-3*x1^2 -1;6 1-3*x2^2]
H = -inv(J(X0[1],X0[2]))*F(X0[1],X0[2]) 
[-3.45 -3.45]
[-2.286700925481873 -2.761577628837895]
[-1.4624857977508279 -2.4347895758642046]
[-0.7354570543294794 -2.2724461590331337]
[1.3062224730230296 -1.6089864310800697]
[1.5668617931211841 -1.9959592237888915]
[1.5582498571428895 -2.225055047084357]
[1.5665384578179202 -2.277492142300413]
[1.5693816234671674 -2.2959024556526546]
[1.5704088548735522 -2.3025033225237737]
[1.5707814198957448 -2.304892168298706]
[1.57091679006564 -2.3057594706206888]
"Pierwiastek: [1.5707814198957448, -2.304892168298706]'"

dla x1=-1 i x2=0.25

[-1.0642857142857143 0.1285714285714286]
[-0.9882413732658417 -0.041127807045914566]
[-0.68995037923022 -0.5987648465831944]
[-0.012635611298492222 -0.6514670268233802]
[0.6543036057064445 0.6539861752005263]
[-0.06565776713529803 0.5789011367729759]
[-0.6023343695832487 -0.5951105769940852]
[0.05962886349665608 -0.44233426993587777]
[0.4186137245525588 0.4145724869016827]
[-0.031039129251535957 0.13199212793746262]
[-0.11802725780008667 -0.11774593357068736]
[0.0007241367500920581 -0.002594467618342408]
[0.0023704439439994496 0.0023704409744387143]
[-5.489996130446373e-9 2.1149166751655707e-8]
[-1.9027973487215778e-8 -1.902797348721577e-8]
"Pierwiastek: [-5.489996130446373e-9, 2.1149166751655707e-8]'"

4.2. Układ równań liniowych

Układ równań liniowych o n równań i n niewiadomych: \[a_{11}x_1+a_{12}x_2+...+a_{1n}x_n=b_1\] \[a_{21}x_1+a_{22}x_2+...+a_{2n}x_n=b_2\] \[...+...+...+...=...\] \[a_{n1}x_1+a_{n2}x_2+...+a_{nn}x_n=b_n\]

możemy przedstawić w postaci macierzowej:

\[\begin{pmatrix}a_{11} & a_{12} & ... & a_{1n}\\ a_{21} & a_{22} & ... & a_{2n} \\ ... & ... & ... & ...\\a_{n1} & a_{n2} & ... & a_{nn}\end{pmatrix} \begin{pmatrix}x_1\\x_2\\...\\x_n\end{pmatrix}=\begin{pmatrix}b_1\\b_2\\...\\b_n\end{pmatrix}\]

Z kolei tę reprezentację możemy przedstawić za pomocą wersji uproszczonej: \[Ax=b\]

4.2.1. Układy proste w rozwiązywaniu

Wśród systemów liniowych możemy wyróżnić takie, których znalezienie rozwiązania \(x=A^{-1}b\) jest proste.

Macierz współczynników ma postać diagonalną: \[\begin{pmatrix}a_{11} & 0 & ... & 0\\ 0 & a_{22} & ... & 0 \\ ... & ... & ... & ...\\0 & 0 & ... & a_{nn}\end{pmatrix} \begin{pmatrix}x_1\\x_2\\...\\x_n\end{pmatrix}=\begin{pmatrix}b_1\\b_2\\...\\b_n\end{pmatrix}\]

To wtedy rozwiązaniem jest wektor: \[\begin{pmatrix}x_1\\x_2\\...\\x_n\end{pmatrix}=\begin{pmatrix}\frac{b_1}{a_{11}}\\\frac{b_2}{a_{22}}\\...\\\frac{b_n}{a_{nn}}\end{pmatrix}\]

Macierz współczynników ma postać dolno-trójkątną \[\begin{pmatrix}a_{11} & 0 & ... & 0\\ a_{21} & a_{22} & ... & 0 \\ ... & ... & ... & ...\\a_{n1} & a_{n2} & ... & a_{nn}\end{pmatrix} \begin{pmatrix}x_1\\x_2\\...\\x_n\end{pmatrix}=\begin{pmatrix}b_1\\b_2\\...\\b_n\end{pmatrix}\]

To wtedy rozwiązaniem jest wektor, który uzyskuje się przez zastosowanie algorytmu zastąpienia w przód (ang. forward substitution):

\[\begin{pmatrix}x_1\\x_2\\...\\x_n\end{pmatrix}=\begin{pmatrix}\frac{b_1}{a_{11}}\\\frac{b_2-a_{21}x_1}{a_{22}}\\...\\\frac{b_n-a_{n1}x1-a_{n2}x2-...}{a_{nn}}\end{pmatrix}\]

Macierz współczynników ma postać górno-trójkątną \[\begin{pmatrix}a_{11} & a_{12} & ... & a_{1n}\\ 0 & a_{22} & ... & a_{2n} \\ ... & ... & ... & ...\\0 & 0 & ... & a_{nn}\end{pmatrix} \begin{pmatrix}x_1\\x_2\\...\\x_n\end{pmatrix}=\begin{pmatrix}b_1\\b_2\\...\\b_n\end{pmatrix}\]

To wtedy rozwiązaniem jest wektor, który uzyskuje się przez zastosowanie algorytmu zastąpienia w tył (ang. back substitution): \[\begin{pmatrix}x_n\\...\\x_2\\x_1\end{pmatrix}=\begin{pmatrix}\frac{b_n}{a_{nn}}\\...\\\frac{b_2-...-a_{2n}x_n}{a_{22}}\\\frac{b_1-a_{12}x_2-...-a_{1n}x_n}{a_{11}}\end{pmatrix}\]

Macierz współczynników ma postać permutacyjnej macierzy dolno-trójkątnej, bądź górno-trójkątnej

\[\begin{pmatrix}a_{11} & 0 & ... & 0\\ ... & ... & ... & ...\\ a_{n1} & a_{n2} & ... & a_{nn}\\a_{21} & a_{22} & ... & 0 \end{pmatrix} \begin{pmatrix}x_1\\...\\x_n\\x_2\end{pmatrix}=\begin{pmatrix}b_1\\b_n\\...\\b_2\end{pmatrix}\]

Znając wektor permutacji macierzy, możemy znaleźć jej postać dolno-trójkątną, bądź górno-trójkątną. Następnie stosujemy algorytm zastąpienia w przód, bądź w tył.

Uwaga: W algorytmach zastąpienia w przód i w tył mamy dzielenie przez współczynniki znajdujące się na przekątnej macierzy. One muszą być różne od zera, aby rozwiązanie istniało. No i oczywiście macierz odwrotna musi też istnieć.

4.2.2. Metoda Banachiewicza zwana także metodą LU (ang. lower-upper, pl. dolno-górna)

Metoda polega na rozłożeniu na czynniki macierzy tj. znalezieniu dolno-trójkątnej (L) i górno-trójkątnej (U) macierzy, których iloczyn daje macierz współczynników głównego równania: \[A=LU\]

Nasz układ równań możemy przedstawić w postaci: \[Lz=b,\text{szukamy z}\] \[Ux=z,\text{szukamy x}\] \[z=L^{-1}b\] \[Ux=L^{-1}b\] \[LUx=b\]

\[L=\begin{pmatrix}l_{11} & 0 & ... & 0 \\ l_{21} & l_{22} & ... & 0 \\ ... & ... & ... & ...\\l_{n1} & l_{n2} & ... & l_{nn}\end{pmatrix} \]

\[U=\begin{pmatrix}u_{11} & u_{12} & ... & u_{1n}\\ 0 & u_{22} & ... & u_{2n} \\ ... & ... & ... & ...\\0 & 0 & ... & u_{nn}\end{pmatrix} \]

Twierdzenie: jeśli każdy wiodący minor główny macierzy A stopnia n jest różny od zera (czyli taki minor jest nieosobliwy), to macierz A jest rozkładalna na czynniki L i U.

\[A_k=\begin{pmatrix}a_{11} & a_{12} & ... & a_{1k} \\ a_{21} & a_{22} & ... & a_{2k} \\ ... & ... & ... & ...\\a_{k1} & a_{k2} & ... & a_{kk}\end{pmatrix},1\le k \le n \]

Jeśli macierz posiada taki rozkład, to nie znaczy jeszcze, że ten rozkład jest jednoznaczny. Znajdowanie rozkładu zaczynamy od przypisania wartości niezerowych współczynnikom na przekątnej w tylko jednej macierzy np. dla \(l_{ii}=1, \text{ dla } 1\leq i\leq n\) - wtedy taką macierz nazywamy jednostkową macierzą dolno-trójkątną, dla \(u_{ii}=1, \text{ dla } 1\leq i\leq n\) - jednostkowa macierz górno-trójkątna.

Podstawowe wzory do zbudowania algorytmu:
\[a_{kk}=\sum_{s=1}^{k-1} l_{ks}u_{sk}+l_{kk}u_{kk}\] \[a_{kj}=\sum_{s=1}^{k-1} l_{ks}u_{sj}+l_{kk}u_{kj}\] \[a_{ik}=\sum_{s=1}^{k-1} l_{is}u_{sk}+l_{ik}u_{kk}\]

  • jeśli \(l_{kk}\neq0\) to obliczamy ukj z drugiego równania a lik z trzeciego: \[u_{kj} = a_{kj}-\sum_{s=1}^{k-1} l_{ks}u_{sj},\text{ dla } k\leq j\leq n\] \[l_{ik} = \frac{a_{ik}-\sum_{s=1}^{k-1} l_{is}u_{sk}}{u_{kk}},\text{ dla } (k+1)\leq i \leq n\]
  • jeśli \(u_{kk}\neq0\) to obliczamy lik z trzeciego równania z ukj z drugiego: \[l_{ik}=a_{ik}-\sum_{s=1}^{k-1} l_{is}u_{sk},\text{ dla }k\leq i \leq n\] \[u_{kj}=\frac{a_{kj}-\sum_{s=1}^{k-1} l_{ks}u_{sj}}{l_{kk}},\text{ dla } (k+1)\leq j\leq n\]
  • jeśli macierz jest symetryczna i dodatnio określona to lkk=ukk i lks=usk: \[l_{kk}=\sqrt{a_{kk}-\sum_{s=1}^{k-1} l_{ks}^2}\]

    \[l_{ik} = \frac{a_{ik}-\sum_{s=1}^{k-1} l_{is}l_{ks}}{l_{kk}},\text{ dla } (k+1)\leq i \leq n\]

Algorytm oparty na jednostkowej macierzy dolno-trójkątnej L nazywamy metodą Doolittle‘a, a oparty na jednostkowej macierzy górno-trójkątnej U metodą Crouta. Gdy L=UT to macierz jest symetryczna, a więc zachodzi równość lks=usk, dla dowolnych k i s - algorytm oparty na takiej własności nazywamy metodą Choleskiego. Aby zastosować metodę Choleskiego macierz oprócz tego, że musi być symetryczna, to musi być także dodatnio określona tj. \(x^TAx\geq0,\text{ dla wszystkich } x\in\mathbb R^n\smallsetminus \{0\}\)

Przykład macierzy symetrycznej, która nie jest dodatnio określona:
\[\begin{pmatrix}1 & 2 \\ 2 & 1\end{pmatrix}\] \[\begin{pmatrix}-1 & 1\end{pmatrix}\begin{pmatrix}1 & 2 \\ 2 & 1\end{pmatrix}\begin{pmatrix}-1 \\ 1\end{pmatrix}=-2<0\]

Twierdzenie: Jeśli macierz A jest rzeczywista, symetryczna i dodatnio określona, to posiada jednoznaczny rozkład na czynniki L i U \(A=LU=LL^T\), gdzie L jest dolno-trójkątna z dodatnimi współczynnikami na przekątnej

Kryterium Sylvestera: macierz symetryczna A jest dodatnio określona wtedy i tylko wtedy, gdy wszystkie jej wiodące minory główne są dodatnie

using LinearAlgebra
isposdef(A) # funkcja do sprawdzania, czy macierz A jest dodatnio określona
issymmetric(A) # funkcja do sprawdzania, czy macierz A jest symetryczna

Informacje o metodach do obliczeń rozkładu na czynniki macierzy współczynników w dokumentacji Julki: https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#man-linalg-factorizations

4.2.3. Metoda eliminacji Gaussa

Układ równań liniowych 4x4 ma postać:

\[\begin{pmatrix}a_{11} & a_{12} & a_{13} & a_{14}\\ a_{21} & a_{22} & a_{23} & a_{24} \\ a_{31} & a_{32} & a_{33} & a_{34}\\a_{41} & a_{42} & a_{43} & a_{44}\end{pmatrix} \begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}=\begin{pmatrix}b_{1}\\b_{2}\\b_{3}\\b_{4}\end{pmatrix}\]

Skrócona postać macierzowa: \(Ax=b\)

Przykład:

\[\begin{pmatrix}3 & 2 & 1 & 2\\ 6 & 8 & 1 & -1 \\ -3 & 2 & 2 & -2\\9 & 2 & 6 & 1\end{pmatrix} \begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}=\begin{pmatrix}1\\3\\2\\-1\end{pmatrix}\]

W metodzie eliminacji Gaussa naszym celem jest, poprzez zastosowanie działań elementarnych na wierszach macierzy współczynników A, znaleźć rozkład na czynniki \(A=LU\), by rozwiązać układ równań liniowych \(LUx=b\) w takiej postaci: \(Ux=L^{-1}b\)

Działania elementarne na wierszach macierzy to (Ai i Aj oznaczają wiersze macierzy A):

  1. Zamienianie miejscami wierszy \(A_i \leftrightarrow A_j\)
  2. Mnożenie współczynników wiersza macierzy przez niezerową liczbę \(\lambda A_i \rightarrow A_i\)
  3. Dodawanie do danego wiersza drugi przemnożony przez niezerową liczbę \(A_j + \lambda A_i \rightarrow A_j\)

W tej najprostszej postaci metody wykorzystamy tylko działanie elementarne nr 3. Działamy na wierszach. Pierwszy wiersz pozostaje niezmieniony. Pozostałe wiersze tak modyfikujemy, aby wyzerować kolumnę nr 1. Krok nr 1: \(A_2-2A_1\); \(A_3-(-1A_1)\);\(A_4-3A_1\); Te działania dotyczą także wektora współczynników b. \[\begin{pmatrix}3 & 2 & 1 & 2\\ 0 & 4 & -1 & -5 \\ 0 & 4 & 3 & 0\\0 & -4 & 3 &-5\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}=\begin{pmatrix}1\\1\\3\\-4\end{pmatrix}\]

Element a11=3 nazywamy współczynnikiem prowadzącym (ang. pivot element). Wiersz A1 nazywamy wierszem prowadzących (ang. pivot row). Współczynniki, przez które przemnażane są wiersze, nazywamy mnożnikami (ang. multipliers)

Krok nr 2. \(A_3-1A_2\); \(A_4-(-1A_2)\); \[\begin{pmatrix}3 & 2 & 1 & 2\\ 0 & 4 & -1 & -5 \\ 0 & 0 & 4 & 5\\0 & 0 & 2 &-10\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}=\begin{pmatrix}1\\1\\2\\-3\end{pmatrix}\]

Element a22=4 jest współczynnikiem prowadzącym.

Krok nr 3. \(A_4-0.5A_3\)

\[\begin{pmatrix}3 & 2 & 1 & 2\\ 0 & 4 & -1 & -5 \\ 0 & 0 & 4 & 5\\0 & 0 & 0 &-12.5\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\\x_4\end{pmatrix}=\begin{pmatrix}1\\1\\2\\-4\end{pmatrix}\]

Element a33=4 jest współczynnikiem prowadzącym. W kroku nr 3 uzyskaliśmy macierz górno-trójkątną.

\[U=\begin{pmatrix}3 & 2 & 1 & 2\\ 0 & 4 & -1 & -5 \\ 0 & 0 & 4 & 5\\0 & 0 & 0 &-12.5\end{pmatrix}\]

Macierz dolno-trójkąta jest złożona ze współczynników, przez które przemnażane były wiersze: \[L=\begin{pmatrix}1 & 0 & 0 & 0\\ 2 & 1 & 0 & 0 \\ -1 & 1 & 1 & 0\\3 & -1 & 0.5 &1\end{pmatrix}\]

\(A=LU\):

\[\begin{pmatrix}3 & 2 & 1 & 2\\ 6 & 8 & 1 & -1 \\ -3 & 2 & 2 & -2\\9 & 2 & 6 & 1\end{pmatrix}=\begin{pmatrix}1 & 0 & 0 & 0\\ 2 & 1 & 0 & 0 \\ -1 & 1 & 1 & 0\\3 & -1 & 0.5 &1\end{pmatrix}\begin{pmatrix}3 & 2 & 1 & 2\\ 0 & 4 & -1 & -5 \\ 0 & 0 & 4 & 5\\0 & 0 & 0 &-12.5\end{pmatrix}\]

A=[3 2 1 2;6 8 1 -1;-3 2 2 -2;9 2 6 1]
U=[3 2 1 2;0 4 -1 -5;0 0 4 5;0 0 0 -12.5]
L=[1 0 0 0;2 1 0 0;-1 1 1 0; 3 -1 0.5 1]
L*U == A

Nasz algorytm składa się z następujących kroków: \[\begin{pmatrix}a_{11} & a_{12} & a_{13} & a_{14}\\ a_{21} & a_{22} & a_{23} & a_{24} \\ a_{31} & a_{32} & a_{33} & a_{34}\\a_{41} & a_{42} & a_{43} & a_{44}\end{pmatrix}\]

function gauss(n,A)
    # dla k od 1 do n-1:
    #     dla i od k+1 do n
    #         z = u_{ik}/u_{kk} 
    #         u_{ik} = 0
    #         dla j od k+1 do n
    #               u_{ij}=u_{ij}-z*u_{kj}
    return U
end

Niezerowe współczynniki prowadzące stanowią niejedyne ograniczenie w stosowaniu wyżej opisanej metody.

Współczynnik prowadzący jest 0:
\[\begin{pmatrix}0 & 1 \\ 1 & 1 \end{pmatrix}\begin{pmatrix}x_1\\x_2\end{pmatrix}=\begin{pmatrix}1\\2\end{pmatrix}\] Współczynnik prowadzący jest bliski zeru:

\[\begin{pmatrix}\epsilon & 1 \\ 1 & 1 \end{pmatrix}\begin{pmatrix}x_1\\x_2\end{pmatrix}=\begin{pmatrix}1\\2\end{pmatrix}\]

Po zastosowaniu metody eliminacji Gaussa otrzymamy: \[\begin{pmatrix}\epsilon & 1 \\ 0 & 1-\epsilon^{-1} \end{pmatrix}\begin{pmatrix}x_1\\x_2\end{pmatrix}=\begin{pmatrix}1\\2-\epsilon^{-1}\end{pmatrix}\]

Rozwiązaniem analitycznym jest: \(x_2=\frac{2-\epsilon^{-1}}{1-\epsilon^{-1}}\approx 1\) \(x_1=\frac{1-x_2}{\epsilon}\approx 1\)

Ale rozwiązaniem komputerowym dla \(\epsilon < eps(Float64)\) będzie: \(x_2=\frac{2-\epsilon^{-1}}{1-\epsilon^{-1}}= 1\); \(x_1=\frac{1-x_2}{\epsilon}= 0\)

Problem generuje nam \(\epsilon^{-1}\), które to wyrażenie jest bardzo dużą liczbą. Problem jest związany z dokładnością reprezentacji liczb podczas ich odejmowania, tj. \(2-\epsilon^{-1}\) i \(1-\epsilon^{-1}\)

 Int64(0b10000110110)-1023 # –> 55
 bitstring(-1/0.00000000000000002) == bitstring(2-1/0.00000000000000002) # –> true
 Int64(0b10000110101)-1023 # –> 54
 bitstring(-1/0.00000000000000005) == bitstring(2-1/0.00000000000000005) # –> true

 Int64(0b10000110100)-1023 # –> 53
 bitstring(-1/0.00000000000000009) == bitstring(2-1/0.00000000000000009) # –> false

 Int64(0b10000110011)-1023 # —> 52
 bitstring(-1/0.0000000000000002) == bitstring(2-1/0.0000000000000002) # –> false


bitstring(-0.5e17) == bitstring(2-0.5e17) # –> true
bitstring(-0.5e16) == bitstring(2-0.5e16) # –> false

bitstring(-1/0.00000000000000002)
bitstring(-0.5e17)
# "1100001101100110001101000101011110000101110110001010000000000000"
bitstring(2-1/0.00000000000000002)
bitstring(2-0.5e17)
# "1100001101100110001101000101011110000101110110001010000000000000"

# Komputer odejmując dwie liczba sprowadza je do tej samej unormowanej
# reprezentacji naukowej, czyli mamy takie działanie: 
0.00000000000000002*1e17 - 0.5*1e17 # –> -0.5e17 
# Dostajemy taki wynik, gdyż liczba 2 w tej reprezentacji jest poza zakresem
# dokładności reprezentowania liczby zminnoprzecinkowej i dlatego nam się zeruje
# podczas próby odejmownia.

# W tym przypadku liczba 2 nie będzie zerowana, gdyż jest w zakresie dokładności:
# eps(Float64) –> 2.220446049250313e-16
0.0000000000000002*1e16 - 0.5*1e16 # –> -0.4999999999999998e16

bitstring(-1/0.0000000000000002)
bitstring(-0.5e16)
# "1100001100110001110000110111100100110111111000001000000000000000" 
bitstring(2-1/0.0000000000000002)
bitstring(2-0.5e16)
# "1100001100110001110000110111100100110111111000000111111111111110"

W celu rozwiązania tego problemu do naszego wcześniejszego algorytmu dodamy wyszukiwanie względnie największego współczynnika prowadzącego w danym wierszu. Na podstawie określenia dla każdego wiersza takiego współczynnika stworzymy wektor permutacji P, który będzie określał kolejność działań na wierszach. \(PAx=Pb\)

Nasz algorytm podzielmy na dwie części: znajdowania rozkładu na czynniki (ang. factorization) oraz znajdowania rozwiązania równania (ang. solution). Pierwsza część będzie oparta o algorytm eliminacji w przód (ang. forward elimination), a druga część o algorytm zastąpienia w tył (ang. back substitution)

W celu znalezienia wektora permutacji szukamy maksymalnego elementu w każdym wierszu: \(s_i = max{(|a_{i1}|,|a_{i2}|,...,|a_{in}|)},1 \leq i \leq n\)

a następnie szukamy względnie największych współczynników prowadzących: \(P_i=|a_{i1}|/s_i\), na których podstawie tworzymy wektor permutacji P tj. każdy kolejny element wektora P jest mniejszy od poprzedniego: \(P_1 \geq P_2 \geq ... \geq P_n\)

S=zeros(n);
for i in 1:n
    S[i]=findmax(abs.(A[i,:]))[1];
end
P=sortperm(abs.(A[:,1])./S,rev=true)

\[\begin{pmatrix}3 & 2 & 4\\ 6 & 8 & 1 \\ -6 & 2 & 2\end{pmatrix} \begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}1\\2\\3\end{pmatrix}\]

A=[3 2 4;6 8 1;-6 2 2]
P=[1;2;3]
S=zeros(n);
  for i in 1:n
        S[i]=findmax(abs.(A[i,:]))[1]; # –> S={4,8,6}
  end
abs.(A[:,1])./S # –> 0.75,0.75,1.0
pivot_list=sortperm(abs.(A[:,1])./S,rev=true) # –> P={3,1,2}

# pierwszy wiersz prowadzący to 3; podmeniamy odpowiednio w P wiersz 1 z wierszem 3
P=[3;2;1]

Zgodnie w wektorem permutacji naszym pierwszym wierszem prowadzącym jest wiersz nr 3.

  1. \(A_2-(-1A_3),A_1-(-0.5A3)\)

\[\begin{pmatrix}0 & 3 & 5\\ 0 & 10 & 3 \\ -6 & 2 & 2\end{pmatrix} \begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}2.5\\5\\3\end{pmatrix}\]

Kolejnym wierszem jest wiersz nr 1

S = [4;8;6]
P=[3;2;1]
A=[0 3 5;0 10 3;-6 2 2]
abs.(A[P[2:3],2])./S[P[2:3]] # –> 1.25,0.75
pivot_list=sortperm(abs.(A[P[2:3],1])./S[P[2:3]],rev=true) # –> P={1,2}
# drugi wiersz prowadzący to 1; podmeniamy odpowiednio w P wiersz 2 z wierszem 1
P=[3;1;2]
  1. \(A_2-10/3A_1)\)

\[\begin{pmatrix}0 & 3 & 5\\ 0 & 0 & -41/3 \\ -6 & 2 & 2\end{pmatrix} \begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}2.5\\-10/3\\3\end{pmatrix}\]

Znaleźliśmy macierz permutacyjną górno-trójkątną U: \[U=\begin{pmatrix}0 & 3 & 5\\ 0 & 0 & -41/3 \\ -6 & 2 & 2\end{pmatrix}\]

Macierz permutacyjną dolno-trójkątną stanowią mnożniki: \[L=\begin{pmatrix}-0.5 & 1 & 0\\ -1 & 10/3 & 1 \\ 1 & 0 & 0\end{pmatrix}\]

czyli mamy: \[PA= \begin{pmatrix}-6 & 2 & 2\\ 3 & 2 & 4 \\ 6 & 8 & 1\end{pmatrix} = \begin{pmatrix}1 & 0 & 0\\ -0.5 & 1 & 0 \\ -1 & 10/3 & 1\end{pmatrix}\begin{pmatrix}-6 & 2 & 2\\ 0 & 3 & 5 \\ 0 & 0 & -41/3\end{pmatrix}\] \[Ux=L^{-1}b = \begin{pmatrix}-6 & 2 & 2\\ 0 & 3 & 5 \\ 0 & 0 & -41/3\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}1 & 0 & 0\\ -0.5 & 1 & 0 \\ -1 & 10/3 & 1\end{pmatrix}^{-1}\begin{pmatrix}3\\1\\2\end{pmatrix}\]

Sprawdzenie zgodności obliczenia rozkładu na czynniki L i U:

A=[-6.0 2.0 2.0;3.0 2.0 4.0;6.0 8.0 1.0];
B=[3,1,2];
L=[1 0 0;-0.5 1 0;-1 10/3 1];
U=[-6 2 2;0 3 5;0 0 -41/3];
L*U
3×3 Matrix{Float64}:
 -6.0  2.0  2.0
  3.0  2.0  4.0
  6.0  8.0  1.0

Sprawdzenie zgodności obliczeń współczynników b

B=[3,1,2] ;
L=[1 0 0;-0.5 1 0;-1 10/3 1];
inv(L)*B
3-element Vector{Float64}:
  3.0
  2.5
 -3.333333333333334

Pseudokod:

  function factorization(n,A)
        for i in 1:n
            S[i]=findmax(abs.(U[i,:]))[1];
        end
        P=sortperm(abs.(A[:,1])./S,rev=true)
          # dla k od 1 do n-1:
          #     dla i od k+1 do n
          #         z = u_{pi,k}/u_{pk,k} 
          #         zapisujemy do tablicy mnożniki zamiast 0
          #         u_{pi,k} = z
          #         dla j od k+1 do n
          #                 u_{pi,j}=u_{pi,j}-z*u_{pk,j}

        return U,P
  end

  function solution(n,U,P,B)
        # dla k od 1 do (n-1)
        #     dla i od (k+1) do n
        #         b_{pi}=b_{pi}-u{pi,k}*b_{pk}
      # stosujemy algorymt zliczania w tył:       
        # x_{n}=b_{pn}/u_{pn,n}
        # dla i od (n-1), o krok -1, do 1
        #     x_{i}=(b_{pi}-sum(u_{pi,j}*x_{j} dla j od (i+1) do n)/u_{pi,i}

        return X
  end

function gausspivot(n,A,B)
    U,P=factorization(n,A);
    X=solution(n,U,P,B);
    return X
end

4.2.4. Metody iteracyjne

Problem znajdowania rozwiązania równań liniowych definiowaliśmy jako: \(Ax=b\). W metodach iteracyjnych modyfikujemy naszą definicje, dodając do niej macierz dzieląca (ang. splitting matrix): \(Qx = (Q-A)x + b\), które jest równoznaczne z podstawą definicją, gdyż \(Qx = Qx-Ax + b \rightarrow Ax=b\)

W postaci iteracyjnej nasza nowa definicja problemu wygląda następująco: \[Qx^{(k)} = (Q-A)x^{(k-1)} + b\,\text{ dla }k\geq 1 \]

Po dwóch stronach równania otrzymaliśmy zmienną x. W przeciwieństwie do metody eliminacji Gaussa, która umożliwia otrzymanie rozwiązania ścisłego, to w metodach iteracyjnych uzyskujemy rozwiązanie przybliżone.

Wektor początkowy x0 może być dowolny. Pytanie więc, jakie warunki musi spełniać A i Q, aby dla dowolnego wektora początkowego rozwiązanie x(k) istniało, a tym samym algorytm był zbieżny: \(\lim\limits_{k\rightarrow \infty}||x^{(k)}-x||=0\)

Po pierwsze macierze Q i A muszą być nieosobliwe, tj. istnieją dla nich macierze odwrotne. Kolejny warunek jest opisany w twierdzeniu poniżej.

Gdy przemnożone zostanie równanie przez macierz odwrotną do Q, to otrzymamy: \[x^{(k)} = (I-Q^{-1}A)x^{(k-1)} + Q^{-1}b\]

Twierdzenie o zbieżności metod iteracyjnych: jeśli \(||I-Q^{-1}A||<1\) dla
pewnej podporządkowanej normy macierzowej, to dla dowolnego wektora początkowego
rezultat xk działania algorytmu zbiega do rozwiązania równania Ax=b.

Przykładowe podporządkowane normy macierzowe:
Norma L1: \(||A||_{1}=\max\limits_{1\leq j\leq n}\sum_{i=1}^{n}|a_{ij}|\)

Norma L2 - norma Euklidesowa: \(||A||_{2}=\max\limits_{1\leq i\leq n} |\sigma_{i}|=\sqrt{\rho(A^{T}A)}\), \(\rho(A^{T}A)\) - promień spektralny, czyli największa wartość własna \(\lambda\) macierzy \(A^{T}A\)

Norma L: \(||A||_{\infty}=\max\limits_{1\leq i\leq n}\sum_{j=1}^{n}|a_{ij}|\)

# l_1 
function l1(n,A)
        findmax(sum(abs.(A[i,:]) for i in 1:n))
end
# l_2
function l2(A)
        sqrt(findmax(eigvals(A'*A))[1])
end
function linf(n,A)
        # l_inf
        findmax(sum(abs.(A[:,i]) for i in 1:n))
end

Przykład dla normy L1

\[\begin{pmatrix}5 & 2 &1\\ 5 & 7 &1\\ 3 &2 &6 \end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}1\\2\\3\end{pmatrix}\]

Wiersz 1: |5|+|2|+|1|=8
Wiersz 2: |5|+|7|+|1|=13
Wiersz 3: |3|+|2|+|6|=8

\(||A||_{1} = 13\)

Norma L1 mówi, jak bardzo macierz A może rozciągnąć lub skurczyć wektory. W naszym przypadku macierz A maksymalnie może rozciągnąć wektor jednostkowy aż do długości 13.

Zbadajmy przypadek rozciągania wektorów \(e_{1}=[\frac{1}{3},\frac{1}{3},\frac{1}{3}]\) oraz \(e_{2}=[1,0,0]\)

\(y_{1}= A \cdot e_{1} = \begin{pmatrix}2.67\\4.33\\3.67\end{pmatrix}\)
\(y_{2}= A \cdot e_{2} = \begin{pmatrix}5\\5\\3\end{pmatrix}\)

\(||y_{1}||_{1} = 10.67\)
\(||y_{2}||_{1} = 13\)

W Julce normy macierzowe możemy obliczyć z wykorzystaniem opnorm: https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#LinearAlgebra.opnorm

4.2.5. Metoda iteracyjna: metoda Jacobiego

Przykład: \[\begin{pmatrix}5 & 2 &1\\ 5 & 7 &1\\ 3 &2 &6 \end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}1\\2\\3\end{pmatrix}\]

Pytanie: czy w tym przykładzie spełnione jest twierdzenie o zbieżności metod iteracyjnych, tj. czy $||I-Q-1A||<1 ?

W metodzie Jakobiego Q jest macierz diagonalną macierzy A tzn. Q=diag(A), więc nasze równanie możemy zapisać w postaci \(Qx = (Q-A)x + b\):

\[\begin{pmatrix}5 & 0 &0\\ 0 & 7 &0\\ 0 &0 &6 \end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\Biggl[\begin{pmatrix}5 & 0 &0\\ 0 & 7 &0\\ 0 &0 &6 \end{pmatrix}-\begin{pmatrix}5 & 2 &1\\ 5 & 7 &1\\ 3 &2 &6 \end{pmatrix}\Biggr]\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}+\begin{pmatrix}1\\2\\3\end{pmatrix}\]

\[\begin{pmatrix}5 & 0 &0\\ 0 & 7 &0\\ 0 &0 &6 \end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\Biggl[\begin{pmatrix}0 & -2 &-1\\ -5 & 0 &-1\\ -3 &-2 &0 \end{pmatrix}\Biggr]\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}+\begin{pmatrix}1\\2\\3\end{pmatrix}\]

\[x_{1}^{(k)}=\frac{1}{5}-\frac{2}{5}x_{2}^{(k-1)}-\frac{1}{5}x_{3}^{(k-1)}\] \[x_{2}^{(k)}=\frac{2}{7}-\frac{5}{7}x_{1}^{(k-1)}-\frac{1}{7}x_{3}^{(k-1)}\] \[x_{3}^{(k)}=\frac{3}{6}-\frac{3}{6}x_{1}^{(k-1)}-\frac{2}{6}x_{2}^{(k-1)}\]

A=[5 2 1;5 7 1;3 2 6];
Q=one(A) .* diag(A);

Po przekształceniach do postaci \(x = (I-Q^{-1}A)x + Q^{-1}b\) mamy: \[\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\Biggl[\begin{pmatrix}1 & 0 &0\\ 0 & 1 &0\\ 0 &0 &1 \end{pmatrix}-\begin{pmatrix}1/5 & 0 &0\\ 0 & 1/7 &0\\ 0 &0 &1/6 \end{pmatrix}\begin{pmatrix}5 & 2 &1\\ 5 & 7 &1\\ 3 &2 &6 \end{pmatrix}\Biggr]\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}+\begin{pmatrix}1/5 & 0 &0\\ 0 & 1/7 &0\\ 0 &0 &1/6 \end{pmatrix}\begin{pmatrix}1\\2\\3\end{pmatrix}\]

Sprawdzamy, czy spełnione jest twierdzenie o zbieżności metod iteracyjnych:

Ione = one(A);
Qinv=inv(Q);
linf(3,Ione-Qinv*A)[1]<1 # true

\[I-Q^{-1}A = \begin{pmatrix}0 & -2/5 &-1/5\\ -5/7 & 0 & -1/7\\ -3/6 &-2/6 &0\end{pmatrix}\]

Zgodnie z twierdzeniem o zbieżności metod iteracyjnych stwierdzanym, że rezultat działania algorytmu będzie zbiegał do rozwiązania równania Ax=b dla dowolnych warunków początkowych.

Działanie algorytmu możemy rozpocząć od wybrania losowych wartości początkowych \(x_{1}^{(0)}\) i \(x_{2}^{(0)}\), \(x_{3}^{(0)}\) . Rezultatem działania algorytmu jest wektor \((x_{1}^{(k)},x_{2}^{(k)},x_{3}^{(k)})^{T}\), który reprezentuje przybliżone rozwiązanie układu tj. takie, którego odległość od rozwiązania dla k-1 jest mniejsza od oczekiwanej dokładności \(\epsilon\): \(||x^{(k)}-x^{(k-1)}||<\epsilon\)

function jakobi(n,A,B,X,M,Eps)
    # n - wymiar macierzy
    # A - mecierz współczynników równania liniowego
    # B - wektor wyrazów wolnych 
    # X - wektor niewiadomych
    # M - maksymalna ilość iteracji
    # Eps - dopuszczalny błąd
    # dla k od 1 do M
    #     dla i od 1 do n
    #         U_i = (B_i-sum(A_{ij}*X_j dla j od 1 do n jeśli j różne od i))/A_{i,i}
    #     jeśli sqrt(sum((X-U).^2))<Eps) zwróc X
    #     dla i od 1 do n
    #         X_i = U_i
    # zwróć "Algorithm failed to converge"
end

Poniższe twierdzenie jest wersją twierdzenia o zbieżności metod iteracyjnych dla metody Jakobiego:

Twierdzenie o zbieżności metody Jakobiego: Jeśli macierz A jest diagonalnie
dominująca, to rezultat działania metody Jakobiego zbiega do rozwiązania Ax=b dla
dowolnej wartości początkowej.

Macierz diagonalnie dominująca to taka, dla której spełniona jest nierówność: \[|a_{ii}|>\sum_{j=1,j\neq i}^{n} |a_{ij}|,\, \text{dla } 1\leq i\leq n\]

Aby uniknąć dzielenia w algorytmie opisanym przez wzory na początku tego podrozdziału, możemy przekształcić nasze równanie, przemnażając obie strony przez macierz odwrotną D=diag(A) tj: \(D^{-1}Ax=D^{-1}b\)

A=[5 2 1;5 7 1;3 2 6];
D=one(A) .* diag(A);
Dinv=inv(D);
Dinv*A

\(D^{-1}Ax=D^{-1}b\rightarrow\begin{pmatrix}1 & 2/5 &1/5\\ 5/7 & 1 & 1/7\\ 3/6 &2/6 &1\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}1/5\\2/7\\3/6\end{pmatrix}\)

Dzięki temu, że na przekątnej są wartości 1, to do algorytmu nie musimy dodawać operacji dzielenia podczas obliczania x. W celu zrealizowania tego przemnożenia przez macierz odwrotną do Q=diag(A), to do naszego algorytmu dodajemy poniższy kod i odpowiednio modyfikujemy pozostałą część. W algorytmie nadal jest operacja dzielenia, ale w wyrażeniu o wiele mniej złożonym.

# dla i od 1 do n
#     d=1/A_{i,i}
#     B_i=d*B_i;
#     dla j od 1 do n
#         A_{i,j}=d*A_{i,j};

4.2.6. Metoda iteracyjna: metoda Gaussa-Seidla

Ta metoda działa bardzo podobnie do metody Jakobiego. Różnica jest taka, że w metodzie Gaussa-Seidla do obliczania kolejnej niewiadomej np x2 wykorzystujemy obliczenia poprzedniej x1 oraz macierz Q jest macierzą dolno-trójkątną Q=L(A).

\[\begin{pmatrix}5 & 2 &1\\ 5 & 7 &1\\ 3 &2 &6 \end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}1\\2\\3\end{pmatrix}\]

Twierdzenie o zbieżności metody Gaussa-Seidla: Jeśli macierz A jest diagonalnie
dominująca, to rezultat działania metody Gauss-Siedla zbiega do rozwiązania Ax=b dla
dowolnej wartości początkowej.

\[\begin{pmatrix}5 & 0 &0\\ 5 & 7 &0\\ 3 &2 &6 \end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\Biggl[\begin{pmatrix}5 & 0 &0\\ 5 & 7 &0\\ 3 &2 &6 \end{pmatrix}-\begin{pmatrix}5 & 2 &1\\ 5 & 7 &1\\ 3 &2 &6 \end{pmatrix}\Biggr]\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}+\begin{pmatrix}1\\2\\3\end{pmatrix}\]

\[\begin{pmatrix}5 & 0 &0\\ 5 & 7 &0\\ 3 &2 &6 \end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}0 & -2 &-1\\ 0 & 0 &-1\\ 0 &0 &0 \end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}+\begin{pmatrix}1\\2\\3\end{pmatrix}\]

\[x_{1}^{(k)}=\frac{1}{5}-\frac{2}{5}x_{2}^{(k-1)}-\frac{1}{5}x_{3}^{(k-1)}\] \[x_{2}^{(k)}=\frac{2}{7}-\frac{5}{7}x_{1}^{(k)}-\frac{1}{7}x_{3}^{(k-1)}\] \[x_{3}^{(k)}=\frac{3}{6}-\frac{3}{6}x_{1}^{(k)}-\frac{2}{6}x_{2}^{(k)}\]

A=[5 2 1;5 7 1;3 2 6];
Q=[5 0 0;5 7 0; 3 2 6];

Po przekształceniach do postaci \(x = (I-Q^{-1}A)x + Q^{-1}b\) mamy: \[\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\Biggl[\begin{pmatrix}1 & 0 &0\\ 0 & 1 &0\\ 0 &0 &1 \end{pmatrix}-\begin{pmatrix}1/5 & 0 &0\\ -1/7 & 1/7 &0\\ -0.052381 &-0.047619 &1/6 \end{pmatrix}\begin{pmatrix}5 & 2 &1\\ 5 & 7 &1\\ 3 &2 &6 \end{pmatrix}\Biggr]\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}+\begin{pmatrix}1/5 & 0 &0\\ -1/7 & 1/7 &0\\ -0.052381 &-0.047619 &1/6 \end{pmatrix}\begin{pmatrix}1\\2\\3\end{pmatrix}\]

Ione = one(A);
Qinv=inv(Q);
linf(3,Ione-Qinv*A)[1]<1 # true
Ione-Qinv*A

\[I-Q^{-1}A = \begin{pmatrix}0 & -2/5 &-1/5\\ 0 & -2/7 & 0\\ 0 & 0.104762 &1/10\end{pmatrix}\]

Zgodnie z twierdzeniem o zbieżności metod iteracyjnych stwierdzanym, że rezultat działania algorytmu będzie zbiegał do rozwiązania równania Ax=b dla dowolnych warników początkowych.

Podobnie jak w metodzie Jakobiego, w celu uniknięcia dodawania operacji dzielenia podczas obliczania x przemnażamy przez macierz odwrotną do D=diag(A).

A=[5 2 1;5 7 1;3 2 6];
D=one(A) .* diag(A);
Dinv=inv(D);
Dinv*A

\(D^{-1}Ax=D^{-1}b\rightarrow\begin{pmatrix}1 & 2/5 &1/5\\ 5/7 & 1 & 1/7\\ 3/6 &2/6 &1\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}1/5\\2/7\\3/6\end{pmatrix}\)

Q=[1 0 0;5/7 1 0; 3/6 2/6 1];
Qinv=inv(Q)

\[\begin{pmatrix}1 & 0 &0\\ 5/7 & 1 & 0\\ 3/6 &2/6 &1\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\Biggl[\begin{pmatrix}1 & 0 &0\\ 5/7 & 1 & 0\\ 3/6 &2/6 &1\end{pmatrix}-\begin{pmatrix}1 & 2/5 &1/5\\ 5/7 & 1 &1/7\\ 3/6 &2/6 &1/6 \end{pmatrix}\Biggr]\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}+\begin{pmatrix}1/5\\2/7\\3/6\end{pmatrix}\]

\[\begin{pmatrix}1 & 0 &0\\ 5/7 & 1 & 0\\ 3/6 &2/6 &1\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=\begin{pmatrix}0& -0.4 &-0.2\\ 0 & 0 & -0.142857\\ 0 & 0 &0\end{pmatrix}\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}+\begin{pmatrix}1/5\\2/7\\3/6\end{pmatrix}\]

function gausssiedel(n,A,B,X,M,Eps)
    # dla i od 1 do n
     #     d=1/A_{i,i}
     #     B_i=d*B_i;
     #     dla j od 1 do n
     #         A_{i,j}=d*A_{i,j};
     # dla k od 1 do M
     #     dla i od 1 do n
     #         X_i = B_i-sum(A_{ij}*X_j dla j od 1 do n jeśli j różne od i)
     #     jeśli sqrt(sum((X-U).^2))<Eps) zwróc X
     #     dla i od 1 do n
     #         U_i = X_i
     # zwróć "Algorithm failed to converge"
end

4.2.7. Metoda iteracyjna: metoda najszybszego spadku

Jeśli macierz współczynników układu równań liniowych A

  • jest rzeczywista
  • kwadratowa nxn
  • symetryczna \(A^T=A\)
  • dodatnio określona \(x^TAx>0,x\neq 0\)

to problem znajdowania x spełniającego równanie Ax=b jest równoważny ze znajdowaniem x, dla którego funkcja kwadratowa \(q(x)=\langle x,Ax\rangle-2\langle x,b\rangle\) osiąga minimum. Ten związek można udowodnić obliczając gradient funkcji g(x): \(\nabla q(x) = 2Ax - 2b\). Minimum funkcji \(q(x)\) występuje gdy \(\nabla q(x) = 0\), czyli: 2Ax - 2b = 0 –> Ax=b.

Funkcja q(x) zawsze ma minimum jedno miejsce zerowe – w punkcie 0. Jeśli \(b=0\), to funkcja jest dodatnia dla każdego x, gdyż macierz A jest dodatnio określona. Gdy \(b \neq 0\), to funkcja przyjmuje wartości ujemne dla x między miejscami zerowymi. Funkcja q(x) z macierzą dodatnio określoną ma minimum globalne.

Funkcję kwadratową zdefiniowaliśmy, wykorzystując iloczyn skalarny dwóch wektorów x i y: \(\langle x,y\rangle = \sum_{i=1}^n x_iy_i\)

Własności iloczynu:

  1. Przemmienność: \(\langle x,y\rangle=\langle y,x\rangle\)
  2. Jednorodność: \(\langle ax,y\rangle=a\langle x,y\rangle,a\in \mathbb{R}\)
  3. Rozłączność: \(\langle x+y,z\rangle=\langle x,z\rangle + \langle y,z\rangle\)
  4. Dla operacji macierzowych: \(\langle x,Ay\rangle=\langle A^Tx,y\rangle\)

Przykład dla A=5 i b=2
\(5x-2=0\) -> \(x=\frac{2}{5}\)
\(q(x)=\langle x,5x\rangle-2\langle x,2\rangle=5x^2-4x\)

Funkcja kwadratowa osiąga minimum, gdy jej pochodna jest równa 0. \(g'(x)=10x-4=0\) –> \(x=\frac{2}{5}\), to rozwiązanie jest równe rozwiązaniu równania liniowego \(5x-b=0\)

Wyjaśnienie graficzne: https://www.geogebra.org/m/uqgsmvye

Przykład dla A=[5 4;4 5] i b=[2;2]
\[\begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\begin{pmatrix}x_1\\x_2\end{pmatrix}=\begin{pmatrix}2\\2\end{pmatrix}\] \[5x_1+4x_2=2, 4x_1+5x_2=2\] \[x_2=\frac{1}{2}-\frac{5}{4}x_1, 4x_1+\frac{5}{2}-\frac{25}{4}x_1=2\] \[\frac{9}{4}x_1=\frac{1}{2},x_1=\frac{2}{9},x_2=\frac{1}{2}-\frac{5}{18}=\frac{2}{9}\]

\[q(x)=\langle \begin{pmatrix}x_1\\x_2\end{pmatrix},\begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\begin{pmatrix}x_1\\x_2\end{pmatrix}\rangle-2\langle \begin{pmatrix}x_1\\x_2\end{pmatrix},\begin{pmatrix}2\\2\end{pmatrix}\rangle\] \[q(x)=\langle \begin{pmatrix}x_1\\x_2\end{pmatrix},\begin{pmatrix}5x_1+4x_2 \\ 4x_1 +5x_2\end{pmatrix}\rangle-2\langle \begin{pmatrix}x_1\\x_2\end{pmatrix},\begin{pmatrix}2\\2\end{pmatrix}\rangle\] \[q(x)=5x_1^2+4x_1x_2+5x_2^2+4x_1x_2 - 4x_1-4x_2\] \[q(x)=5x_1^2+5x_2^2+8x_1x_2- 4x_1-4x_2\] \[\frac{dq}{dx_1}=10x_1+8x_2- 4=5x_1+4x_2-2=0\] \[\frac{dq}{dx_2}=10x_1+8x_2- 4=5x_1+4x_2-2=0\]

Wyjaśnienie graficzne: https://www.geogebra.org/3d/fbfgpezb

Jak znaleźć rozwiązanie numerycznie bez liczenia pochodnej względem wektora X? Znajdźmy jednowymiarową krzywą wzdłuż badanej powierzchni funkcji. Nasza krzywa rozpoczyna się w x i zmienia się zgodnie z wektorem v, który przemnażamy z każdym krokiem przez t.

\(x^{k+1}=x^{k}+t_kv^{k}\)

Metoda najszybszego spadku bazuje na prostej intuicji: iteracyjnie poruszamy się w kierunku przeciwnym do gradientu funkcji q(x), czyli w kierunku najszybszego spadku wartości funkcji. Jest to analogiczne do schodzenia z góry po najstromszej ścieżce. Kierunek spadku v jest jest zgodny z gradientem funkcji q(x): \(v^k = −(2Ax^k - 2b) = 2(b-Ax^k)\). Dla uproszczenia przyjmujemy \(v^k = b-Ax^k = r^k\), gdzie \(r^k\) to wektor reszt (residual), reprezentujący "błąd" w przybliżeniu równania \(Ax=b\). Pozostaje wyznaczyć optymalną wielkość kroku t.

\[q(x+tv) = \langle x+tv, A(x+tv)\rangle-2\langle x+tv,b\rangle\] \[q(x+tv) = \langle x+tv, Ax+Atv)\rangle-2\langle x+tv,b\rangle\] \[q(x+tv) = \langle x,Ax+Atv\rangle+\langle tv,Ax+Atv\rangle-2\langle x+tv,b\rangle\] \[q(x+tv) = \langle Ax,x\rangle+\langle Ax,tv\rangle+\langle tv,Ax\rangle+\langle tv,Atv\rangle-2\langle x+tv,b\rangle\] \[q(x+tv) = \langle Ax,x\rangle+t\langle Ax,v\rangle+t\langle v,Ax\rangle+t^2\langle v,Av\rangle-2\langle x,b\rangle-2t\langle v,b\rangle\] \[q(x+tv) = \langle Ax,x\rangle-2\langle x,b\rangle+2t\langle Ax,v\rangle-2t\langle b,v\rangle+t^2\langle v,Av\rangle\] \[q(x+tv) = \langle Ax,x\rangle-2\langle x,b\rangle+2t\langle Ax-b,v\rangle+t^2\langle v,Av\rangle\] \[q(x+tv) = q(x)+2t\langle Ax-b,v\rangle+t^2\langle v,Av\rangle\]

Ta funkcja względem t jest funkcją kwadratową, tj. ma jedno minimum, a nie ma maksimum. Wartość parametru t, dla którego pochodna tej funkcji równa się 0 wykorzystamy do znalezienia wartości minimalnej naszej funkcji.

\[\frac{q(x+tv)}{dt}=2\langle Ax-b,v\rangle+2t\langle Av,v\rangle=0\] \[t^*=\frac{\langle b-Ax,v\rangle}{\langle Av,v\rangle}\] \[t^*=\frac{\langle v,v\rangle}{\langle Av,v\rangle}\] \[t^*=\frac{||v||^2}{\langle Av,v\rangle}\]

Dla t* funkcja q(x+tv) osiąga minimum:
\[q(x+t^*v) = q(x)+t^*[2\langle Ax-b,v\rangle+t^*\langle Av,v\rangle]\] \[q(x+t^*v) = q(x)+t^*[2\langle Ax-b,v\rangle+\langle b-Ax,v\rangle]\] \[q(x+t^*v) = q(x)-t^*\langle b-Ax,v\rangle\] \[q(x+t^*v) = q(x)-\frac{\langle b-Ax,v\rangle^2}{\langle Av,v\rangle}\]

Wyrażenie ułamkowe zawsze będzie większe od zera, czyli q(x+t*v) będzie mniejsze od q(x).

Sprawdźmy, czy dla naszych wcześniej omawiany przykładów znajdziemy wartość minimalną funkcji.

Dla A=5 i b=2 z warunkiem początkowym x=1:
\(q(1)=5\cdot 1^2-4\cdot 1=1\)
\(q(1+t^*v)=1-\frac{\langle 2-5,v\rangle^2}{\langle 5v,v\rangle}\)
\(q(1+t^*v)=1-\frac{9v^2}{5v^2}\)
\(q(1+t^*v)=1-\frac{9}{5}=-\frac{4}{5}\)

Podstawiając tą wartość do funkcji, którą wykorzystujmy do znalezienia miejsca zerowego układów równań liniowych, to otrzymamy: \(-\frac{4}{5}=5x^2-4x\), wiemy, że miejsce zerowe naszej funkcji liniowej jest dla x=2/5; gdy podstawimy tą wartość do naszego równania, to otrzymamy: \(-\frac{4}{5}=5\cdot \frac{4}{25}-4\frac{2}{5}=\frac{4}{5}-\frac{8}{5}=-\frac{4}{5}\), wszystko się zgadza.

Drugi przykład dla A=[5 4;4 5] i b=[2;2] z warunkiem początkowych x=[1;1] \[q(x)=5x_1^2+5x_2^2+8x_1x_2- 4x_1-4x_2\] \[q([1;1])=5+5+8-4-4=10\] \[q([1;1]+t^*v)=10-\frac{\langle \begin{pmatrix}2\\2\end{pmatrix}-\begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\begin{pmatrix}1\\1\end{pmatrix},\begin{pmatrix}v_1\\v_2\end{pmatrix}\rangle^2}{\langle \begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\begin{pmatrix}v_1\\v_2\end{pmatrix},\begin{pmatrix}v_1\\v_2\end{pmatrix}\rangle}\] \[q([1;1]+t^*v)=10-\frac{\langle \begin{pmatrix}-7\\-7\end{pmatrix},\begin{pmatrix}v_1\\v_2\end{pmatrix}\rangle^2}{\langle \begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\begin{pmatrix}v_1\\v_2\end{pmatrix},\begin{pmatrix}v_1\\v_2\end{pmatrix}\rangle}\]

Wektor v określa kierunek iteracji, więc powinien to być gradient spadku (gradient wzrostu $∇ q(x)), który jest równy reszcie r: \[r=b-Ax\]. To jest nasza funkcja kosztu: sprawdzamy jak daleko nasze obliczenia Ax są od oczekiwanych b. Kierunek wektor r jest zgodny z gradientem wzrostu, ale ma przeciwny zwrot. W naszym przykładzie: \[v=r=\begin{pmatrix}2\\2\end{pmatrix}-\begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\begin{pmatrix}1\\1\end{pmatrix}=\begin{pmatrix}-7\\-7\end{pmatrix}\rangle\] \[\frac{dq(x)}{x_1}(x_1=1,x_2=1)=14=-2(b-Ax)\] \[\frac{dq(x)}{x_2}(x_1=1,x_2=1)=14=-2(b-Ax)\]

\[q([1;1]+t^*v)=10-\frac{\langle \begin{pmatrix}-7\\-7\end{pmatrix},\begin{pmatrix}-7\\-7\end{pmatrix}\rangle^2}{\langle \begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\begin{pmatrix}-7\\-7\end{pmatrix},\begin{pmatrix}-7\\-7\end{pmatrix}\rangle}\] \[q([1;1]+t^*v)=10-\frac{9604}{882}=-\frac{8}{9}\]

Podstawiamy do q(x):
\(-\frac{8}{9}=5\cdot \frac{2}{9}^2+5\cdot \frac{2}{9}^2+8\cdot \frac{2}{9}\cdot \frac{2}{9}- 4\cdot \frac{2}{9}-4\cdot \frac{2}{9}\), \(-\frac{8}{9}=-\frac{8}{9}\), wszystko się zgadza.

Wzory na zbudowanie algorytmu:
\(v^{k}=b-Ax^{k}\)
\(t_k=\frac{\langle v^{k},v^{k}\rangle}{\langle Av^{k},v^{k}\rangle}\)
\(x^{k+1}=x^{k}+t_kv^{k}\)

Gdy wektor r unormujemy \(\lVert {v^{k}}\rVert = 1\), to tk zmierzymy odległość pomiędzy wektorami xk i xk+1.

Szybkość zbieżności zależy od uwarunkowania macierzy A (stosunku największej do najmniejszej wartości własnej). Dla źle uwarunkowanych macierzy, metoda najszybszego spadku może wymagać wielu iteracji, gdyż tworzy charakterystyczny wzór "zig-zag"

  1. Przykład 1: Przypadek jednowymiarowy

    Dla \(A=5\) i \(b=2\) rozwiązujemy \(5x=2\):

    Analityczne rozwiązanie: \(x = \frac{2}{5}\)

    Funkcja kwadratowa: \(q(x) = 5x^2 - 4x\)

    Rozpoczynając od \(x^0 = 1\):

    \(v^0 = 2 - 5(1) = -3\)

    \(t_0 = \frac{(-3)^2}{5(-3)^2} = \frac{1}{5}\)

    \(x^1 = 1 + \frac{1}{5}(-3) = 1 - \frac{3}{5} = \frac{2}{5}\)

    W tym prostym przypadku osiągamy dokładne rozwiązanie w jednej iteracji.

  2. Przykład 2: Przypadek dwuwymiarowy

    Dla \(A=\begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\), \(b=\begin{pmatrix}2\\2\end{pmatrix}\)

    Funkcja kwadratowa: \(q(x) = 5x_1^2 + 5x_2^2 + 8x_1x_2 - 4x_1 - 4x_2\)

    Rozpoczynając od \(x^0 = \begin{pmatrix}1 \ 1\end{pmatrix}\):

    \(v^0=r=\begin{pmatrix}2\\2\end{pmatrix}-\begin{pmatrix}5& 4 \\ 4 & 5\end{pmatrix}\begin{pmatrix}1\\1\end{pmatrix}=\begin{pmatrix}-7\\-7\end{pmatrix}\)

    \(t_0 = \frac{\langle -7, -7\rangle}{\langle A(-7,-7),(-7,-7) \rangle} = \frac{98}{882} = \frac{1}{9}\)

    \(X^1 = \begin{pmatrix}1 \\ 1\end{pmatrix} + \frac{1}{9}\begin{pmatrix}-7 \\ -7\end{pmatrix} = \begin{pmatrix}\frac{2}{9} \\ \frac{2}{9}\end{pmatrix}\)

    Do wygenerowania macierzy dodatnio określonej możemy skorzystać z formuły:

    using LinearAlgebra
    A=[1 2;2 1]; isposdef(A) == false
    A=A'*A; isposdef(A) == true
    
  3. Przykład 3: Przypadek dwuwymiarowy z macierzą diagonalną o wysokim współczynniku uwarunkowania

    Dla \(A=\begin{pmatrix}1& 0 \\ 0 & 100\end{pmatrix}\), \(b=\begin{pmatrix}1\\1\end{pmatrix}\)

    Rozpoczynając od \(x^0 = \begin{pmatrix}0 \ 0\end{pmatrix}\):

    iteracja 1:

    \(v^0=r=\begin{pmatrix}1\\1\end{pmatrix}-\begin{pmatrix}1& 0 \\ 0 & 100\end{pmatrix}\begin{pmatrix}0\\0\end{pmatrix}=\begin{pmatrix}1\\1\end{pmatrix}\)

    \(t_0 = \frac{ \langle 1, 1 \rangle}{\langle A(1,1), (1,1) \rangle} = \frac{2}{101} \approx 0.0198\)

    \(X^1 = \begin{pmatrix}0 \\ 0\end{pmatrix} + \frac{2}{101}\begin{pmatrix}1 \\ 1\end{pmatrix} = \begin{pmatrix}\frac{2}{101} \\ \frac{2}{101}\end{pmatrix}\)

    iteracja 2:

    \(v^1=r=\begin{pmatrix}1\\1\end{pmatrix}-\begin{pmatrix}1& 0 \\ 0 & 100\end{pmatrix}\begin{pmatrix}\frac{2}{101}\\ \frac{2}{101}\end{pmatrix} \approx \begin{pmatrix}0.9802\\-0.9802\end{pmatrix}\)

    \(t_1 = \frac{ \langle 0.9802, -0.9802 \rangle }{\langle A(0.9802,-0.9802), (0.9802,-0.9802) \rangle} \approx \frac{1.9216}{97.04} \approx 0.0198\)

    \(X^2 = \begin{pmatrix}\frac{2}{101} \\ \frac{2}{101}\end{pmatrix} + \frac{2}{101}\begin{pmatrix}0.9802 \\ -0.9802\end{pmatrix} = \begin{pmatrix}0.39 \\ 0.00039\end{pmatrix}\)

    Dokładne rozwiązanie:

    \(X^* = \begin{pmatrix}1 \\ 0.01 \end{pmatrix}\)

4.2.8. Problem znajdowania regresji liniowej jako problem znajdowania minimum funkcji kwadratowej

Mamy dane uczące: \({(x_{i},y_{i})}^n_{i=1}\), gdzie:

  • i – to indeks pojedynczej obserwacji (od 1 do n)
  • \(x_{i}\) – to wektor cech dla i-tej obserwacji np dla dwóch cech (xi1, xi2); wielkość tego wektora to d+1, gdzie d to wymiar wielomianu; dla regresji liniowej d=1
  • \(y_{i}\) – to wartość rzeczywista (odpowiedź) dla tej obserwacji.

chcemy dopasować model liniowy: \(\hat{y} = w^T \phi(x_{i})\), gdzie:

  • \(w \in \mathbb{R}^{d+1}\) – to wektor parametrów (współczynników regresji wraz z elementem wolnym),
  • \(\phi(x_{i}) \in \mathbb{R}^{d+1}\) – przekształcenie wejścia xi na wektor cech (wartości liczbowych używanych przez model). Dla regresji liniowej \(x_{i}=\phi(x_{i})\)
    • dla regresji liniowej z wyrazem wolnym \(\phi(x_{i}) = \begin{pmatrix} 1 \\ x_{i} \end{pmatrix}\)
    • dla regresji wielomianowej stopnia 2: \(\phi(x_{i}) = \begin{pmatrix} 1 \\ x_{i} \\ x_{i}^2 \end{pmatrix}\)
  • \(\hat{y}\) – to estymowana wartość dla xi

Minimalizujemy:

  • błąd średniokwadratowy (MSE): \(\text{J}(w)=\text{MSE}(w) = \frac{1}{n} \sum_{i=1}^n \left( \hat{y}_i - y_i \right)^2 = \frac{1}{n} \sum_{i=1}^n \left( w^T \varphi(x_i) - y_i \right)^2\); J(w) – to ogólne oznaczenie funkcji kosztu (ang. cost function lub loss function) w optymalizacji.

lub:

  • \(min_{w} \sum_{i=1}^n \left( w^T \varphi(x_i) - y_i \right)^2\)

Dla regresji liniowej:

  • \(w \in \mathbb{R}^{2}\) np. \(\alpha\) i \(\beta\)
  • \(\phi(x_{i}) \in \mathbb{R}^{2}\), \(\phi(x_{i}) = \begin{pmatrix} 1 \\ x_{i1} \end{pmatrix}\)

\(min_{\alpha, \beta} \sum_{i=1}^n \left( \alpha + \beta x_i - y_i \right)^2\)

\(\sum_{i=1}^n (\alpha + \beta x_i) - y_i)^2 = \left\lVert \begin{pmatrix} \alpha + \beta x_1) - y_1 \\ . \\ . \\ . \\ \alpha + \beta x_n) - y_n \end{pmatrix} \right\rVert_2^2 = \left\lVert \begin{pmatrix} 1 & x_{1} \\ . \\ . \\ . \\ 1 & x_{n} \end{pmatrix} \begin{pmatrix} \alpha \\ \beta \end{pmatrix} - \begin{pmatrix} y_1 \\ . \\ . \\ . \\ y_n \end{pmatrix} \right\rVert_2^2 = \left\lVert Ax - b \right\rVert_2^2\)

gdzie:

\(A = \begin{pmatrix} 1 & x_{1} \\ . \\ . \\ . \\ 1 & x_{n} \end{pmatrix}\), \(x = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}\); \(b = \begin{pmatrix} y_1 \\ . \\ . \\ . \\ y_n \end{pmatrix}\)

to nasz problem znajdowania krzywej regresji przyjmuje postać: \(min_{x} \left\lVert Ax - b \right\rVert_2^2\), \(A \in \mathbb{R}^{n \times (d+1)}\), \(x \in \mathbb{R}^{d+1}\), \(b \in \mathbb{R}^n\), \(n \ge (d+1)\).

Przejście do postaci kwadratowej: q(x)=⟨x,Ax⟩−2⟨x,b⟩ – związanie naszego problemu ze znajdowaniem globalnego minimum funkcji.

\(q(x) = \left\lVert Ax - b \right\rVert_2^2 = (Ax - b)^{T}(Ax - b)\)

Wzór skróconego mnożenia: \((u - v)^T (u - v) = u^T u - u^T v - v^T u + v^T v\)

Własność transpozycji: \((A x)^T = x^T A^T\)

\(q(x)=(Ax - b)^{T}(Ax - b) = (Ax)^T Ax - (Ax)^T b - b^T Ax + b^T b\) \(q(x)=x^T A^T Ax - x^T A^T b - b^T Ax + b^T b\)

Ponadto: \(b^T Ax\) to skalar, a skalar jest równy swojej transpozycji. \(b^T Ax = (b^T Ax)^T = x^TA^Tb\)

\(q(x)=x^T A^T Ax - x^T A^T b - x^T A^T b + b^T b\) \(q(x)=x^T A^T Ax - 2x^T A^T b + b^T b\)

\(\nabla q(x) = 2 A^T A x - 2 A^T b = 2A^T(Ax-b)\)

\(min_{x} \left\lVert Ax - b \right\rVert_2^2 \Leftrightarrow \nabla q(x) = 0\)

\(2A^T(Ax-b)=0\)

\(x=(A^TA)^{-1} (A^T b)\)

  1. Przykład dla n=3 i d=1

    \((x, y) = \left\{ (1, 2), (2, 3), (3, 5) \right\}\)

    \(A = \begin{pmatrix} 1 & 1 \\ 1 & 2 \\ 1 & 3 \\ \end{pmatrix}\)

    \(b = \begin{pmatrix} 2 \\ 3 \\ 5 \\ \end{pmatrix}\)

    \(x = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}\)

    \(q(x)=x^T A^T Ax - 2x^T A^T b + b^T b\)

    Obliczamy kolejno:

    \(A^T A = \begin{pmatrix} 1 & 1 & 1 \\ 1 & 2 & 3 \\ \end{pmatrix} \begin{pmatrix} 1 & 1 \\ 1 & 2 \\ 1 & 3 \\ \end{pmatrix} = \begin{pmatrix} 3 & 6 \\ 6 & 14 \\\end{pmatrix}\)

    \(A^T b = \begin{pmatrix} 1 & 1 & 1 \\ 1 & 2 & 3 \\ \end{pmatrix} \begin{pmatrix} 2 \\ 3 \\ 5 \\ \end{pmatrix}=\begin{pmatrix} 10 \\ 23 \\ \end{pmatrix}\)

    \(b^T b = 2^2 + 3^2 + 5^2 = 4 + 9 + 25 = 38\)

    Podstawiamy do funkcji: \(q(x) = x^T \begin{pmatrix} 3 & 6 \\ 6 & 14 \\ \end{pmatrix}x- 2 x^T \begin{pmatrix} 10 \\ 23 \\ \end{pmatrix} + 38\)

    \(q(x) = \begin{pmatrix} \alpha & \beta \end{pmatrix} \begin{pmatrix} 3 & 6 \\ 6 & 14 \\ \end{pmatrix}\begin{pmatrix} \alpha \\ \beta \end{pmatrix}- 2 \begin{pmatrix} \alpha & \beta \\ \end{pmatrix} \begin{pmatrix} 10 \\ 23 \\ \end{pmatrix} + 38\)

    \(\begin{pmatrix} \alpha & \beta \end{pmatrix} \begin{pmatrix} 3 & 6 \\ 6 & 14 \end{pmatrix}\begin{pmatrix} \alpha \\ \beta \end{pmatrix} = \begin{pmatrix} 3\alpha + 6\beta & 6\alpha + 14\beta \end{pmatrix} \begin{pmatrix} \alpha \\ \beta \end{pmatrix}= 3\alpha^2 + 6\alpha\beta + 6\alpha\beta + 14\beta^2=3\alpha^2 + 12\alpha\beta + 14\beta^2\)

    \(q(x)=3\alpha^2 + 12\alpha\beta + 14\beta^2 - 20\alpha - 46\beta + 38\)

    Interpretacja geometryczna: https://www.geogebra.org/calculator/rr2m8peh

    Rozwiązanie układu normalnego:

    \(\nabla q(x) = 2 A^T A x - 2 A^T b = 2A^T(Ax-b)=0\)

    \(A^T Ax = A^T b\)

    \(\begin{pmatrix} 3 & 6 \\ 6 & 14 \end{pmatrix} \begin{pmatrix} \alpha \\ \beta \end{pmatrix}=\begin{pmatrix} 10 \\ 23 \end{pmatrix}\)

    Obliczamy odwrotność:

    \((A^T A)^{-1} = \frac{1}{3 \cdot 14 - 6 \cdot 6} \begin{pmatrix} 14 & -6 \\ -6 & 3 \\ \end{pmatrix} = \frac{1}{6} \begin{pmatrix} 14 & -6 \\ -6 & 3 \\\end{pmatrix}\)

    I ostatecznie: \(x = (A^T A)^{-1} A^T b\)

    Obliczamy: \(x = \frac{1}{6} \begin{pmatrix} 14 & -6 \\ -6 & 3 \\ \end{pmatrix} \begin{pmatrix} 10 \\ 23 \end{pmatrix}= \frac{1}{6}\begin{pmatrix} 140 - 138 \\ -60 + 69 \end{pmatrix} = \frac{1}{6} \begin{pmatrix} 2 \\ 9 \end{pmatrix} = \begin{pmatrix} 0.\overline{3} \\ 1.5 \end{pmatrix}\)

    Ostateczny model regresji: \(\hat{y} = 0.33 + 1.5 x\)

    https://www.geogebra.org/calculator/xrdrxhrw

5. Reprezentacja funkcji

5.1. Interpolacja wielomianowa

Problem znalezienia reprezentacji funkcji sprowadza się do znalezienia wielomianu w z najniższym możliwym rzędem.

Twierdzenie: jeśli x0,x1, … , xn są różnymi liczbami
rzeczywistymi, to dla odpowiadających im wartościom reprezentowanej funkcji
y0,y1, … , yn istnieje dokładnie jeden wielomian wn o stopniu
co najwyżej n, taki że: \(w(x_{i})=y_{i},\text{ gdzie } 0\leq i \leq n\)

5.1.1. Wielomian interpolacyjny - postać potęgowa

Wielomian możemy przedstawić w postaci liniowej kombinacji wielomianów 1,x,x2 … np.
\(w(x)=a_{0}+a_{1}x+a_{2}x^2+\text{ ... }+a_{n}x^n\)

Dla warunku interpolacyjnego \(w(x_{i})=y_{i},\text{ gdzie } 0\leq i \leq n\) taki wielomian następnie możemy przedstawić w postaci macierzowej z n+1 równaniami liniowymi oraz n+1 niewiadomymi np. dla wielomianu stopnia n=2 mamy:

\(\begin{pmatrix}1 & x_{0} &x_{0}^2\\ 1 & x_{1} & x_{1}^2\\ 1 &x_{2}&x_{2}^2\end{pmatrix}\begin{pmatrix}a_0\\a_1\\a_2\end{pmatrix}=\begin{pmatrix}y_0\\y_1\\y_2\end{pmatrix}\)

5.1.2. Wielomian interpolacyjn Langrangea

\(w(x)=y_{0}l_{0}(x)+y_{1}l_{1}(x)+\text{ ... }+y_{n}l_{n}(x)=\sum\limits_{k=0}^n y_{k}l_{k}(x)\)

\(l_{i}=\prod\limits_{j=0;j\neq i}^n \frac{x-x_{j}}{x_{i}-x_{j}},\,0 \leq i \leq n\)

li - wielomian, który zależy od każdej wartości xi, a nie zależy od wartości interpolowanej funkcji. Wielomian interpolacyjny Langrangea jest sumą iloczynów wartości interpolowanej funkcji oraz wielomianów li. Inaczej powstaje wielomian interpolacyjny Newtona, omawiany w następny rozdziale – w każdym kroku iteracji powstający tam wielomian interpolacyjny Newtona zależy od wartości funkcji interpolacyjnej oraz od kolejnych wartości xi.

Przykład dla \(f(x)=x^3+5x^2-2x-5\) i wybranych punktów interpolacyjnych x0=2,x1=-1,x2=-4,x3=1
\(l_{0}=\frac{x-x_{1}}{x_{0}-x_{1}}\frac{x-x_{2}}{x_{0}-x_{2}}\frac{x-x_{3}}{x_{0}-x_{3}}=\frac{(x+1)(x+4)(x-1)}{(2+1)(2+4)(2-1)}=\frac{1}{18}(x+1)(x+4)(x-1)\)
\(l_{1}=\frac{x-x_{0}}{x_{1}-x_{0}}\frac{x-x_{2}}{x_{1}-x_{2}}\frac{x-x_{3}}{x_{1}-x_{3}}=\frac{(x-2)(x+4)(x-1)}{(-1-2)(-1+4)(-1-1)}=\frac{1}{18}(x-2)(x+4)(x-1)\)
\(l_{2}=\frac{x-x_{0}}{x_{2}-x_{0}}\frac{x-x_{1}}{x_{2}-x_{1}}\frac{x-x_{3}}{x_{2}-x_{3}}=\frac{(x-2)(x+1)(x-1)}{(-4-2)(-4+1)(-4-1)}=-\frac{1}{90}(x-2)(x+1)(x-1)\)
\(l_{3}=\frac{x-x_{0}}{x_{3}-x_{0}}\frac{x-x_{1}}{x_{3}-x_{1}}\frac{x-x_{2}}{x_{3}-x_{2}}=\frac{(x-2)(x+1)(x+4)}{(1-2)(1+1)(1+4)}=-\frac{1}{10}(x-2)(x+1)(x+4)\)

\(y_{0} = f(2)=19; y_{1}=f(-1) = 1; y_{2}=f(-4) = 19;y_{3}=f(1) = -1\)

Wielomian interpolacyjny Langrangea dla badanej funkcji ma postać:
\(w(x)=19l_{0}(x)+l_{1}(x)+19l_{2}(x)-l_{3}(x)\)

Jeśli w wielomianie Langrangea wykonamy podstawienie x=xi, to otrzymamy postać macierzową:
\(\begin{pmatrix}l_{0}(x_{0}) & l_{1}(x_{0}) &l_{2}(x_{0}) &l_{3}(x_{0})\\ l_{0}(x_{1}) & l_{1}(x_{1}) &l_{2}(x_{1}) &l_{3}(x_{1})\\ l_{0}(x_{2}) & l_{1}(x_{2}) &l_{2}(x_{2}) &l_{3}(x_{2})\\ l_{0}(x_{3}) & l_{1}(x_{3}) &l_{2}(x_{3}) &l_{3}(x_{3})\end{pmatrix}\begin{pmatrix}y_0\\y_1\\y_2\\y_3 \end{pmatrix}=\begin{pmatrix}w_{0}\\w_{1}\\w_{2}\\w_{3}\end{pmatrix}\)

W tym przypadku macierz współczynników jest macierzą jednostkową, gdyż wielomiany znajdujące się poza przekątną, zerują się (sprawdź wzory na li).

\(w_{0} = 19l_0=\frac{19}{18}(x+1)(x+4)(x-1)\)
\(w_{1} = 1l_1=\frac{1}{18}(x-2)(x+4)(x-1)\)
\(w_{2} = 19l_2=-\frac{19}{90}(x-2)(x+1)(x-1)\)
\(w_{3} = -1l_3=\frac{1}{10}(x-2)(x+1)(x+4)\)

\(w(x)=\sum\limits_{i=0}^n w_{i}\)

5.1.3. Wielomian interpolacyjny Newtona

\(0:\,w_{0}=a_{0}\)
\(1:\,w_{1}=a_{0}+a_{1}(x-x_{0})\)
\(2:\,w_{2}=a_{0}+a_{1}(x-x_{0})+a_{2}(x-x_{0})(x-x_{1})\)
\(3:\,w_{3}=a_{0}+a_{1}(x-x_{0})+a_{2}(x-x_{0})(x-x_{1})+a_{3}(x-x_{0})(x-x_{1})(x-x_{2})\)
\(n:\,w_{n}=a_{0}+a_{1}(x-x_{0})+a_{2}(x-x_{0})(x-x_{1})+\text{ ... }+a_{n}(x-x_{0})\text{ ... }(x-x_{n-1})\)

Postać uproszczona: \(w_{n}=\sum\limits_{i=0}^n a_{i}\prod\limits_{j=0}^{i-1}(x-x_{j})\)

Jeśli wykonamy podstawienie x=xi, to otrzymamy postać macierzową dolno-trójkątną: \(\begin{pmatrix}1 & 0 & 0\\ 1 & (x_{1}-x_{0}) & 0\\ 1 & (x_{2}-x_{0})& (x_{2}-x_{0})(x_{2}-x_{1})\end{pmatrix}\begin{pmatrix}a_0\\a_1\\a_2\end{pmatrix}=\begin{pmatrix}f(x_0)\\f(x_1)\\f(x_2)\end{pmatrix}\)

\(a_{0} = f(x_{0})\)
\(a_{1} = \frac{f(x_{1})-a_{0}}{x_{1}-x_{0}}=\frac{f(x_{1})-f(x_{0})}{x_{1}-x_{0}}\)

\(a_{2} = \frac{f(x_{2})-a_{0}-(x_{2}-x_{0})a_{1}}{(x_{2}-x_{0})(x_{2}-x_{1})}= \frac{f(x_{2})-f(x_{0})-(x_{2}-x_{0})\frac{f(x_1)-f(x_0)}{x_1-x_0}}{(x_{2}-x_{0})(x_{2}-x_{1})}= \frac{(x_1-x_0)(f(x_{2})-f(x_{0}))-(x_{2}-x_{0})(f(x_1)-f(x_0))}{(x_{2}-x_{0})(x_{2}-x_{1})(x_1-x_0)}\)

Do obliczania współczynników a będziemy korzystać z poniższych wzorów na ilorazy różnicowe:
\(a_0=f[x_{0}]=f(x_{0})\)
\(a_1=f[x_0,x_1] = \frac{f(x_{1})-f(x_{0})}{x_{1}-x_{0}}\)
\(a_2=f[x_0,x_1,x_2]=\frac{f[x_1,x_2]-f[x_0,x_1]}{x_2-x_0}\)

\(\frac{f[x_1,x_2]-f[x_0,x_1]}{x_2-x_0}= \frac{\frac{f(x_2)-f(x_1)}{x_2-x_1}-\frac{f(x_1)-f(x_0)}{x_1-x_0}}{x_2-x_0}= \frac{(x_1-x_0)(f(x_{2})-f(x_{0}))-(x_{2}-x_{0})(f(x_1)-f(x_0))}{(x_{2}-x_{0})(x_{2}-x_{1})(x_1-x_0)}\)

Wzór ogólny na ilorazy różnicowe:
\(f[x_i,x_{i+1},...,x_{i+j}]=\frac{f[x_{i+1},x_{i+2},...,x_{i+j}]-f[x_{i},x_{i+1},...,x_{i+j-1}]}{x_{i+j}-x_i}\)

Kolejność wykonywania operacji od lewej do prawej:

  rząd 0 rząd 1 rząd 2 rząd 3
x0 f[x0] f[x0,x1] f[x0,x1,x2] f[x0,x1,x2,x3]
x1 f[x1] f[x1,x2] f[x1,x2,x3]  
x2 f[x2] f[x2,x3]    
x3 f[x3]      

Szukany wielomian jest liniową kombinacją współczynników z pierwszego wiersza.

Przykład dla \(f(x)=x^3+5x^2-2x-5\) i wybranych punktów interpolacyjnych x0=2,x1=-1,x2=-4,x3=1

Warunki początkowe:

xi 2 -1 -4 1
f(xi) 19 1 19 -1

Obliczenia:

2 19 \(\frac{1-19}{-1-2}=6\) \(\frac{-6-6}{-4-2}=2\) \(\frac{1-2}{1-2}=1\)
-1 1 \(\frac{19-1}{-4+1}=-6\) \(\frac{-4+6}{1+1}=1\)  
-4 19 \(\frac{-1-19}{1+4}=-4\)    
1 -1      

w0 = 19
w1 = 19 + 6(x-2)
w2 = 19 + 6(x-2)+2(x-2)(x+1)
w3 = 19 + 6(x-2)+2(x-2)(x+1)+(x-2)(x+1)(x+4)

19 + 6(x-2)+2(x-2)(x+1)+(x-2)(x+1)(x+4) = \(x^3+5x^2-2x-5\) - to musi być tym samym, gdyż te wielomiany stopnia trzeciego są takie same w punktach \(w(x_{i})=y_{i}\). A dla tych punktów istnieje tylko jeden wielomian stopnia trzeciego.

Algorytm do obliczania współczynników wielomianu, gdzie A jest macierzą odpowiadającą tabeli pokazanej wyżej, tj. zawierającą wszystkie ilorazy różnicowe oraz wartości początkowe xi.

A # macierz Nx(N+1)
A[:,1]=# wartości początkowe X
A[:,2]=# wartości badanej funkcji f(x)
dla j = 3:(n+1)
        dla i = 1:(n+2-j)
            A[{i}{j}] = (A[{i+1}{j-1}] - A[{i}{j-1}])/(A[{i+j-2}{1}] - A[{i}{1}])

6. Obliczanie pochodnych

\(f'(x)\approx \frac{f(x+h)-f(x)}{h}\)

\(f'(x)\approx \frac{f(x)-f(x-h)}{h}\)

Pierwszy wykorzystujemy do obliczania pochodnej w przód (ang. forward) a drugi w tył (ang. backward).

Dla funkcji liniowych powyższy wzór jest dokładny niezależny od wartości h.

\(f(x)=kx+b\)
\(f'(x)= \frac{(k(a+h)-ka)}{h}=\frac{(ka+kh-ka)}{h}=\frac{kh}{h}=k\)

Dla pozostałych funkcji liczonych powyższymi metodami wynik jest zależny zarówno od wartości h oraz precyzji reprezentacji liczb zmiennoprzecinkowych. Do znalezienia wzoru na błąd odcięcia (ang. truncation error) skorzystamy z rozwinięcia funkcji w szereg Taylora. Ten błąd jest zależny do wartości h.

\(f(x+h)=f(x)+(x+h-x)f'(x)+\frac{(x+h-x)^2}{2}f''(\xi)\)
\(f(x+h)=f(x)+hf'(x)+\frac{h^2}{2}f''(\xi)\)

Po przekształceniu otrzymamy wzór na pochodną wraz z błędem odcięcia:

\(f'(x)=\frac{f(x+h)-f(x)}{h}-\frac{h}{2}f''(\xi)\)

\(\xi \in (x,x+h)\)

Przykład: f(x)=x2
\(f^{(1)}(x)=2x,f^{(2)}(x)=2\)
dla x=1 i h=0.01
\(f'(1)=\frac{1.01^2-1^2}{0.01}=2.01\)
błąd odcięcia: \(\frac{0.01}{2}2=0.01,\text{niezależnie do wartości }\xi\)

Funkcje x2 możemy dokładnie odwzorować za pomocą rozwinięcia w szereg Taylora aż do drugiej pochodnej.
\(f(x+h)=f(x)+hf'(x)+\frac{h^2}{2}f''(x)\)

W powyższych obliczeniach uzyskaliśmy dokładną wartość błędu odcięcia, ze względu na tę dokładność odwzorowania funkcji x2 za pomocą rozwinięcia w szereg Taylora.

Jeśli teraz spróbujemy tego samego dla wielomianu o stopniu wyższym np. x3, to będziemy musieli dodać element z trzecią pochodną, aby dokładnie odwzorować funkcję.

Przykład: f(x)=x3
\(f^{(1)}(x)=3x^2,f^{(2)}(x)=6x,f^{(3)}(x)=6\)
dla x=1 i h=0.01
\(f'(1)=\frac{1.01^3-1^3}{0.01}=3.0301\)
błąd odcięcia: \(\frac{0.01}{2}6 \cdot 1=0.03,\text{dla }\xi=x=1\)
błąd odcięcia: \(\frac{0.01}{2}6 \cdot 1.01=0.0303,\text{dla }\xi=x=1.01\)

Rzeczywisty błąd odcięcia wynosi 0.0301 i znajduje się on w przedziale (0.03,0.0303)

Funkcje x3 możemy dokładnie odwzorować za pomocą rozwinięcia w szereg Taylora aż do trzeciej pochodnej.
\(f(x+h)=f(x)+hf'(x)+\frac{h^2}{2}f''(x)+\frac{h^3}{6}f'''(x)\)

Błąd odcięcia będzie obliczony dokładnie, jeśli zsumujemy dwa ostanie człony równania dla ξ=x: \(\frac{0.01}{2}6 \cdot 1 + \frac{0.01^2}{6}6 =0.0301,\text{dla }\xi=x=1\)

Przykład: f(x)=ln(x)
\(f^{(1)}(x)=\frac{1}{x},f^{(2)}(x)=-\frac{1}{x^2}\)
dla x=2 i h=0.01
\(f'(2)=\frac{ln(2.01)-ln(2)}{0.01}=0.498754\)
błąd odcięcia : \(\frac{0.01}{2}\cdot(-\frac{1}{2^2})=-0.00125,\text{dla }\xi=x=2\)
błąd odcięcia: \(\frac{0.01}{2}\cdot(-\frac{1}{2.01^2})=-0.00123759,\text{dla }\xi=x=2.01\)

Rzeczywisty błąd odcięcia wynosi -0.001245848896 i znajduje się on w przedziale (-0.00125, -0.00123759)

Całkowity błąd obliczeń numerycznych nie tylko składa się z błędu odcięcia, który zależy od wartości h, ale również od precyzji reprezentacji liczb zmiennoprzecinkowych.

function first_deriv(n,f,x,h)
    table=zeros(n,9); fx=f(x);
    fderiv=1/x;fdderiv1=-x^(-2);fdderiv2=-(x+h)^(-2);
    table[:,4] .= fderiv; # pochodna wzorcowa
    for i in 1:n
        table[i,1:3]= [i,h,f(x+h)-fx]; # numer kroku, długość korku, różnica wartości funkcji 
        table[i,5]= table[i,3]/h; # pochdna obliczona numerycznie
        # granice błędów odcięcia oraz błąd całkowity
        table[i,6:8]= [abs(h/2*fdderiv1),abs(h/2*fdderiv2),abs(fderiv-table[i,5])]; # 
        sortset = sort([table[i,7],table[i,6]]);
        #sprawdzanie, czy błąd całkwity znajduje się w granicach błędu odcięcia
        table[i,9]= table[i,8]>sortset[1] &&  table[i,8]<sortset[2]; 
        h=h/5;
    end
    return table
end

f(x)=log(x);
table=first_deriv(25,f,2,0.1)

plot(table[:,1],table[:,8],yscale = :log10)
savefig("round_error.png")

round_error.png

Najmniejszy błąd całkowity otrzymaliśmy w kroku 11 i 12 dla h=1.024e-8 i h=2.048e-9 err=1.9979e-9

Błąd odcięcia naszej poprzedniej metody jest rzędu \(\mathcal{O}(h)\). Ponieważ h jest zawsze bardzo małe, to im wyższa potęga przy h, tym wpływ elementu błędu we wzorze na pochodną coraz mniejszy. Poprzez połączenie metod obliczania w przód i w tył znajdziemy metodę symetryczną obliczania pochodnej z błędem rzędu kwadratowego.

\(f'(x)\approx \frac{f(x+h)-f(x)+f(x)-f(x-h)}{2h}=\frac{f(x+h)-f(x-h)}{2h}\)

Do znalezienia błędu odcięcia znowu wykorzystamy rozwinięcie funkcji w szereg Taylora:
\(f(x+h)=f(x)+hf'(x)+\frac{h^2}{2}f''(x)+\frac{h^3}{6}f'''(\xi)\)
\(f(x-h)=f(x)-hf'(x)+\frac{h^2}{2}f''(x)-\frac{h^3}{6}f'''(\xi)\)

Odejmijmy stronami:
\(f(x+h)-f(x-h)=2hf'(x)+2\frac{h^3}{6}f'''(\xi)\)
\(f'(x)=\frac{f(x+h)-f(x-h)}{2h}-\frac{h^2}{6}f'''(\xi)\)

W powyższego wzoru wynika, że metoda symetryczna obliczania pochodnej jest dokładna dla funkcji nawet kwadratowych! Dla x2 trzecia pochodna jest równa 0.

\(\xi \in (x-h,x+h)\)

function first_deriv_symmetric(n,f,x,h)
    table=zeros(n,9);
    fderiv=1/x;fddderiv1=2(x-h)^(-3);fddderiv2=2(x+h)^(-3)
    table[:,4] .= fderiv; # pochodna wzorcowa
    for i in 1:n
        table[i,1:3]= [i,h,(f(x+h)-f(x-h))]; # numer kroku, długość korku, różnica wartości funkcji 
        table[i,5]= table[i,3]/(2*h); # pochdna obliczona numerycznie
        # granice błędów odcięcia oraz błąd całkowity
        table[i,6:8]= [abs(h^2/6*fddderiv1),abs(h^2/6*fddderiv2),abs(fderiv-table[i,5])];
        sortset = sort([table[i,7],table[i,6]]);
        #sprawdzanie, czy błąd całkwity znajduje się w granicach błędu odcięcia
        table[i,9]= table[i,8]>sortset[1] &&  table[i,8]<sortset[2]; 
        h=h/5;
    end
    return table
end

f(x)=log(x);
table=first_deriv_symmetric(25,f,2,0.1)

plot(table[:,1],table[:,8],yscale = :log10)
savefig("round_error2.png")

round_error2.png

Najmniejszy błąd całkowity w kroku 7 dla h=6.4e-6 err=5.7041e-12

Jeśli zamiast odejmować stronami poprzednie równania, dodamy je, to otrzymamy wzór na obliczania pochodnej drugiego rzędu:

\(f(x+h)=f(x)+hf'(x)+\frac{h^2}{2}f''(x)+\frac{h^3}{6}f'''(x)+\frac{h^4}{24}f''''(\xi)\)
\(f(x-h)=f(x)-hf'(x)+\frac{h^2}{2}f''(x)-\frac{h^3}{6}f'''(x)+\frac{h^4}{24}f''''(\xi)\)

\(f(x+h)+f(x-h)=2f(x)+2\frac{h^2}{2}f''(x)+2\frac{h^4}{24}f''''(\xi)\)
\(h^2f''(x)=f(x+h)+f(x-h)-2f(x)-\frac{h^4}{12}f''''(\xi)\)
\(f''(x)=\frac{1}{h^2}(f(x+h)+f(x-h)-2f(x))-\frac{h^2}{12}f''''(\xi)\)

\(\xi \in (x-h,x+h)\)

Wzory na obliczania pochodnej mogą składać się z więcej niż dwóch punktów dla pochodnej obliczanej w przód i w tył ((x, f(x)),(x + h, f(x + h))) oraz więcej niż trzy punkty dla pochodnej obliczanej metodą symetryczną ((x - h, f(x - h)), (x, f(x)),(x + h, f(x + h))), jak do tej pory rozpatrywaliśmy. Do znalezienia wzoru wielopunktowego dla obliczania pochodnej w przód, w tył i symetrycznie, to wpierw znajdziemy rozwiązanie równania:

W przód dla k+1 punktów:
\(f^{(n)}(x)=Af(x)+Bf(x+1\cdot h)+...+Zf(x+k\cdot h)\)

W tył dla k+1 punktów:
\(f^{(n)}(x)=Af(x)+Bf(x-1 \cdot h)+...+Zf(x-k \cdot h)\)

Symetryczne dla 2*k+1 punktów:
\(f^{(n)}(x)=Af(x-k \cdot h)+Bf(x-(k-1)h)+...+Cf(x)+...+Df(x+(k-1)\cdot h)+Ef(x+kh)\)

Do znalezienia rozwiązania rozwijam funkcję po prawej stronie w szereg Taylora. Dzięki temu, w tym równaniu pojawi się pochodna tego samego rzędu, co po lewej stronie równania. Naszym celem jest uzyskać w równaniu tożsamość, tj. po lewej i prawej stronie równania ma być to samo.

Przykład: znajdziemy wzór na obliczanie w przód pierwszej pochodnej z czterema punktami:

\(f'(x)=Af(x)+Bf(x+h)+Cf(x+2h)+Df(x+3h)\)

\(Af(x) = Af(x)\)
\(Bf(x+h) = Bf(x) + Bhf'(x)+B\frac{h^2}{2}f''(x)+B\frac{h^3}{6}f'''(x)\)
\(Cf(x+2h) = Cf(x) + C2hf'(x)+C\frac{4h^2}{2}f''(x)+C\frac{8h^3}{6}f'''(x)\)
\(Df(x+3h) = Df(x) + D3hf'(x)+D\frac{9h^2}{2}f''(x)+D\frac{27h^3}{6}f'''(x)\)

Aby po lewej i prawej stronie równania było to samo, to muszę nałożyć taki warunek na współczynniki, aby wszystkie elementy równania po prawej stronie się wyzerowały oprócz elementu z pochodną tego samego rzędu, co po lewej stronie równania.

\(A+B+C+D=0\)
\(0+hB+2hC+3hD=1\)
\(0+\frac{h^2}{2}B+2h^2C+\frac{9h^2}{2}D=0\)
\(0+\frac{h^3}{6}B+\frac{4h^3}{3}C+\frac{27h^3}{6}D=0\)

Po podzieleniu równań przez h, h2 i h3, otrzymamy do rozwiązania taki układ równań liniowych:
\(A+B+C+D=0\)
\(0+1B+2C+3D=\frac{1}{h}\)
\(0+\frac{1}{2}B+2C+\frac{9}{2}D=0\)
\(0+\frac{1}{6}B+\frac{4}{3}C+\frac{27}{6}D=0\)

Postać macierzowa:
\(\begin{pmatrix}1 & 1 & 1 & 1\\ 0 & 1 & 2 & 3\\ 0 & \frac{1}{2} & 2 & \frac{9}{2} \\ 0 & \frac{1}{6} & \frac{4}{3} & \frac{27}{6}\end{pmatrix}\begin{pmatrix}A\\B\\C\\D\end{pmatrix}=\begin{pmatrix}0\\\frac{1}{h}\\0\\0\end{pmatrix}\)

h=1 
M = [1 1 1 1;0 1 2 3;0 1/2 2 9/2;0 1/6 4/3 27/6]
Y = [0;1/h;0;0]
M \ Y # Operacja znajdowania rozwiązania układu równań liniowych

Naszym rozwiązaniem jest \(A=-\frac{11}{6}\, B=3\, C=-\frac{3}{2}\, D=\frac{1}{3}\)

\(f'(x)=-\frac{11}{6}f(x)+3f(x+h)-\frac{3}{2}f(x+2h)+\frac{1}{3}f(x+3h)\)

h=0.01 # weźmy wartość h bardziej praktyczną
m = [1 1 1 1;0 1 2 3;0 1/2 2 9/2;0 1/6 4/3 27/6]
y = [0;1/h;0;0]
m\y # operacja znajdowania rozwiązania układu równań liniowych

\(A = -183\frac{1}{3}, B=300, C=-150, D=33\frac{1}{3}\)
\(f'(x)=-183\frac{1}{3}f(x)+300f(x+h)-150f(x+2h)+33\frac{1}{3}f(x+3h)\)
\(f'(x)=\frac{-\frac{11}{6}f(x)+3f(x+h)-\frac{3}{2}f(x+2h)+\frac{1}{3}f(x+3h)}{0.01}\)

Kolejny przykład: znajdziemy wzór na obliczanie w przód pierwszej pochodnej z dwoma punktami:

\(f'(x)=Af(x)+Bf(x+h)\)

\(Af(x) = Af(x)\)
\(Bf(x+h) = Bf(x) + Bhf'(x)\)

\(A+B=0\)
\(0+hB=1\)

\(A+B=0\)
\(0+B=\frac{1}{h}\)

Postać macierzowa:
\(\begin{pmatrix}1 & 1\\ 0 & 1 \end{pmatrix}\begin{pmatrix}A\\B \end{pmatrix}=\begin{pmatrix}0\\\frac{1}{h}\end{pmatrix}\)

h=0.01 
M = [1 1;0 1]
Y = [0;1/h]
M\Y # Operacja znajdowania rozwiązania układu równań liniowych

Naszym rozwiązaniem jest \(A=-100\, B=100\)

\(f'(x)=-100f(x)+100f(x+h) = 100(f(x+h)-f(x)) = \frac{f(x+h)-f(x)}{\frac{1}{100}}=\frac{f(x+h)-f(x)}{0.01}\)

Otrzymaliśmy w ten sposób znany nam wzór na pochodną z początku tego rozdziału.

Korzyść z obliczania numerycznie pochodnej wzorami wielopunktowymi jest taka, że dla tego samego kroku ℎ uzyskujemy znacznie mniejszy błąd obcięcia, ale kosztem większej liczby obliczeń - w dwupunktowych błąd odcięcia jest rzędu \(\mathcal{O}(h)\) a w trzypunktowych \(\mathcal{O}(h^2)\)

7. Całkowanie

7.1. Podejście symboliczne

\(\int f(x)dx = F(x)+C\)
\(\int\limits_{a}^b f(x)dx = F(b)-F(a)\)

W metodach numerycznych do obliczenia całki z funkcji f(x) będziemy zastępować funkcję f inną np. g, która jest akceptowalnie dobrym przybliżeniem funkcji f oraz jest prosta w scałkowaniu.

\(\int\limits_{a}^b f(x)dx \approx \int\limits_{a}^b g(x)dx\)

W metodach, które poznamy, nasza funkcja g będzie wielomianem. Jeśli funkcja podcałkowa jest wielomianem np stopnia 1, to korzystając z metody trapezów, opartej na przybliżeniu wielomianem pierwszego stopnia, to nasze obliczenia są dokładne. Nie może być innego wielomianu tego stopnia przechodzącego przez te dwa same punktu.

7.2. Całkowanie przez interpolacje wielomianową

Wykorzystamy wielomian Langrangea stopnia n:
\(l_{i}=\prod\limits_{j=0;j\neq i}^{n}\frac{x-x_{j}}{x_{i}-x_{j}},\,0\leq i \leq n\)

\(w(x)=\sum\limits_{i=0}^n f(x_{i})l_{i}(x)\)

\(\int\limits_{a}^b f(x)dx \approx \int\limits_{a}^b w(x)dx = \sum\limits_{i=0}^n f(x_{i})\int\limits_{a}^b l_{i}(x)dx\)

\(\int\limits_{a}^b f(x)dx \approx \sum\limits_{i=0}^n A_{i}f(x_{i})\)

\(A_{i}=\int\limits_{a}^b l_{i}(x)dx\)

Ten wzór jest nazywany metodą Newtona-Cotesa

Do znalezienia wielomianu stopnia n potrzebujemy n+1 punktów.

7.2.1. Metoda prostokątów dla punktu środkowego: wielomian stopnia 0

\(\int\limits_{a}^b f(x)dx \approx f(\frac{a+b}{2})(b-a)\)

Nasza funkcja g jest wielomianem stopnia 0, czyli jest wartością stałą. Geometrycznie reprezentuje pole powierzchni prostokąta.

Integration_rectangle.png
Źródło: Wikipedia

  1. Wzory złożone dla metody prostokątów

    Lewa suma Riemana: \(\int\limits_{a}^b f(x)dx \approx \Delta x \sum\limits_{i=0}^{k-1}f(a+i \cdot \Delta x)\)

    Błąd:
    \(\frac{(b-a)^2}{2n}|f^{(1)}(\xi)|\)
    \(\xi \in (a,b)\)

    Prawa suma Riemana: \(\int\limits_{a}^b f(x)dx \approx \Delta x \sum\limits_{i=1}^{k}f(a+i \cdot \Delta x)\)

    Błąd:
    \(\frac{(b-a)^2}{2n}|f^{(1)}(\xi)|\)
    \(\xi \in (a,b)\)

    Punktu środkowego: \(\int\limits_{a}^b f(x)dx \approx \Delta x \sum\limits_{i=0}^{k-1}f(a+\frac{(1+2i)}{2} \cdot \Delta x)\)

    Błąd:
    \(\frac{(b-a)^3}{24n^2}|f^{(2)}(\xi)|\)
    \(\xi \in (a,b)\)

    Przykład dla f(x)=x2
    \(\int\limits_{1}^2 x^2dx = \frac{1}{3}x^3|_{1}^2=\frac{7}{3}\)

    using Plots
    f(x)=x^2;a=1;b=2;k=0;
    err=zeros(100,3);
    for i in 1:100
            global k=k+10;
            dx=(b-a)/k;
            err[i,1]=7/3-dx*sum(f(a+j*dx) for j=0:(k-1))
            err[i,2]=7/3-dx*sum(f(a+j*dx) for j=1:k)
            err[i,3]=7/3-dx*sum(f(a+(1+2*j)*dx/2) for j=0:(k-1))
            plot(err,xlabel="Iteracja zmniejszająca Δx", label=["Lewa suma Riemana" "Prawa suma Riemana" "Punktu środkowego"])
            savefig("rectangle_error.png")
            plot(err[:,3],xlabel="Iteracja zmniejszająca Δx",label="Punktu środkowego")
            savefig("midpoint_error.png")
    end
    

    Dla funkcji monotoniczne rosnącej wartość całki oznaczonej z lewej sumy Riemana jest mniejsza od rzeczywistej wartości całki, a wartość z prawej sumy Riemana jest większa. Dla funkcji monotoniczne malejącej jest odwrotnie
    rectangle_error.png

    Błąd w metodzie punktu środkowego znacznie mniejszy niż w przypadku lewej i prawej sumy.
    midpoint_error.png

7.2.2. Metoda trapezów: wielomian stopnia 1

Funkcję podcałkową przybliżamy wielomianem stopnia pierwszego, czyli funkcją liniową. Poprzez takie przybliżenie, pole powierzchni, które tworzy funkcja g, jest polem trapezu: Integration_trapezoid.png
Źródło: Wikipedia

To pole jest równe:
\(f(x_i)\Delta x + \frac{1}{2}(f(x_{i+1})-f(x_{i}))\Delta x = \frac{f(x_i) + f(x_{i+1})}{2}\Delta x\)

\(\int\limits_{a}^b f(x)dx \approx \frac{f(x_i) + f(x_{i+1})}{2}\Delta x\)

  1. Wzór złożony

    Dla jednorodnie rozłożonych punktów xi:
    \(\int\limits_{a}^b f(x)dx \approx \Delta x \sum\limits_{i=0}^n \frac{f(x_i)+f(x_{i+1})}{2}\)

    Ten wzór możemy zapisać także w formie:
    \(\int\limits_{a}^b f(x)dx \approx \frac{\Delta x}{2}[f(a)+2\sum\limits_{i=1}^{n-1} f(x_i) + f(b)]\)

    \(x_{i}=a+i\Delta x\), \(i=0,1,2,...,n-1,n\), \(\Delta x=\frac{b-a}{n}\)

    Błąd:
    \(-\frac{1}{12}(b-a)(\Delta x)^2f^{(2)}(\xi)\)
    \(\xi \in (a,b)\)

    f(x)=x^2;n=100; a=1;b=2;
    dx=(b-a)/n;
    # pierwsza forma
    X=[a+i*dx for i=0:n];
    sum(((f(X[i])+f(X[i+1]))) for i=1:n) * dx/2
    # druga forma
    X=[a+i*dx for i=1:(n-1)];
    dx/2*(f(a)+f(b)+2*sum(f(X[i]) for i=1:(n-1)))
    

7.2.3. Metoda Simpsona: wielomian stopnia 2

\(\int\limits_{a}^b f(x)dx \approx \frac{b-a}{6}[f(a)+4f(\frac{a+b}{2})+f(b)]\)

Funkcję podcałkową przybliżamy wielomianem stopnia drugiego, czyli funkcją kwadratową. Poprzez takie przybliżenie, pole powierzchni, które tworzy funkcja g jest polem pod parabolą: Simpsons_method_illustration.png
Źródło: Wikipedia

  1. Wzór złożony

    Dla parzystej liczby punktów:
    \(\int\limits_{a}^b f(x)dx \approx \frac{\Delta x}{3}\sum\limits_{i=1}^{n/2}[f(x_{2i-2})+4f(x_{2i-1})+f(x_{2i})]\)

    Aby uniknąć powtórzeń obliczeń w powyższym wzorze, modyfikujemy go następująco:
    \(\int\limits_{a}^b f(x)dx \approx \frac{\Delta x}{3}[f(a)+2\sum\limits_{i=2}^{n/2}f(x_{2i-2})+4\sum\limits_{i=1}^{n/2}f(x_{2i-1})+f(b)]\)

    \(x_{i}=a+i\Delta x\), \(i=0,1,2,...,n-1,n\), \(\Delta x=\frac{b-a}{n}\)

    Błąd:
    \(-\frac{1}{180}(b-a)(\Delta x)^4f^{(4)}(\xi)\)
    \(\xi \in (a,b)\)

    Ze wzoru na błąd wynika, że metodą Simpsona uzyskamy dokładny wynik obliczeń nawet dla wielomianów stopnia trzeciego.

    f(x)=x^2;n=100; a=1;b=2;
    dx=(b-a)/n;
    X=[a+i*dx for i=0:n];
    dx/3*sum((f(X[2*i-1])+4*f(X[2*i])+f(X[2*i+1])) for i=1:Int(n/2))
    dx/3*(f(X[1])+2*sum(f(X[2*i-1]) for i=2:Int(n/2)) + 4*sum(f(X[2*i]) for i=1:Int(n/2))+f(X[n+1]))
    
    ## Mniejsza alokacja pamięci i obliczenia wykonywane szybciej dla wzoru bez modyfikacji
    @time dx/3*sum((f(X[2*i-1])+4*f(X[2*i])+f(X[2*i+1])) for i=1:Int(n/2))
    @time dx/3*(f(X[1])+2*sum(f(X[2*i-1]) for i=2:Int(n/2)) + 4*sum(f(X[2*i]) for i=1:Int(n/2))+f(X[n+1])) 
    

8. Znajdowanie rozwiązania równań różniczkowych zwyczajnych

Ograniczymy się do równań autonomicznych, czyli takich, w których nie występuje czas w sposób jawny.

Celem jest znaleźć rozwiązanie dla dowolnego warunku początkowego:
\[\dot x = f(x(t))\]
\[x(t_{0}) = x_0\]

8.1. Metoda Eulera (metoda Rungego–Kutty pierwszego rzędu)

\[x_{n+1} = x_{n} + f(x_{n})\Delta t\]

Ulepszona metoda Eulera: \[\tilde{x}_{n+1} = x_{n} + f(x_{n})\Delta t\] \[x_{n+1} = x_{n} + 0.5(f(x_{n})+f(\tilde{x}_{n+1}))\Delta t\]

Implementacja algorytmu Eulera dla \(\dot x = x^2-1\):

using Plots
n=100;m=10;
y = zeros(n,m);
for j=1:m 
    y[1,j] = j/10 - 0.1    
    for i=2:n
          y[i,j] = y[i-1,j] + (y[i-1,j]^2-1)*0.1;
    end
end
x = 0:0.1:9.9;
plot(y,linestyle = :dot)
savefig("euler_method.png")

euler_method.png

Obrazek 1: Metoda Eulera dla \(\dot x = x - x^3\)

8.2. Metoda Rungego–Kutty czwartego rzędu

\[k_{1} = f(x_{n})\Delta t\] \[k_{2} = f(x_{n}+0.5k_{1})\Delta t\] \[k_{3} = f(x_{n}+0.5k_{2})\Delta t\] \[k_{4} = f(x_{n}+k_{3})\Delta t\] \[x_{n+1} = x_{n}+\frac{1}{6}(k_{1} + 2k_{2} + 2k_{3} + k_{4})\]

Implementacja algorytmu Runge-Kutta dla \(\dot x = x-x^3\):

using Plots
n=100;m=10;
y = zeros(n,m);
dt = 0.1;
for j=1:m 
    y[1,j] = j/10 - 0.5;    
    for i=2:n
          k1 = (y[i-1,j] - y[i-1,j]^3)*dt;
          k2 = (y[i-1,j] + 0.5*k1 - (y[i-1,j] + 0.5*k1)^3)*dt;
          k3 = (y[i-1,j] + 0.5*k2 - (y[i-1,j] + 0.5*k2)^3)*dt;
          k4 = (y[i-1,j] + 0.5*k3 - (y[i-1,j] + 0.5*k3)^3)*dt;
          y[i,j] = y[i-1,j] + 1/6*(k1+2*k2+2*k3+k4);
    end
end
plot(y,linestyle = :dot)
savefig("runge_kutta_metod.png");

runge_kutta_metod.png

Obrazek 2: Metoda Runge-Kutta dla \(\dot x = x - x^3\)

9. Metoda tworzenia dłoni do macania funkcji

Taką metodą jest rozwinięcie funkcji f(x) w szereg Taylora w punkcie „a“ tylko do elementu liniowego: \[f(x) \approx f(a)+(x-a)\frac{f'(a)}{1!}=g(x)\]. Powstałą w ten sposób funkcję g(x) można wykorzystać do dotykania innych bardziej złożonych funkcji.

Pochodna w punkcie \[f'(a) = \lim\limits_{h\rightarrow 0}=\frac{f(a+h)-f(a)}{h}\]

Z definicji pochodnej jako stycznej możemy wyprowadzić wzór na metodę stycznych \[f'(x_n)=\frac{f(x_n)-0}{x_n-x_{n+1}}\] \[f'(x_n)(x_n-x_{n+1})=f(x_n)\] \[x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)}\] Reprezentacja graficzna \[y=f(x)\]

using Plots
f(x)=x^3;
fderiv(x)=3x^2
g1(x)=x;
#dla x=0.5 fderiv=3/4
g2(x)= 1/8
g3(x)= 3/8
g4(x) = 3/4*x # f'(a)x
# funkcje g4 przesuwamy w dół za pomocą wyrażenia: (3/4*0.5-0.5^3)
g5(x) = 3/4*x - (3/4*0.5-0.5^3) # # f'(a)x - (f'(a)a - f(a))

plot(f;xlims=(0,0.8),ylims=(0,0.6))
plot!([g1 g2 g3 g4 g5]);
vline!([1/2])
savefig("taylor_1.png")

taylor_1.png

\[y=f'(a)x-(f'(a)a-f(a))\] Dostaliśmy wzór na rozwinięcie w szereg Taylora \[y=f'(a)x-f'(a)a+f(a)\] \[y=f(a) + f'(a)x-f'(a)a\] \[y=f(a) + (x-a)f'(a)\]

10. Twierdzenie Taylora

Lokalne przybliżanie funkcji rzeczywistej za pomocą wielomianu złożonego z kolejnych jej pochodnych: \[f(x) = f(a) + \frac{x-a}{1!}f^1(a) + \frac{(x-a)^2}{2!}f^2(a) + \dots + \frac{(x-a)^n}{n!}f^n(a) + R_{n}(x,a)\]

Wygenerowano o: 2026-01-07 śro 12:00

Validate