W poprzedniej części kursu wyjaśniłem, jak łatwo w Octave można utworzyć własne programy (tzw. skrypty). Funkcje użytkownika najłatwiej można zdefiniować właśnie w formie skryptów.
Załóżmy, że naszym celem jest zdefiniowanie
funkcji o nazwie sin5
, która dla argumentu x
obliczać będzie wartość
wyrażenia x - x3/6 + x5/120
. Aby zdefiniować taką funkcję:
sin5.m
function y = sin5(x) y = x - x**3/6 + x**5/120; endfunction
Uwaga! Powyższa definicja nie jest optymalna! Niedługo ją poprawimy!
Funkcje tę wywołujemy w Octave w naturalny sposób:
> sin5 (0.1) ans = 0.099833
Ogólnie, podczas definiowania funkcji w pliku obowiązują następujące zasady:
m
(tj. mieć postać nazwa_funkcji_m
).function
i endfunction
.function y = f(a, b, c)
function [y, eps, xi] = f(a, b, c)
y = x - x**3/6; y = y + x**5/120;Niezakończenie jakiejś instrukcji średnikiem spowoduje wyświetlenie wykonywanych w niej obliczeń – ta cecha przydaje się np. podczas szukania błędów w skrypcie, ale rzadko kiedy ma zastosowanie w gotowej, przetestowanej funkcji, stąd zalecenie, by instrukcje kończyć średnikiem.
cos
, czyli
funkcja wyznaczająca cosinus kąta:
> cos([0, 0.1, 0.2]) ans = 1.00000 0.99500 0.98007
Jak widać, jeżeli argumentem funkcji cos
jest wektor, wynikiem jest wektor utworzony
z wartości tej funkcji dla wszystkich elementów wektora [0, 0.1, 0.2]
,
czyli
cos([0, 0.1, 0.2]) = [cos(0), cos(0.1), cos(0.2)]
Ogólnie, jeżeli f
jest odwzorowaniem, a v
macierzą n na m, to
f(v)
też jest macierzą n na m,
przy czym jeżeli podstawimy w = f(v)
, to spełniona będzie zależność
w(k,l) = f(v(k,l))
. Innymi słowy, w k-tym wierszu i l-tej kolumnie
macierzy f(v)
znajduje się wartość funkcji f
dla argumentu v(k,l)
.
Informacja o tym, czy dana funkcja Octave jest odwzorowaniem, znajduje się w dokumentacji tej funkcji
(help
), w której odwzorowania określane są jako mapper function lub mapping function,
zwykle już w pierwszym wierszu opisu.
Odwzorowania są w Octave bardzo ważną klasą funkcji, gdyż umożliwiają rozwiązywanie wielu podstawowych problemów numerycznych, takich jak znajdowanie pierwiastków równań z wieloma niewiadomymi, rozwiązywanie równań różniczkowych czy obliczanie pól powierzchni (całek), a także tworzenie wykresów funkcji.
Jak można się łatwo przekonać, nasza funkcja sin5
nie jest odwzorowaniem
i nie można jej wywoływać z argumentami innymi niż skalary, co znacznie ogranicza jej użyteczność.
Można temu jednak łatwo zaradzić. Aby funkcja sin5
stała się odwzorowaniem,
wystarczy poprzedzić kropką wszystkie występujące w jej definicji operatory potęgowania:
function y = sin5(x) y = x - (x.**3)/6 + (x.**5)/120; endfunction
Ogólnie, kropką można poprzedzić dowolny operator "iloczynowy",
czyli operator mnożenia (.*
, dzielenia (./
lub .\
) i
potęgowania (.**
lub .^
). To, kiedy stosować tę kropkę,
zależy od następujących czynników:
x/2
jest zawsze równoważne wyrażeniu x./2
. x * y
macierz x
musi mieć tyle kolumn,
ile wierszy ma macierz y
.x/y
jest równoważne x*(y-1)
,
a więc wiąże się z kosztownym obliczeniem odwrotności y
.x\y
ma wartość (x-1)*y
,
która jest wyznaczana szybkim algorytmem niewymagającym wyznaczania x-1
.x.*y
i x./y
macierze x
i y
muszą mieć ten sam rozmiar. Operacje z kropką wykonywane są "element po elemencie". Np.
> m = [1 -1; -1 1] m = 1 -1 -1 1 > v = [1 2; 3 4] v = 1 2 3 4 > m .* v ans = 1 -2 -3 4 > m * v ans = -2 -2 2 2
.\
", ".+
" i ".–".
> 2./mzostanie zinterpretowane następująco:
2 ./ m
Na zakończenie warto dodać, że jeżeli w pliku zawierającym definicję funkcji
umieścimy komentarz (najlepiej przed jej definicją),
będzie on wyświetlany przez Octave po wydaniu polecenia help nazwa_funkcji
(np. help sin5
). Stąd też ostateczna
zawartość pliku sin5.m
może wyglądać następująco:
# Funkcja sin5 przybliża wartość funkcji sin(x) za pomocą wielomianu stopnia 5. # # Przybliżenie to jest całkiem dobre dla małych wartości x, # np. dla |x| < 1. # function y = sin5(x) y = x - x.**3/6 + x.**5/120; endfunction
function
i endfunction
?x*y
i x.*y
?x/y
i x./y
?x**2
i x.**2
?x+y
i x.+2
?f(x)
zadaną wzorem:f(x) = x * sin2(x) * sqrt (abs (1 - x)) + x*cos(x)
gdziesqrt
oznacza pierwiastek kwadratowy, a abs
to wartość bezwzględna.
f(x)
dla 0 ≤ x ≤ 2.5
.
W tym celu możesz użyć następującego ciągu poleceń:
> x = 0 : 0.001 : 2.5; > y = f(x); > plot (x,y)Pierwsze z nich definiuje wektor "x-ów", drugie – wektor wartości w odpowiednich "x-ach", a trzecie powoduje wygenerowanie takiego wykresu jak ten:
f(x) = 0
ma pierwiastek z
równy
w przybliżeniu 2.2. Wyznacz jego wartość za pomocą polecenia fsolve:
> z = fsolve("f", 2.2)
f(x)
dla
0 ≤ x ≤ z
a
osią rzędnych (tj. y = 0
) (na poniższym rysunku zostało ono zaznaczone kolorem zielonym):
Możesz posłużyć się następującym poleceniem:
> quad("f", 0, z)
quad
i fsolve
.
Jakie jest znaczenie ich parametrów? Jak można ocenić dokładność rozwiązań?