Błędy obliczeń numerycznych w języku Python
Wprowadzenie Przeczytaj Film samouczek Sprawdź się Dla nauczyciela
Komputery to w naszym mniemaniu bardzo dokładne i precyzyjne urządzenia, jednak w swoich obliczeniach narażone są na różne rodzaje błędów. Te błędy to między innymi:
ograniczona dokładność danych źródłowych, konwersja pomiędzy systemami liczbowymi, zaokrąglenia spowodowane reprezentacją danych,
trudności wykonywania operacji na bardzo małych i dużych liczbach.
W tym e‐materiale przeanalizujemy błąd względny jako kryterium zakończenia procedury iteracyjnej.
Twoje cele
Stworzysz funkcję wspomagającą obliczanie błędu względnego.
Przygotujesz program wizualizujący wartość błędu względnego dla zaokrągleń.
Źródło: Nick Hillier, domena publiczna.
Błędy obliczeń numerycznych w języku Python
Przeczytaj
Wyobraźmy sobie sytuację, w której musimy dopompować powietrzem koło w samochodzie, a nie mamy dostępu do sterowanej cyfrowo stacji - dysponujemy tylko ręcznym manometrem. Czy łatwo będzie nam ustalić ciśnienie w oponie na 2,2 barów? Nie, ponieważ ręczny manometr nie jest wystarczająco dokładny.
Istnieją tu zatem dwie wartości ciśnienia:
wartość zmierzona, którą odczytujemy z ręcznego manometru,
wartość dokładna, której nie znamy; moglibyśmy ją poznać, gdybyśmy dysponowali precyzyjnym, elektronicznym manometrem.
Różnicę między tymi wartościami nazywamy błędem bezwzględnym, a stosunek tego błędu do wartości dokładnej - błędem względnym.
Definicja: błąd bezwzględny
Różnica pomiędzy wartością dokładną a względną (np. zmierzoną lub po zaokrągleniu). Informuje, o ile różni się wartość zmierzona od dokładnej.
Możemy zapisać równanie:
– wartość dokładna – wartość zmierzona Definicja: błąd względny
Stosunek błędu bezwzględnego do wartości dokładnej, czasami wyrażany również w procentach.
Możemy zapisać równanie obliczające wartość:
lub też obliczające procent:
– błąd względny – wartość dokładna
– wartość zmierzona
Przykład 1
Znając powyższe wzory, możemy napisać funkcje obliczające odpowiednie wartości.
Δx = |x − x
0| x
x
0δ =
Δxx=
|x−xx 0|δ =
Δxx∗ 100 Δx
x x
0def blad_bezwzgledny(x, x0):
""" x - wartość dokładna x0 - wartość zmierzona"""
from math import fabs return fabs(x-x0)
def blad_wzgledny(x, x0):
""" x - wartość dokładna x0 - wartość zmierzona"""
d = blad_bezwzgledny(x,x0) return d / x
def blad_wzgledny_proc(x, x0):
""" x - wartość dokładna x0 - wartość zmierzona"""
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Dla zainteresowanych
W języku Python istnieje moduł o nazwie Decimal. Zapewnia on obsługę poprawnie zaokrąglonej arytmetyki zmiennoprzecinkowej.
Przeanalizujmy teraz sytuację, w której symulujemy „ręczne” obliczenie wartości za pomocą zaokrąglenia. Prześledźmy błąd bezwzględny oraz względny dla kolejnych zaokrągleń liczby pi.
W naszym przykładzie mamy podaną wartość liczby pi z dokładnością do 42 miejsc po przecinku. W języku Python, w module math, jest zapisana wartość liczby pi jako math.pi z dostępną precyzją - w tym przypadku 3.141592653589793. Będziemy obliczali błąd bezwzględny i względny oraz błąd względny wyrażony w procentach dla kolejnych przybliżeń aż do dwóch miejsc po przecinku, a więc do wartości 3.14.
Przykład 2
Napiszmy kod, który pozwoli nam zobrazować błąd w zależności od kolejnych zaokrągleń. Przygotujemy dwa wykresy w celu odpowiedniego doboru skali.
Wykres pierwszy – błąd względny/bezwzględny wartościowo:
π ≈ 3, 141592653589793238462643383279502884197169. . .
d = blad_bezwzgledny(x,x0) return (d / x) * 100
# przykładowe wywołanie
print(blad_bezwzgledny(2.18, 2.2)) 0.020000000000000018
print(blad_wzgledny(2.18, 2.2)) 0.009174311926605512
print(blad_wzgledny_proc(2.18, 2.2)) 0.9174311926605512
import matplotlib.pyplot as plt import math
wart_pi = math.pi bl_bezw = []
bl_wzgl = []
bl_wzgp = []
X = [ x for x in range(13) ]
for prz in range(14, 1, -1):
q = round(wart_pi, prz)
bl_bezw.append(blad_bezwzgledny(wart_pi, q)) bl_wzgl.append(blad_wzgledny(wart_pi, q)) bl_wzgp.append(blad_wzgledny_proc(wart_pi, q))
# pierwszy wykres
plt.plot(X, bl_bezw, label="Błąd bezwzględny") plt.plot(X, bl_wzgl, label="Błąd względny") plt.legend()
plt.title(f"Wartość bazowa pi = {math.pi}.") plt.grid(True)
plt.show()
# drugi wykres
plt.plot(X, bl_wzgp, label="Błąd względny procentowo") plt.legend()
plt.title(f"Wartość bazowa pi = {math.pi}.") plt.grid(True)
plt.show() 18
19 20 21 22 23 24 25 26 27 28 29
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Wykres drugi – błąd względny procentowo:
Ważne!
Możemy z łatwością zauważyć, że błąd przybliżenia (zarówno względny, jak i bezwzględny) do pewnego momentu jest bardzo mały, natomiast w ostatnich przybliżeniach nagle gwałtownie wzrasta. Dzięki tej obserwacji program obliczający przybliżenie możemy zmodyfikować w ten sposób, aby przerwał w momencie, gdy błąd względny przekroczy pewną zdefiniowaną przez nas granicę.
Przykład 3
Napiszmy kod, który pozwoli nam zaokrąglić liczbę pi do takiego przybliżenia, dla którego błąd względny nie przekroczy 0,01 procenta.
import matplotlib.pyplot as plt import math
wart_pi = math.pi przyblizenia = []
bl_bezw = []
bl_wzgl = []
bl_wzgp = []
for prz in range(14, 1, -1):
q = round(wart_pi, prz)
if blad_wzgledny_proc(wart_pi, q) > 0.01:
ok = f"Ostatni wynik w założonym błędzie = {przyblizenia[-1]}."
break else:
bl_bezw.append(blad_bezwzgledny(wart_pi, q)) bl_wzgl.append(blad_wzgledny(wart_pi, q)) bl_wzgp.append(blad_wzgledny_proc(wart_pi, q)) 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Wykres pierwszy – błąd względny/bezwzględny wartościowo:
Wykres drugi – błąd względny procentowo:
Już wiesz
Na koniec podsumujmy najważniejsze wnioski:
błąd względny może wynikać z różnych przyczyn,
wartość procentowa błędu względnego może być elementem granicznym dla funkcji iteracyjnej.
przyblizenia.append(q)
# przygotowanie wartości X bazując na długości listy przybliżeń X = [ x for x in range(len(przyblizenia)) ]
# pierwszy wykres
plt.plot(X, bl_bezw, label="Błąd bezwzględny") plt.plot(X, bl_wzgl, label="Błąd względny") plt.legend()
plt.title(f"Wartość bazowa pi = {math.pi}." + ok) plt.grid(True)
plt.show()
# drugi wykres
plt.plot(X, bl_wzgp, label="Błąd względny procentowo") plt.legend()
plt.title(f"Wartość bazowa pi = {math.pi}." + ok) plt.grid(True)
plt.show() 19
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
Słownik
manometr
przyrząd do pomiaru ciśnienia gazów i cieczy wyższego od ciśnienia atmosferycznego bar
jednostka miary ciśnienia w układzie CGS
Film samouczek
Polecenie 1
Przeanalizuj pętlę iteracyjną wykonującą przybliżenie liczby dziesiętnej na przykładzie stałej e i wykorzystaj błąd względny jako kryterium zakończenia procedury.
Przygotujemy funkcję, która będzie przyjmować dwa param dziesiętną oraz założony procent błędu względnego, któreg przekroczenie zakończy proces przybliżania. Wewnątrz fun umieścimy licznik wykonanych iteracji oraz wyświetlenie k przybliżeń.
def przyblizenie(liczba, procent):
"""liczba: wartość float procent: wartość float"""
def blad_wzgledny_proc(x, x0):
""" x - wartość dokładna x0 - wartość zmierzona"""
from math import fabs
return (fabs(x-x0) / x) * 100
procent_wzgledny = 0 liczba_przyblizona = 0
# liczymy ilość cyfr w części ułamkowej ile_cyfr = len(str(liczba).split(".")[1]
wynik_l = 0 wynik_p = 0 krok = 1
while procent_wzgledny < procent:
ile_cyfr -= 1
liczba_przyblizona = round(liczba, i procent_wzgledny = blad_wzgledny_pro print(f"Krok {krok}: przybliżenie to if procent_wzgledny < procent:
wynik_l = liczba_przyblizona wynik_p = procent_wzgledny krok += 1
print(f"Wynikiem jest liczba {wynik_l}, Do naszej prezentacji użyjemy stałej e oraz ustalimy granicz błędu względnego jako 0,00005.
import math print(math.e) 2.718281828459045
przyblizenie(2.718281828459045, 0.00005) Wewnątrz funkcji inicjujemy ważne elementy; obliczamy pa ile_cyfr, który pozwoli nam zautomatyzować dokonywan przybliżeń.
procent_wzgledny = 0 liczba_przyblizona = 0
# liczymy ilość cyfr w części ułamkowej ile_cyfr = len(str(liczba).split(".")[1]
wynik_l = 0 wynik p = 0
wynik_p = 0 krok = 1
Obliczenie liczby cyfr wykonujemy, zamieniając liczbę floa str, który dzielimy metodą split na listę dwuelementową (wykorzystując znak kropki jako separator), a potem sprawd długość napisu po kropce.
ile_cyfr = len(str(liczba).split(".")[1]) Rozpoczynamy pętlę, którą będziemy wykonywali do mome przekroczymy zakładany procent błędu, i w której przeprow odpowiednie obliczenia; jeśli obliczony procent nie przekr zakładanego progu, zapiszemy wynik w zmiennych pomocn i zwiększymy licznik kroków.
while procent_wzgledny < procent:
ile_cyfr -= 1
liczba_przyblizona = round(liczba, i procent_wzgledny = blad_wzgledny_pro print(f"Krok {krok}: przybliżenie to if procent_wzgledny < procent:
wynik_l = liczba_przyblizona wynik_p = procent_wzgledny krok += 1
W pierwszym kroku otrzymamy następującą wartość:
Krok 1: Przybliżenie to 2.71828182845905, a W kolejnym kroku otrzymamy następującą wartość:
Krok 2: przybliżenie to 2.718281828459, a pr W kolejnym kroku otrzymamy następującą wartość:
Krok 3: przybliżenie to 2.718281828459, a pr Należy zauważyć, że wartość ta jest identyczna z poprzedn ponieważ w poprzednim wypadku na końcu występowało 0 zostało wypisane na ekranie funkcją print.
W kolejnym kroku otrzymamy następującą wartość:
Krok 4: przybliżenie to 2.71828182846, a pro W kolejnym kroku otrzymamy następującą wartość:
Krok 5: przybliżenie to 2.7182818285, a proc W kolejnym kroku otrzymamy następującą wartość:
Krok 6: przybliżenie to 2.718281828, a proce W kolejnym kroku otrzymamy wartość:
Krok 7: przybliżenie to 2.71828183, a procen W kolejnym kroku otrzymamy następującą wartość:
Krok 8: przybliżenie to 2.7182818, a procent W kolejnym kroku otrzymamy następującą wartość:
Krok 9: przybliżenie to 2.718282, a procent W kolejnym kroku otrzymamy następującą wartość:
Krok 10: przybliżenie to 2.71828, a procent W tym momencie procent błędu przekracza zakładaną war i a pętla kończy swoje działanie; jednak wartości końcowe z
Polecenie 2
Wykonaj program przedstawiony na filmie.
Film dostępny na portalu epodreczniki.pl
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.
Film nawiązujący do treści lekcji dotyczący implementacji algorytmu wyznaczania pierwiastka przy pomocy metody siecznych w języku Python.
wyświetlone przy wykorzystaniu wartości pomocniczych, a naprawdę będą to wartości z poprzedniego kroku.
Wynikiem jest liczba 2.718282, dla której pr
Sprawdź się
Pokaż ćwiczenia: 輸醙難
Ćwiczenie 1
Wykonaj poniższy kod w wybranym środowisku języka Python. Sprawdź wynik, zaznacz prawidłową odpowiedź, a następnie zastanów się, dlaczego kod daje taki wynik.
Zapisz swoje spostrzeżenia.
Wskaż wynik działania kodu.
błąd exception OverflowError
[0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048, 0.0048]
[0, 0, 0, 0.0074, 0.0019, 0.0081, 0.0081, 0.002, 0.0055, 0.0084, 0.0003, 0.0066, 0.0049, 0.0021, 0.0023, 0.0086, 0.0056, 0.0006, 0.0077, 0.0021, 0.0001, 0.0022, 0.0066]
def spr_pr_prz(x, x0):
from math import fabs
return round((fabs(x - x0) / x) * 100, 4)
def sr_wzrost(lista, zaokr):
sr_x = sum(lista)/len(lista) sr_x0 = round(sr_x, zaokr) return spr_pr_prz(sr_x, sr_x0)
ll = []
lq = []
for i in range(20):
ll.append(4.77777) q1 = sr_wzrost(ll, 3) lq.append(q1)
print(lq) 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
輸
Ćwiczenie 2
Zdefiniuj funkcję testowa(), która będzie budować dwie wskazane poniżej listy i zwracać je jako tuplę:
listę A kolejnych losowych liczb rzeczywistych, korzystając z funkcji random.uniform(1,5),
listę B kolejnych błędów względnych dla sumy elementów listy A, zaokrąglonych do dwóch miejsc po przecinku (błąd również powinien być zaokrąglony do dwóch miejsc po przecinku); funkcja powinna działać tak długo, jak długo błąd względny będzie mniejszy lub równy 0,14; należy ustawić ziarno random.seed() na wartość 23453.
Przykładowe wywołania:
Zdefiniowanie funkcji testowa()
Sprawdzenie, czy funkcja testowa() dla seed=23453 zwraca odpowiednią wartość.
1. def testowa():
2. # tutaj Twój kod 3. return None 4.
5.
6. # tu przykładowe wywołania 7. wynik = testowa()
8. print(wynik)
9. print(type(wynik) is tuple)
Przedstaw swoje uzasadnienie do kodu napisanej funkcji.
wynik = testowa() # inny seed dla danych przykładowych
print(wynik)
([1.250677100300023, 2.877455656628443]
[0.05, 0.2])
print(type(wynik))
<class 'tuple'>
1 2 3 4 5 6 7 8
醙
Ćwiczenie 3
Zdefiniuj funkcję testowa(lista_liczb), która dla listy zawierającej minimum cztery elementy typu float obliczy:
procentowy błąd względny w zaokrągleniu do dwóch miejsc po przecinku dla elementów listy zaokrąglonych do jednego miejsca po przecinku (klucz: "A1"),
sumę wszystkich elementów (klucz: "A2"),
średnią arytmetyczną wszystkich elementów (klucz: "A3"),
średnią arytmetyczną parzystych elementów (klucz: "A4"), element o indeksie 0 jest parzysty,
średnią arytmetyczną nieparzystych elementów (klucz: "A5"), bez elementu o indeksie 0.
Funkcja powinna sprawdzić następujące dane wejściowe:
czy parametr jest parametrem typu list,
czy zawiera minimum cztery elementy,
czy każdy z elementów jest typem float.
Jeśli którykolwiek z warunków nie będzie spełniony, funkcja powinna zwrócić wartość False.
Funkcja powinna zwracać słownik (dict) zawierający te wartości w odpowiednich kluczach.
Przykładowe wywołania:
Zdefiniowanie funkcji testowa()
Sprawdzenie, czy funkcja testwa() dla parametru "Python" zwraca wartość False.
Sprawdzenie, czy funkcja testwa() dla parametru [1.0, "A", 2.0, 3.0, 4, 5] zwraca wartość False.
Sprawdzenie, czy funkcja testwa() dla podanego poprawnego parametru zwraca poprawną wartość.
def testowa(lista_liczb):
# tutaj Twój kod return None
wynik = testowa(1) print(wynik)
print(type(wynik) is bool) False
True
wynik = testowa([1.0, "A", 2.0, 3.0]) print(wynik)
print(type(wynik) is bool) False
True
wynik = testowa([3.6018327867100246, 4.962810995605217, 3.229473128048408, 2.1292589089151415, 4.4949 print(wynik)
print(type(wynik) is dict)
{'A1': 0.28, 'A2': 21.059674796705764, 'A3': 3.0085249709579664, 'A4': 3.1318172738722185, 'A5': 2.84 True
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
難
# tu przykładowe wywołania wynik = testowa(1)
print(wynik)
print(type(wynik) is bool)
wynik = testowa([3.6018327867100246, 4.962810995605217, 3.229473128048408, 2.1292589089151415, 4.494972947818487, 1.44033579 print(wynik)
print(type(wynik) is dict)
Przedstaw swoje uzasadnienie do kodu napisanej funkcji.
Dla nauczyciela
Autor: Adam Jurkiewicz Przedmiot: Informatyka
Temat: Błędy obliczeń numerycznych w języku Python Grupa docelowa:
III etap edukacyjny, liceum ogólnokształcące, technikum, zakres rozszerzony Podstawa programowa:
Zakres podstawowy i rozszerzony Cele kształcenia – wymagania ogólne
1) Rozumienie, analizowanie i rozwiązywanie problemów na bazie logicznego i abstrakcyjnego myślenia, myślenia algorytmicznego i sposobów reprezentowania informacji.
Zakres rozszerzony
I. Rozumienie, analizowanie i rozwiązywanie problemów. Uczeń spełnia wymagania określone dla zakresu podstawowego, a ponadto:
7. wyjaśnia, jakie może być źródło błędów pojawiających się w obliczeniach komputerowych: błąd zaokrąglenia, błąd przybliżenia;
Kształtowane kompetencje kluczowe:
kompetencje obywatelskie;
kompetencje cyfrowe;
kompetencje osobiste, społeczne i w zakresie umiejętności uczenia się;
kompetencje matematyczne oraz kompetencje w zakresie nauk przyrodniczych, technologii i inżynierii.
Cele operacyjne ( językiem ucznia):
Stworzysz funkcję wspomagającą obliczanie błędu względnego.
Przygotujesz program wizualizujący wartość błędu względnego dla zaokrągleń.
Strategie nauczania:
konstruktywizm;
konektywizm.
Metody i techniki nauczania:
dyskusja;
rozmowa nauczająca z wykorzystaniem multimedium i ćwiczeń interaktywnych;
metody aktywizujące.
Formy pracy:
praca indywidualna;
praca w parach;
praca w grupach;
praca całego zespołu klasowego.
Środki dydaktyczne:
komputery z głośnikami, słuchawkami i dostępem do internetu;
zasoby multimedialne zawarte w e‐materiale;
tablica interaktywna/tablica, pisak/kreda;
telefony z dostępem do internetu;
oprogramowanie dla języka Python 3 (lub nowszej wersji), w tym PyCharm lub IDLE.
Przebieg lekcji Przed lekcją:
1. Przygotowanie do zajęć. Nauczyciel loguje się na platformie i udostępnia e‐materiał: „Błędy obliczeń numerycznych w języku Python”. Nauczyciel prosi uczniów o zapoznanie się z treściami w sekcji „Przeczytaj” dotyczącymi programowania.
Faza wstępna:
1. Nauczyciel wprowadza uczniów szczegółowo w temat lekcji i jej cele. Może posłużyć się wyświetloną na tablicy zawartością sekcji
„Wprowadzenie”.
2. Rozpoznanie wiedzy uczniów. Nauczyciel prosi wybranego ucznia lub uczniów o przedstawienie sytuacji problemowej związanej z tematem lekcji.
Faza realizacyjna:
1. Uczniowie analizują przykład z sekcji „Przeczytaj” i powtarzają zaprezentowane rozwiązanie na swoim komputerze.
2. Praca z multimedium. Nauczyciel czyta polecenie nr 1 „Przeanalizuj pętlę iteracyjną wykonującą przybliżenie liczby dziesiętnej na przykładzie stałej e i wykorzystaj błąd względny jako kryterium zakończenia procedury.” w sekcji „Film samouczek”. Prosi uczniów, aby w parach przeanalizowali rozwiązanie problemu. Uczniowie odtwarzają kolejne kroki na swoich komputerach.
3. Ćwiczenie umiejętności. Uczniowie wykonują indywidualnie ćwiczenie nr 1, a następnie porównują swoje odpowiedzi z kolegą lub koleżanką.
4. W kolejnym etapie uczniowie dobierają się w pary i wykonują ćwiczenia nr 2 i 3. Następnie konsultują swoje rozwiązania z inną parą uczniów i ustalają jedną wersję odpowiedzi.
Faza podsumowująca:
1. Na koniec zajęć nauczyciel raz jeszcze wyświetla na tablicy temat lekcji i cele zawarte w sekcji „Wprowadzenie”. W odniesieniu do ich realizacji dokonuje szczegółowej oceny rozwiązania zastosowanego przez wybranego ucznia.
2. Wybrany uczeń podsumowuje zajęcia, zwracając uwagę na nabyte umiejętności, omawia ewentualne problemy podczas rozwiązania ćwiczeń z programowania w języku Python.
Praca domowa:
1. Uczniowie wykonują polecenie nr 2 z sekcji „Film samouczek”.
Materiały pomocnicze:
Oficjalna dokumentacja techniczna dla języka Python 3 (lub nowszej wersji).
Oficjalna dokumentacja techniczna dla oprogramowania PyCharm lub IDLE.
Wskazówki metodyczne:
Uczniowie mogą wykorzystać multimedium w sekcji „Film samouczek” do przygotowania się do lekcji powtórkowej.