MN01LAB: Różnice pomiędzy wersjami
mNie podano opisu zmian |
m Zastępowanie tekstu – „,↵</math>” na „</math>,” |
||
(Nie pokazano 12 wersji utworzonych przez 3 użytkowników) | |||
Linia 1: | Linia 1: | ||
<!-- | |||
Konwertowane z pliku LaTeX przez latex2mediawiki, zob. http://www.ii.uj.edu.pl/ pawlik1/latex2mediawiki.php. | |||
Niezb�dne rozszerzenia i modyfikacje oryginalnego latex2mediawiki | |||
wprowadzi� przykry@mimuw.edu.pl | |||
--> | |||
=Eksperymenty ze środowiskiem obliczeń numerycznych= | |||
{{powrot |Metody numeryczne | do strony głównej | |||
przedmiotu <strong>Metody numeryczne</strong>}} | |||
== | <div class="mw-collapsible mw-made=collapsible mw-collapsed"> | ||
Oglądaj wskazówki i rozwiązania __SHOWALL__<br> | |||
Ukryj wskazówki i rozwiązania __HIDEALL__ | |||
</div> | |||
W Linuxie | W Linuxie czas działania programu można zbadać poleceniem <code style="color: #666">time</code>. | ||
<div style="margin-top:1em; padding-top,padding-bottom:1em;"> | <div style="margin-top:1em; padding-top,padding-bottom:1em;"> | ||
Linia 8: | Linia 21: | ||
<div class="exercise"> | <div class="exercise"> | ||
Który | Który z poniższych programów wykona się szybciej? | ||
<div | |||
<div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>x = 1.0; | |||
x | for( i = 0; i < N; i++) | ||
for( i | x = x/3.0; | ||
x | |||
</pre></div> | </pre></div> | ||
czy | czy | ||
<div | <div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>x = 1.0; f = 1.0/3.0; | ||
for( i = 0; i < N; i++) | |||
x | x = x*f; | ||
for( i | |||
x | |||
</pre></div> | </pre></div> | ||
Linia 28: | Linia 38: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em"> | ||
Oczywiście, szybszy będzie program nie wykorzystujący dzielenia. Optymalizujący | Oczywiście, szybszy będzie program nie wykorzystujący dzielenia. Optymalizujący | ||
kompilator ( | kompilator (<code style="color: #666">gcc -O3</code>) strawi, a nawet będzie jeszcze bardziej zadowolony z pozornie | ||
rozrzutnego kodu | rozrzutnego kodu | ||
<div | <div style="margin: 1em; padding:1em; color: #000; background-color:#fcfcfc;"><pre>x = 1.0; | ||
for( i = 0; i < N; i++) | |||
x | x = x*(1.0/3.0); | ||
for( i | |||
x | |||
</pre></div> | </pre></div> | ||
dlatego, że stałą, przez którą trzeba mnożyć <math> | dlatego, że stałą, przez którą trzeba mnożyć <math>x</math>, wyliczy przed wykonaniem | ||
programu. | programu. | ||
Sprawdź, czy z wyłączoną optymalizacją ten kod okaże się najwolniejszy | Sprawdź, czy z wyłączoną optymalizacją ten kod okaże się najwolniejszy ze wszystkich... (okazuje się, że nie!) | ||
ze wszystkich... | |||
(okazuje się, że nie!) | |||
</div></div></div> | </div></div></div> | ||
Linia 54: | Linia 60: | ||
* binarnego | * binarnego | ||
kolejne wartości <math> | kolejne wartości <math>\sin(\pi\cdot i\cdot 0.4)</math>, gdzie <math>i=0,\ldots, 1024</math>. Następnie porównaj | ||
rozmiary plików i możliwości ich odczytania zewnętrznymi narzędziami. Wreszcie, | rozmiary plików i możliwości ich odczytania zewnętrznymi narzędziami. Wreszcie, | ||
wczytaj liczby z pliku i porównaj je z oryginalnymi wartościami sinusa. Czy | wczytaj liczby z pliku i porównaj je z oryginalnymi wartościami sinusa. Czy | ||
Linia 66: | Linia 72: | ||
Różnice są skutkiem konwersji liczb zmiennoprzecinkowych do formatu | Różnice są skutkiem konwersji liczb zmiennoprzecinkowych do formatu | ||
dziesiętnego. Oczywiście, zapis w formacie binarnym daje dokładną kopię | dziesiętnego. Oczywiście, zapis w formacie binarnym daje dokładną kopię | ||
zawartości pamięci, | zawartości pamięci, nie ma więc żadnych strat. | ||
</div></div></div> | </div></div></div> | ||
Linia 73: | Linia 79: | ||
<div class="exercise"> | <div class="exercise"> | ||
Pomyśl, jak obliczać, korzystając jedynie z czterech działań podstawowych: <math> | Pomyśl, jak obliczać, korzystając jedynie z czterech działań podstawowych: <math>+,\, | ||
-, \, \times, \, \div</math>, wartość funkcji <code>exp(</code><math> | -, \, \times, \, \div</math>, wartość funkcji <code>exp(</code><math>x</math><code>)</code> = <math>e^x</math> dla | ||
dowolnych <math> | dowolnych <math>x</math> rzeczywistych. Naszym kryterium jest, by <math>|e^x - \exp(x)| \leq | ||
\epsilon</math>, czyli by błąd bezwzględny aproksymacji nie przekroczył zadanego | \epsilon</math>, czyli by błąd bezwzględny aproksymacji nie przekroczył zadanego | ||
<math> | <math>\epsilon</math>. | ||
Wykonaj eksperymenty w C lub w Octave, pokazujące koszt metody w zależności od | Wykonaj eksperymenty w C lub w Octave, pokazujące koszt metody w zależności od | ||
<math> | <math>x</math> oraz w zależności od <math>\epsilon</math>. Przeprowadź też sekwencję testów | ||
potwierdzających | potwierdzających twoje rachunki co do oczekiwanej dokładności (porównując się z | ||
funkcją biblioteczną). W C możesz korzystać ze stałej <code>M_E</code> <math> | funkcją biblioteczną). W C możesz korzystać ze stałej <code>M_E</code> <math>\approx e = | ||
\exp(1)</math>, zdefiniowanej w pliku nagłówkowym | \exp(1)</math>, zdefiniowanej w pliku nagłówkowym <code style="color: #666">math.h</code>. | ||
</div></div> | </div></div> | ||
Linia 90: | Linia 96: | ||
W pierwszym odruchu myślimy o szeregu definiującym funkcję wykładniczą, | W pierwszym odruchu myślimy o szeregu definiującym funkcję wykładniczą, | ||
<center><math> | <center><math> | ||
e^x = \sum_{n=0}^\infty \frac{x^n}{n!} | e^x = \sum_{n=0}^\infty \frac{x^n}{n!} | ||
</math></center> | </math></center> | ||
Linia 96: | Linia 102: | ||
lub o granicy | lub o granicy | ||
<center><math> | <center><math> | ||
e^x = \lim_{n\rightarrow \infty} \left( 1 + \frac{x}{n}\right)^{\frac{n}{x}} | e^x = \lim_{n\rightarrow \infty} \left( 1 + \frac{x}{n}\right)^{\frac{n}{x}}</math></center> | ||
</math></center> | |||
Drugi pomysł okazuje się niewypałem | Drugi pomysł okazuje się niewypałem między innymi dlatego, że musi korzystać z operacji | ||
potęgowania z wykładnikiem rzeczywistym (którą skądinąd najprościej | potęgowania z wykładnikiem rzeczywistym (którą skądinąd najprościej | ||
implementować korzystając m.in. z funkcji <math> | implementować korzystając m.in. z funkcji <math>\exp</math>, jako <math>x^y = \exp(y\cdot \log(x))</math>). | ||
Przyjrzyjmy się więc pierwszemu. | Przyjrzyjmy się więc pierwszemu. | ||
<center><math> | <center><math> | ||
e^x \approx \sum_{n=0}^N \frac{x^n}{n!} | e^x \approx \sum_{n=0}^N \frac{x^n}{n!}</math>,</center> | ||
</math></center> | |||
a dokładniej, ze wzoru na resztę w postaci Lagrange'a, | a dokładniej, ze wzoru na resztę w postaci Lagrange'a, | ||
<center><math> | <center><math> | ||
|e^x - \sum_{n=0}^N \frac{x^n}{n!} | \leq \frac{|\xi|^{N+1}}{(N+1)!} | |e^x - \sum_{n=0}^N \frac{x^n}{n!} | \leq \frac{|\xi|^{N+1}}{(N+1)!}</math></center> | ||
</math></center> | |||
Stąd dostajemy oszacowanie, oznaczając wartość szeregu obciętego na <math> | Stąd dostajemy oszacowanie, oznaczając wartość szeregu obciętego na <math>N</math>-tym | ||
wyrazie, | wyrazie, | ||
<center><math> | <center><math> | ||
\exp(x,N) = \sum_{n=0}^N \frac{x^n}{n!} | \exp(x,N) = \sum_{n=0}^N \frac{x^n}{n!}</math>,</center> | ||
</math></center> | |||
<center><math> | <center><math> | ||
\left | \left|e^x - \exp(x,N) \right| \leq \frac{|x|^{N+1}}{(N+1)!}, | ||
</math></center> | </math></center> | ||
tak więc <math> | tak więc <math>N</math> musi być takie, że <math>\frac{|x|^{N+1}}{(N+1)!} \leq \epsilon</math>. | ||
Poniżej mamy kod, który to realizuje w Octave (analogiczny program w C napisz | Poniżej mamy kod, który to realizuje w Octave (analogiczny program w C napisz samodzielnie): | ||
samodzielnie): | |||
<div | <div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>function [y, N] = expa(x, epsilon) | ||
y = skladnik = 1.0; N = 1; | |||
function [y, N] | |||
y | |||
while (abs(skladnik) > epsilon) | while (abs(skladnik) > epsilon) | ||
skladnik * | skladnik *= (x/N); | ||
y + | y += skladnik; | ||
N++; | N++; | ||
end | end | ||
end | end | ||
</pre></div> | </pre></div> | ||
Możesz go przetestować pod względem osiąganej dokładności i kosztu, porównując się z | Możesz go przetestować pod względem osiąganej dokładności i kosztu, porównując się z | ||
funkcją biblioteczną: | funkcją biblioteczną: | ||
<div | <div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre>function [blad, relblad, koszt] = testexp(expX, x) | ||
dokladnie = exp(x); | |||
function [blad, relblad, koszt] | [wartosc, koszt] = feval(expX,x,1e-8); | ||
dokladnie | blad = abs(wartosc-dokladnie); | ||
[wartosc, koszt] | relblad = blad/dokladnie; | ||
blad | |||
relblad | |||
end | end | ||
i | i = 0; zakres = linspace(0,7,100); | ||
for x | for x = zakres | ||
fprintf(stderr,'x | fprintf(stderr,'x = %e\n', x); | ||
[blad, relblad, koszt] | i++; | ||
koszta(i) | [blad, relblad, koszt] = testexp('expa',x); | ||
blada(i) | koszta(i) = koszt; relblada(i) = relblad; | ||
blada(i) = blad; | |||
end | end | ||
xlabel('x'); | xlabel('x'); | ||
ylabel('blad'); | ylabel('blad'); | ||
title(['Epsilon | title(['Epsilon = ', num2str(epsilon)]); | ||
plot(zakres, relblada, ';blad wzgledny;'); | plot(zakres, relblada, ';blad wzgledny;'); | ||
plot(zakres, blada, ';blad bezwzgledny;'); | plot(zakres, blada, ';blad bezwzgledny;'); | ||
</pre></div> | </pre></div> | ||
Zgodnie z oczekiwaniami, błąd jest poniżej zadanej tolerancji, tutaj | Zgodnie z oczekiwaniami, błąd jest poniżej zadanej tolerancji, tutaj <math>10^{-8}</math>. | ||
[[Image: | [[Image:MNbladwzglednyexpa.png|thumb|550px|center|Błąd względny aproksymacji wielomianem Taylora.]] | ||
Koszt aproksymacji rośnie wraz z <math>x</math>: | |||
[[Image:MNkosztexpa.png|thumb| | [[Image:MNkosztexpa.png|thumb|550px|center|Koszt aproksymacji wielomianem Taylora.]] | ||
Niektóre wyniki mogą Cię jednak zaskoczyć: | Niektóre wyniki mogą Cię jednak zaskoczyć: | ||
* Błąd bezwzględny przekracza założoną wartość, gdy <math> | * Błąd bezwzględny przekracza założoną wartość, gdy <math>\epsilon = 2.2\cdot 10^{-15}</math>, a błąd względny od pewnego momentu <strong>rośnie</strong> z <math>x</math>. [[Image:MNbladbezwzglednyexpaeps.png|thumb|550px|center|Błąd względny aproksymacji wielomianem Taylora dla zadanej bardzo małej tolerancji błędu.]] | ||
10^{-15}</math>, a błąd względny od pewnego momentu | * Nie daje się tak policzyć <math>e^{2006}</math>. | ||
[[Image:MNbladbezwzglednyexpaeps.png|thumb| | * Wartość <code style="color: #006">expa()</code> może być ujemna dla <math>x<0</math>. | ||
tolerancji błędu.]] | |||
* Nie daje się tak policzyć <math> | |||
* Wartość <code>expa()</code> może być ujemna dla <math> | |||
Wyjaśnienie tych szokujących faktów (które nie mają nic wspólnego z błędem w | Wyjaśnienie tych szokujących faktów (które nie mają nic wspólnego z błędem w | ||
implementacji) musisz odłożyć do momentu, gdy bliżej przyjrzymy się temu, | implementacji) musisz odłożyć do momentu, [[MN03|gdy bliżej przyjrzymy się temu, <strong>jak</strong> | ||
liczy komputer. | liczy komputer]]. | ||
</div></div></div> | </div></div></div> | ||
Linia 198: | Linia 191: | ||
<div class="exercise"> | <div class="exercise"> | ||
Spróbuj obniżyć koszt wyznaczania <math> | Spróbuj obniżyć koszt wyznaczania <math>\exp(x)</math> dla dużych <math>x</math>. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Wskazówka </span><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Wskazówka </span><div class="mw-collapsible-content" style="display:none"> | ||
<div style="font-size:smaller; background-color:# | <div style="font-size:smaller; background-color:#f9fff9; padding: 1em"> Rzecz w tym, że dla dużych <math>x</math>, trzeba wziąć bardzo dużo wyrazów w szeregu | ||
Taylora. Czy można tak wykombinować, by w rezultacie wziąć ich mniej? </div> | Taylora. Czy można tak wykombinować, by w rezultacie wziąć ich mniej? </div> | ||
</div></div> | </div></div> | ||
Linia 209: | Linia 202: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"><span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie </span><div class="mw-collapsible-content" style="display:none"><div style="margin-left:1em"> | ||
Bez zmieniejszenia ogólności, załóżmy, że <math> | Bez zmieniejszenia ogólności, załóżmy, że <math>x\geq 0</math>. | ||
Jeśli <math> | Jeśli <math>x = k + t</math>, gdzie <math>t \in [0,1)</math>, a <math>k</math> jest całkowite, to oczywiście | ||
<center><math> | <center><math> | ||
e^x = e^{k+t} = e^k e^t = e^k \exp(t) | e^x = e^{k+t} = e^k e^t = e^k \exp(t)</math></center> | ||
</math></center> | |||
Tak więc zadanie redukuje się do wyznaczenia <math> | Tak więc zadanie redukuje się do wyznaczenia <math>\exp(t)</math> dla <strong>małego</strong> <math>t</math> oraz | ||
do co najwyżej <math> | do co najwyżej <math>k</math> dodatkowych mnożeń potrzebnych do wyznaczenia całkowitej | ||
potęgi <math> | potęgi <math>e^k</math> (ile mnożeń <strong>naprawdę</strong> wystarczy?). Pamiętaj, przyjęliśmy, że | ||
znamy reprezentację numeryczną liczby <math> | znamy reprezentację numeryczną liczby <math>e</math>. | ||
<div | <div style="margin: 1em; padding:1em; color: #006; background-color:#fcfcfc;"><pre># wersja B | ||
function [y, N] = expb(x, epsilon) | |||
function [y, N] | k = floor(x); t = x - k; | ||
k | [y, N] = expa(t, epsilon); | ||
[y, N] | for i = 1:k | ||
for i | y *= e; | ||
y * | |||
end | end | ||
N + | N += (k+2); | ||
end | end | ||
</pre></div> | </pre></div> | ||
[[Image:MNkosztexpab.png|thumb| | [[Image:MNkosztexpab.png|thumb|550px|center|Wersja B jest istotnie tańsza.]] | ||
</div></div></div> | </div></div></div> |
Aktualna wersja na dzień 21:51, 11 wrz 2023
Eksperymenty ze środowiskiem obliczeń numerycznych
<<< Powrót do strony głównej przedmiotu Metody numeryczne
Oglądaj wskazówki i rozwiązania __SHOWALL__
Ukryj wskazówki i rozwiązania __HIDEALL__
W Linuxie czas działania programu można zbadać poleceniem time
.
Ćwiczenie
Który z poniższych programów wykona się szybciej?
x = 1.0; for( i = 0; i < N; i++) x = x/3.0;
czy
x = 1.0; f = 1.0/3.0; for( i = 0; i < N; i++) x = x*f;
Ćwiczenie
Napisz program w C, który zapisuje do pliku
- tekstowego
- binarnego
kolejne wartości , gdzie . Następnie porównaj rozmiary plików i możliwości ich odczytania zewnętrznymi narzędziami. Wreszcie, wczytaj liczby z pliku i porównaj je z oryginalnymi wartościami sinusa. Czy możesz wyjaśnić przyczyny różnic?
Powtórz to samo w Octave.
Ćwiczenie: Implementacja funkcji matematycznych
Pomyśl, jak obliczać, korzystając jedynie z czterech działań podstawowych: , wartość funkcji exp(
)
= dla
dowolnych rzeczywistych. Naszym kryterium jest, by , czyli by błąd bezwzględny aproksymacji nie przekroczył zadanego
.
Wykonaj eksperymenty w C lub w Octave, pokazujące koszt metody w zależności od
oraz w zależności od . Przeprowadź też sekwencję testów
potwierdzających twoje rachunki co do oczekiwanej dokładności (porównując się z
funkcją biblioteczną). W C możesz korzystać ze stałej M_E
, zdefiniowanej w pliku nagłówkowym math.h
.
Ćwiczenie: Ciag dalszy
Spróbuj obniżyć koszt wyznaczania dla dużych .