MATHCAD 000 - elementy programowania Obliczenia warunkowe Funkcja if() Funkcja if() umożliwia warunkowe obliczanie wyrażenia w zależności od spełnienia określonego kryte- rium (testu) logicznego. Przydatna jest przede wszystkim do definiowania tzw. funkcji warunkowych (zwanych też sklejanymi). Funkcje te charakteryzują się tym, że nie dają się zapisać w postaci jednego wzoru obowiązującego w całej dziedzinie i z reguły są nieciągłe lub mają nieciągłe pochodne w punktach zszycia. Definicja takiej funkcji składa się z dwóch lub więcej wzorów obowiązujących w rozłącznych podzbiorach dziedziny. Składnia funkcji if() jest następująca: if( test_logiczny, wartość_gdy_prawda, wartość_gdy_fałsz) Przykłady jej zastosowań prezentujemy poniżej. Przykład Dana jest następujaca funkcja: x dla x 0 f ( x) = sin x dla x > 0 Odpowiednia definicja w Mathadzie ma postać: f( x) := if( x 0, x, sin( x) ) 5 0 5 0 fx ( ) 5 Funkcje max(), min() i mod() x Przykład Dla 3 przedziałów: dla x π / g( x) = sin x dla π / x < x < π / + dla x π / należy zastosować zagnieżdżona funkcję if() π p := to tylko dla wygody gx ( ) := if( x p,, if ( x p,, sin( x) )) 0 Przy definicji pewnych klas funkcji warunkowych przydatne mogą być funkcje min(), max() i mod() - pierwsze dwie używamy w sytuacjach gdy chcemy ograniczyć (obciąć) zakresy wartości a ostatnia jest przydatna do definicji funkcji okresowych. gx ( ) x /9
max( cos( x), 0) mod( x, ) 0 0 0 0 0 0 4 6 Materiał dodatkowy. UWAGA: funkcje warunkowe definiowane przy pomocy if() można bez ograniczeń stosować w obliczeniach numerycznych i rysować ich wykresy nawet gdy są nieciągłe. Nie można ich jednak używać w obliczeniach symbolicznych: d f ( x) dx f( x) błąd Ograniczenie to można pokonać stosując odpowiednie funkcje z grupy "piecewise continuous", w szczególności przydatna jest funkcja Heaviside'a. Na przykład omawianą powyżej funkcję f(x) można zapisać następująco: f( x) := Φ( x) sin( x) + Φ( x) x co pozwoli na poprawne operowanie w obliczeniach symbolicznych: f( x) Φ( x) sin( x) + Φ( x) x d dx f( x) simplify + Φ( x) cos( x) Φ( x) Temat ten wykracza jednak poza ramy niniejszego opracowania, gdyż wymaga elementarnej wiedzy z dziedziny dystrybucji Przy definicji pewnych klas funkcji warunkowych przydatne mogą być również funkcje min(), max() i mod() - pierwsze dwie używamy w sytuacjach gdy chcemy ograniczyć (obciąć) zakresy wartości a ostatnia jest przydatna do definicji funkcji okresowych. Funkcje warunkowe można bez ograniczeń stosować w obliczeniach numerycznych i rysować ich wykresy nawet gdy są nieciągłe. Funkcja until() Funkcja until() służy do iteracyjnego (cyklicznego) wykonywania obliczeń aż do spełnienia określonego warunku logicznego. Typowe jej zastosowanie to obliczanie kolejnych wyrazów ciągu lub sumy szeregu dla z góry zadanej dokładności (zob, przykłady poniżej). Pierwszy parametr funkcji until określa tzw. kryterium stopu. Obliczenia trwają tak długo aż parametr ten przyjmie ujemną wartość. Nie podajemy więc warunku logicznego w bezpośredniej postaci a jego odpowiednik liczbowy, tzn. zamiast wyrażenia typu x < a podajemy x-a (<0 już nie piszemy). Drugi parametr określa zwracaną wartość. Aby cały cykl obliczeń miał sens należy dynamicznie zmieniać wartości obydwu parametrów z wykorzystaniem zmiennych zakresowych (iterowanych). Wyjaśnimy to na konkretnym przykładzie poniżej. UWAGA : niewłaściwe uzycie funkcji until może doprowadzić do bardzo długiego cyklu obliczeń. Co prawda - ze wględu na ograniczenia jakie są nałożone na zmiennne zakresowe - /9
nie grozi nam w Mathcadzie pętla nieskończona (tzw. "zapętlenie"), ale i tak należy definiować testowy warunek logiczny ze szczególną uwagą, tak aby zapewnić jego spełnienie w skończonej liczbie iteracji. UWAGA : funkcja until jest przeżytkiem i od Mathcada w wersji 000 obsługiwana jest tylko dla zgodności z wcześniejszymi wersjami programu. Obecnie zalecaną metodą obliczeń iteracyjnych jest zdefiniowanie własnej funkcji-programu, wykorzystującej instrukcję while. Temat ten zostanie przedstawiony w dalszej części materiału. Obliczanie sumy szeregu z ustaloną dokładnością - Metoda *) przykład zaczęrpnięty z materiałów P.K (zob. plik..\pk\wstep_cz.mcd) Elementy szeregu: n := 00 generujemy dużą liczbę elementów ciągu (na zapas) jest to oczywiście trudne do przewidzenia i nieekonomiczne i := 0.. n dla słabo zbieżnych szeregów. można nieco usprawnić obliczenia (zob. metoda poniżej) a i := i Początkowa suma szeregu: s 0 := a 0 Dokładność: ε := 0 0 Funkcja until: komentarz k :=.. n obliczenia iteracyjne rozpoczynamy od s s k := until( a k ε, s k + a k ) i kontynuujemy je do osiągnięcia żądanej dokładności. p := last() s p = 34 zawsze obliczany jest jeden nadmiarowy element (równy 0), dlatego musimy go pominąć Ostatecznie suma szeregu wynosi: Sprawdzenie s p =.9999999999479 i = 0 i err := s p err = 5.8077 0 Obliczanie sumy szeregu z ustaloną dokładnością - Metoda a := 0 s := 0 niszczymy poprzednie definicje (na wszelki wypade a 0 := s 0 := inicjujemy pierwszy element i sumę pozostałe elementy będą obliczane rekurencyjnie a i+ = a i przy obliczaniu samej sumy 3/9
i :=.. 0000000 a i s i n := last() s := until a i ε, n = 34 a i a i s i + tu możemy zdefiniować dużą zmienną zakresową bo zarówno sumę szeregu jak i same jego elementy będziemy generować dynamicznie trzeba tylko uważnie zapisać warunek stopu sztuczka polega na zastosowaniu wektorów co pozwala w jednym cyklu (iteracji) wyznaczyć równolegle nowy element ciągu i nową sumę szeregu s n =.9999999999479 Uwaga: przedstawiona metoda jest tylko nieznacznie lepsza - a na pewno mniej intuicyjna -.od poprzedniej. Uniknęliśmy przeliczania zbyt dużej (nieznanej z góry) liczby elementów ciągu ale za to liczymy je podwójnie!!! Jeżeli elementy ciągu są podane w jawnej postaci (jak w met. ) to nie trzeba ich generować na zapas - w funkcji until można po prostu zapisywać zamiast a i- odpowiedni wzór. Jednak naprawdę efektywne i elastyczne rozwiązanie możemy uzyskać dopiero poprzez zaprogra- mowanie własnej funkcji. Ćwiczenie :. Zdefiniuj funkcje: a) x / f ( x) = cos x dla dla x 0 x > 0 x + ale nie mniej niż dla x b) 3 g( x) = x dla < x < x ale nie więięc niż dla x Sporządź wykresy tych funkcji i ich pochodnych do rzędu n=??, tak aby określić stopień ciągłości funkcji (C 0, C,...) w punktach zszycia.. Materiał dodatkowy. Oblicz sumę szeregu: + 3 4.. + z dokładnością ε = e-7. Uwaga: ponieważ szereg jest naprzemienny to dobrym oszacowaniem błędu jest a i Elementy programowania Wprowadzenie Mathcad oferuje pewne narzędzia do programowania własnych funkcji. Należy podkreślić, że ich 4/9
możliwości są bardzo skromne w porównaniu do klasycznych języków programowania, z których zapożyczono na przykład podstawowe instrukcje sterujące (takie jak if, for i while), jednak ich składnia jest inna i mało intuicyjna (szczególnie dla osób mających już jakieś doświadczenie z programowaniem i przyzwyczajonych do innych niż Mathcad standardów). Kod programu może być realizowany jedynie wewnątrz definicji funkcji, co ogranicza zakres jego zastosowań. Największą wadą jest jednak brak jawnych deklaracji zmiennych i kontroli poprawności typów co przy pisaniu większych programów utrudnia znalezienie błędów. Pomimo wspomnianych wad warto jednak zapoznać się z elementami programowania oferowanymi w Mathcadzie, ponieważ są sytuacje, w których programowanie (nawet prymitywne) jest wręcz niezbędne lub bardzo upraszcza skomplikowane obliczenia. Materiał prezentowany poniżej jest krótkim przeglądem możliwości Mathcada w tym zakresie a nie kursem programowania. Dlatego ograniczono się do podania podstawowych instrukcji sterujących i kilku prostych przykładów bez wnikania w tajniki algorytmiki i sztuki programowania. Dalsze informacje i ciekawe przykłady można znaleźć w "Resource Center". Wskazówka: Aby zacząć programowanie funkcji, należy po wpisaniu początkowej definicji funkcji f(x) := należy kliknąć przycisk "Add Line" z paska narzedziwego "Programming". W kolejnych liniach (Add Line) wpisujemy kod programu, ale słów kluczowych nie mozna wpisywać bezpośrednio z klawiatury - należy je wywoływać poprzez odpowiednie przyciski paska "programming" (lub ew. skróty klawiaturowe). - definicja lokalnej zmiennej wewnątrz bloku (operator przypisania) Składnia: var value x x + Akcja przypisz zmiennej var wartość value zwiększ x o jeden if... if... otherwise - warunkowe obliczenie wyrażenia Składnia: wart if warunek wart if warunek "..." wartx otherwise Akcja = podaj wartość: wart gdy spełniony jest warunek wart gdy spełniony jest warunek itd. wartx we wszystkich pozostałych przypadkach Przykład Przykład funkcję f(x) omawianą na początku tego dokumentu można zapisać następująco f( x) := x if x 0 sin( x) otherwise podobna lecz trochę bardziej rozbudowana będzie definicja funkcji g(x) - tu dla wygody definiujemy roboczą-lokalną zmienną p; jest ona widoczna jedynie wewnątzr bloku reprezentowanego przez pionową kreskę 5/9
4 0 5 0 π gx ( ) := p if x p if x p sin( x) otherwise for - pętla "od-do-co" (cykliczne wykonanie instrukcji dla zmiennej zakresowej) Składnia: Akcja = wykonaj-powtórz N razy for lub i.. N instrukcja for i, 3.. N instr_ "..." instr_k dla i równe od do N wykonaj podaną instrukcję lub (tu z krokiem ) ciąg instrukcji w bloku Pętla for ma zastosowanie gdy z góry wiemy ile razy dana pętla będzie powtórzona. Szczególnie przydatna jest przy operacjach na wektorach i macierzach Przykład 3 Przykład 4 suma: + + 3 +.. + N silnia( n) := s for s s i.. n s i sum( n) := s 0 for s i.. n s s+ i silnia( 0) = 368800 = i 0! 368800 sum( ) = 506 = i = 506 Przykład 5 znalezienie maksymalnego elementu wektora tu w odróżnieniu od standardowej funkcji max() chcemy znaleźć także element (indeks) o maksymalnej wrtości. Nasza funkcja będzie zwracać od razu dwie wartości w postaci wektora. Pierwszy jego element określa numer elementu o maksymalnej wartości a drugi wartość max. Dla przejrzystości programu pomijamy sprawdzenie czy dane wejściowe są wektorem!!! 6/9
imax( v) := ix ORIGIN for i ORIGIN.. last( v) ix i if v i > v ix ix v ix Zmienna ix przechowuje dotychczasowy- -najlepszy-znaleziony indeks. Aby program był uniwersalny nie możemy zaczynać od 0 lub tylko od ORIGIN i kończyć pętlę dla last(v). Testujemy naszą funkcję i := 0.. 9 v i := rnd( 0) generujemy losowy wektor v T = ( 0.03.933 5.85 3.503 8.8.74 7.05 3.04 0.94.473 ) nasza funkcja dla porównania funkcja max() imax( v) = 4 8.838 max( v) = 8.838 OK while - pętla "tak długo jak" Składnia: while war_log instrukcja lub while x < Xmax instr_ "..." instr_k Akcja = wykonaj-powtórz obliczenia tak długo jak spełniony jest warunek logiczny wykonaj podaną instrukcję lub (tu konkretny przykład war_log). wykonaj ciąg instrukcji w bloku Pętlę while stosujemy wtedy gdy nie wiemy z góry ile iteracji trzeba wykonać do osiągnięcia danego celu. Jest ogólniejsza i bardziej wszechstronna od pętli for (na przykład tą ostatnią można bez trudu zapisać w formie while), ale też wymaga większej uwagi, gdyż łatwo przez prostą pomyłkę doprowadzić do tzw. pętli nieskończonej. Należy więc bardzo starannie programować warunek logiczny (i wewnętrzne instrukcje pętli), tak aby zagwarantować osiągnięcie wartości fałsz w skończonej liczbie kroków. Przykład 6 tu jakiś "prymityw" na while (w przygotowaniu) Przykład 7 Sumowanie szeregów z ustaloną dokładnością Jak zauważyliśmy używanie funkcji until() jest niewygodne i mało efektywne gdyż wymaga 7/9
tworzenia wektorów (czasami o dużych rozmiarach) tylko po to aby wyciągnąć jego ostatni element. W takich przypadkach idealnym wręcz rozwiązaniem jest zastosowanie własnej funkcji zaprogramowanej z użyciem pętli while. Napiszemy własną funkcję sinus(x,ε), która liczy wartość sin(x) z ustaloną dokładnością Na początek trochę teorii rozwijamy sin(x) w szereg Taylora sin( x) series, x, 0 x 6 x3 + 0 x5 5040 x7 + x 3 36880 x9 Ten szereg potęgowy możemy przedstawić w postaci: x +.. 3! 5! Funkcję sumującą określoną liczbę wyrazów takiego szeregu można dość łatwo zapisać: sinn( x, N) N ( ) i x i+ := sinn(, 4) = 0.844700970076 ( i + )! i = 0 sin( ) = 0.84470984807897 Ale ile wyrazów trzeba zsumować aby osiągnąć ustaloną dokładność??? Aby nie wnikać za głęboko w tajniki szeregów funkcyjnych podajemy gotowe rozwiązanie. Reszta rozpatrywanego szeregu jest nie większa niż x k+ R k < dla k = i+ nieparzyste ( k + )! Trzeba po prostu sumować szereg tak długo jak (ang. while) błąd określony powyższym wzorem jest większy od ustalonego z góry, dopuszczalnego błędu ε. x 5 Jeszcze kilka uwag zanim zaczniemy programować!!! kolejne elementy szeregu można liczyć efektywnie z rekurencyjnej formuły: a i = a i. i ( i ) podobnie można postąpić dla oszacowania bieżącego błędu (reszty) szeregu x dla dużych x szereg może być na początku wolno zbieżny dlatego warto policzyć jego resztę z dzielenia przez π (modulo), aby uniknąć niepotrzebnej pracy procesora - można tu zapewnić Definicja jeszcze lepszą naszej (szybszą) funkcji zbieżność sinus ale... (pomyśl sam). Testowe zadanie 8/9
( ) := x mod( x, π) sinus x, ε i a x s a r x xx x x while r > ε s i i i+ a a s s+ a r xx i ( i ) ax i + x := e := 0 8 sinus( x, e) sin( x) = 0.84470984807897 = 0.84470984648068 trzeba było obliczyć szereg do wyrazu x err := sin( 0) sinus 0, 0 8 err = 4.95 0 0 ( ) 0 x = rzeczywisty błąd jest dużo mniejszy, gdyż zastosowane oszacowanie błędu jest pesymistyczne (gwarantowane) - w typowych sytuacjach błąd jest o lub rzędy niższy. Ćwiczenie. Przykłady programowania (w opracowaniu) 9/9