Zauważmy, szukaj

Transkrypt

Zauważmy, szukaj
Wstęp do programowania
Wykład 5 ­ Podstawowe techniki programownia w
przykładach
Janusz Szwabiński
Plan wykładu:
Metoda babilońska wyliczania pierwiastka
Liczby pierwsze i sito Eratostenesa
Metoda bisekcji
Sortowanie bąbelkowe
Metoda babilońska wyliczania pierwiastka
Szukamy aproksymacji √S dowolnej liczby nieujemnej S . Jednym z pierwszych algorytmów jest metoda
opisana przez matematyka greckiego Herona z Aleksandrii. Przypuszcza się jednak, że była znana już
wcześniej i stosowana w Babilonii. Metoda składa się z następujących kroków:
1. Rozpocznij z dowolną dodatnią wartością początkową x 0 (im bliżej szukanego pierwiastka, tym
lepiej).
2. Znajdź kolejne przybliżenie według wzoru
1
x n+1 =
2
S
(x n +
)
xn
3. Powtarzaj krok 2 do osiągnięcia pożądanej dokładności.
Wzór z kroku 2 można dość łatwo wyprowadzić. Zauważmy mianowicie, że jeżeli x jest przybliżeniem √S z
błedem ϵ, to
2
S = (x + ϵ)
Rozwijając dwumian i rozwiązując względem ϵ otrzymamy (przy założeniu ϵ
S − x
2
ϵ =
S − x
≃
2
.
2x + ϵ
2x
Przybliżenie x możemy zatem poprawić o błąd ϵ:
S + x
x → x + ϵ =
x +
=
2x
Zaimplementujmy teraz ten algorytm w Pythonie:
2
2
S
x
≪ x ):
In [1]:
S = 345
eps = 10e-6 #błąd przybliżenia
old = 1.0
new = 0.5*(1+S) #nowe przybliżenie dla old=1
while abs(new-old)>eps:
old, new = new, 0.5*(new + S/new)
print(new)
18.57417562100671
In [2]:
import math
print(math.sqrt(S))
18.57417562100671
Powyższa implementacja metody babilońskiej działa, jest jednak mało elegancka. Chcąc policzyć wartość
pierwiastka innej liczby, musimy wyedytować kod, zmienić w nim wartość zmiennej S i uruchomić całość raz
jeszcze. Dlatego lepiej jest opakować powyższy kod w funkcję:
In [3]:
def heron_sq(s, eps):
old, new = 1.0, 0.5 * (1 + s)
while abs(new - old) > eps:
old, new = new, 0.5 * (new + s/new)
return new
Teraz możemy wywoływać go dla dowolnych wartości S i dokładności eps :
In [4]:
heron_sq(S,eps)
Out[4]:
18.57417562100671
In [5]:
heron_sq(S,10e-2)
Out[5]:
18.57421350446529
In [6]:
heron_sq(25,10e-8)
Out[6]:
5.0
In [9]:
heron_sq(25,10e-2)
Out[9]:
5.000023178253949
Liczby pierwsze i sito Eratostenesa
Sito Eratostenesa (https://pl.wikipedia.org/wiki/Sito_Eratostenesa
(https://pl.wikipedia.org/wiki/Sito_Eratostenesa)) to przypisywany Eratostenesowi z Cyreny algorytm
wyznaczania liczb pierwszych z przedziału [2, n] . Metoda składa się z następujących kroków:
1. Ze zbioru liczb naturalnych z przedziału [2, n] , tzn. ze zbioru {2, 3, 4, … , n} wybieramy
najmniejszą, czyli 2, i wykreślamy wszystkie jej wielokrotności większe od niej samej, to jest 4, 6, 8, … .
2. Z pozostałych liczb wybieramy najmniejszą niewykreśloną, czyli 3 i wykreślamy wszystkie jej
wielokrotności.
3. Bierzemy kolejną najmniejszą liczbą niewykreśloną i usuwamy jej wielokrotności.
4. Powtarzamy krok 3 dla pozostałych liczb niewykreślonych.
Najprostsza implementacja w Pythonie mogłaby wyglądać tak:
In [39]:
n = 15
primes = [True]*(n+1)
jemy jako pierwsze)
#interesuje nas przedział [2,n]
#tablica liczb pierwszych (na początku wszystkie traktu
#zbudowana w ten sposób, że indeks elementu jest równy
rozważanej liczbie
for i in range(2,n+1): #zaczynamy od 2
if primes[i]:
#True oznacza liczbę niewykreśloną
for j in range(i+i,n+1,i): #pętla usuwająca wieloktrotności
primes[j] = False
#zaznaczamy usunięcie
for i in range(2,n+1):
ne
if primes[i]:
print(i)
#wyświetlamy na ekran wszystko, co nie zostało wykreślo
2
3
5
7
11
13
Podobnie, jak w poprzednim przykładzie, możemy zdefiniować własną funkcję:
In [41]:
def erat(n):
primes = [True]*(n+1)
for i in range(2,n+1):
if primes[i]:
for j in range(i+i,n+1,i):
primes[j] = False
result = []
for i in range(2,n+1):
if primes[i]:
result.append(i)
return result
In [42]:
erat(3)
Out[42]:
[2, 3]
In [43]:
erat(10)
Out[43]:
[2, 3, 5, 7]
In [44]:
erat(20)
Out[44]:
[2, 3, 5, 7, 11, 13, 17, 19]
Nie jest to oczywiście najbardziej wydajna implemetacja (przegląd takich można znaleźć pod adresem
http://www.macdevcenter.com/pub/a/python/excerpt/pythonckbk_chap1/index1.html?page=2
(http://www.macdevcenter.com/pub/a/python/excerpt/pythonckbk_chap1/index1.html?page=2)). Możemy ją
jednak usprawnić niewielkim nakładem sił. Przeglądanie tablicy można mianowicie przerwać już dla i = √n :
In [45]:
import math
def erat2(n):
imax = int(math.sqrt(n))+1
primes = [True]*(n+1)
for i in range(2,imax+1): #tu nastąpiła zmiana
if primes[i]:
for j in range(i+i,n+1,i):
primes[j] = False
result = []
for i in range(2,n+1):
if primes[i]:
result.append(i)
return result
In [46]:
erat2(20)
Out[46]:
[2, 3, 5, 7, 11, 13, 17, 19]
In [47]:
erat2(4)
Out[47]:
[2, 3]
Metoda bisekcji
Metoda bisekcji (połowienia przedziału,
https://pl.wikipedia.org/wiki/Metoda_r%C3%B3wnego_podzia%C5%82u
(https://pl.wikipedia.org/wiki/Metoda_r%C3%B3wnego_podzia%C5%82u)) to jedna z metod rozwiązywania
równań nieliniowych, opierająca się na twierdzeniu Bolzano­Cauchy'ego:
Jeżeli funkcja ciągła f (x) ma na końcach przedziału domkniętego wartości różnych znaków,
to wewnątrz tego przedziału istnieje co najmniej jeden pierwiastek równania f (x) = 0.
Aby można było stosować tę metodę, muszą być więc spełnione następujące warunki:
1. Funkcja f (x) jest ciągła w przedziale domkniętym [a, b].
2. Funkcja przyjmuje różne znaki na końcach przedziału, tzn.:
f (a)f (b) < 0
Algorytm składa się z następujących kroków:
1. Sprawdź, czy x 1
=
a+b
2
jest pierwiastkiem równania, tzn. czy f (x 1 )
= 0 . Jeżeli tak, algorytm
kończy działanie, a punkt x 1 jest poszukiwanym rozwiązaniem.
2. W przeciwnym razie, dopóki nie osiągniemy pożądanej dokładności, tzn. dopóki |a − b|
> ϵ:
Zgodnie ze wzorem z punktu 1 ponownie wyznacz x 1 .
Traktując x 1 jako punkt podziału przedziału wyjściowego, wybierz podprzedział, w którym
leży miejsce zerowe:
jeżeli f (x 1 )f (a) < 0, to b = x 1,
jeżeli f (x 1 )f (b) < 0, to a = x 1 .
3. Powtarzaj punkt drugi do osiągnięcia żądanej dokładności
Prosta implementacja tego algorytmu w Pythonie może wyglądać tak:
In [68]:
def bisec(fun,a,b,eps):
low, high = a, b
feps = 10e-16 #dokładność, z jaką przyrównamy f(x) do 0
if fun(low)*fun(high) > 0:
print("Podano błędny przedział!")
return
while abs(low-high)>eps:
midpoint = (low + high)/2
if abs(fun(midpoint)) < feps : #sprawdź, czy to rozwiązanie
return midpoint
if fun(low)*fun(midpoint)>0:
low = midpoint
else:
high = midpoint
return midpoint
Zauważmy, że z metody tej możemy skorzystać przy szukaniu pierwiastka z liczby nieujemnej. Z faktu, że
√S = x
wynika mianowicie
x
czyli otrzymaliśmy postać f (x)
In [69]:
def f(x):
return x**2 -2
In [72]:
bisec(f,1,2,10e-6)
Out[72]:
1.4142074584960938
In [71]:
math.sqrt(2)
Out[71]:
1.4142135623730951
2
− S = 0
= 0 . Wykorzystajmy zatem bisekcję do znalezienia pierwiastka z 2:
In [73]:
bisec(f,0,1,10e-6)
Podano błędny przedział!
Sortowanie bąbelkowe
Sortowanie bąbelkowe to prosta metoda sortowania tablic, polegająca na porównywaniu dwóch kolejnych
elementów w tablicy i zamianie ich kolejności, jeżeli zaburza ona porządek sortowania. Sortowanie kończy
się, gdy podczas kolejnego przejścia nie dokonano żadnej zmiany.
Niezależnie od uporządkowania elementów potrzeba n − 1 przejść przez tablicę, aby w pełni ją posortować.
W każdym przejściu dokonuje się n − k porównań, gdzie k to numer przejścia. Innymi słowy
In [78]:
def bubble(alist):
n = len(alist) #liczba elementów do posortowania
for i in range(n-1): #liczba przejść
for j in range(i+1, n):
if alist[j] < alist[i]:
alist[j], alist[i] = alist[i], alist[j]
In [79]:
alist = [54,26,93,17,77,31,44,55,20]
bubble(alist)
print(alist)
[17, 20, 26, 31, 44, 54, 55, 77, 93]
Zwróćmy przy okazji uwagę, że funkcja bubble dokonuje wszystkich zmian w liście wejściowej! Wrócimy do
tej cechy Pythona na kolejnych wykładach:

Podobne dokumenty