Mini tutorial Pythona

 

Krótkie wprowadzenie do Pythona poprzez analizę dośc prostych przykładów prezentujących wybrane konstrukcje w tym języku.

08. Funkcja

Napiszemy bardzo prostą funkcję, która jako parametry bierze współrzędne końców odcinka i zwraca jego środek, oraz długość. Czyli na wejście funkcja dostaje dwie krotki, zwraca natomiast krotkę i liczbę zmiennoprzecinkową.

Wzór na współrzędne środka S odcinka AB:
`x_S = (x_A + x_B)/2`, `y_S = (y_A + y_B)/2`.
Wzór na długość odcinka AB:
`d = sqrt((x_B - x_A)^2 + (y_B - y_A)^2)`.

W definicji funkcji występuje słowo kluczowe def, po którym podajemy nazwę funkcji listę parametrów w nawiasach okrągłych i dwukropek. Ciało funkcji występuję w bloku, który zaznaczamy poprzez wcięcia. Jeśli funkcja ma coś zwracać, stosujemy słowo kluczowe return.

#demo_funkcji.py

#definiujemy funkcję odcinek

def odcinek(A, B):
    #wyznaczanie wspol. srodka odcinka
    xS = (A[0] + B[0])/2.0
    yS = (A[1] + B[1])/2.0
    S = (xS, yS)

    #wyznaczanie dlugosci odcinka
    dx = B[0] - A[0]
    dy = B[1] - A[1]
    d = (dx**2 + dy**2)**0.5

    # Funkcja zwraca dwie wartosci -
    # wspol. srodka odcinka i jego dlugosc
    return S, d

#------------------------------------------
#wywolanie funkcji odcinek

S, d = odcinek((1,1), (3,3))
print S, d

S, d = odcinek((1,1), (4,5))
print S, d

W powyższym programie funkcja odcinek wywołana jest dwa razy. Program wydrukuje wynik:

(2.0, 2.0) 2.82842712475
(2.5, 3.0) 5.0

10. Pętla for

Przeanalizujmy program w języku Python wyznaczający wartość liczby π, do obliczenia której wykozystamy szereg Gregory-Leibnitza:

π = `4 - 4/3 + 4/5 -4/7 +4/9 - 4/11 + ...`

#obliczanie liczby pi

def pi1(n):
    s = 0
    for i in range(1,n+1):
        s = s + 1.0*(-1)**(i+1)*4/(2*i-1)
    return s

print (pi1(10))
print (pi1(20))
print (pi1(30))

print (pi1(1000000))

Program zwróci wyniki:

3.04183961893
3.09162380667
3.1082685667
3.14159165359

Pewną wersję tego programu możemy także znależć na anacondzie lub na githubie.

Zacznijmy od pętli for. Wykona się ona zadaną ilość razy. Tą ilość razy wyznacza tutaj funkcja range. Funkcja range zwraca listę. Na przykład, jeśli n będzie miała wartość 10, to wywołanie range(1, 11) zwróci następującą listę:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Pętla for wykona się wtedy 10 razy. Bieżąca wartość zmiennej i będzie wstawiana do wyrażenia:

s = s + 1.0*(-1)**(i+1)*4/(2*i-1)

tak, że zmienna s będzie kolejno przyjmować wartości:

4.0
2.66666666667
3.46666666667
2.89523809524
3.33968253968
2.97604617605
3.28373848374
3.01707181707
3.25236593472
3.04183961893
3.04183961893

Widzimy, że bardzo powoli wartość zmiennej s zbliża się prawdziwej wartości liczby π. Po 10 iteracjach osiągamy wartość w okolicach 3.04, czyli dość daleko od 3.14.
A chcielibyśby osiągnąć przynajmiej 3.14159 - ciekawe, ile razy pętla for musi się wykonać?

    Co by było gdyby nasze wyrażenie na obliczanie kolejnych wyrazów szeregu wyglądało tak:

        s = s + (-1)**(i+1)*4/(2*i-1)

zamiast

        s = s + 1.0*(-1)**(i+1)*4/(2*i-1)
czyli wyrazy nie byłyby mnożone przez 1.0.

Otrzymalibyśmy wtedy następujące wyniki:

-2
-7
-12
-499997

Jak widać występowanie wartości zmiennoprzecinkowej 1.0 jako czynnika w tym wyrażeniu jest absolutnie potrzebne. Ponieważ w przeciwnym razie, gdy operator dzielenia '/' ma operandy tylko całkowite, wynik dzielenia jest liczbą całkowitą i mamy katastrofę obliczeniową.
Bo w dzieleniu całkowitym mamy: `4/3 = 1`, `4/5 = 0`, itd.
Jeśli natomiast momnożymy te ilorazy przez 1.0 to mieli normalne dzielenie zmiennoprzecinkowe:
`1.0*4/3 = 1.33333333333` , `1.0*4/3 = 0.571428571429`, itd

    Trzeba pamiętać o zainicjowaniu zmiennej s przed pętlą. Brak instrukcji

s = 0
spowoduje błąd wykonania programu.


11. Pętla while

Celem poniższego programu nie jest obliczanie wartości liczby π, ale testowanie szybkości zbieżności szeregu Gregory-Leibnitza. Użyjemy tu pętli while. W naszym kodzie pętla while będzie działać tak długo, dopóki różnica pomiędzy zadaną z góry wartością liczby π czyli zmienną number, a naszym przybliżeniem s jest większa od zadanej wartości error. Pętla działą wewnątrz funkcji pi2, która jako parametry bierze tą zadaną wartość (number) oraz różnicę (error). Funkcja zwróca trzy wartości:

  • zadany błąd, różnicę - error
  • obliczaną wartość liczby π - s
  • liczbę iteracji potrzebnych by wyjść z pętli - i

#number_pi_2
#obliczanie liczby p
#pętla while

number = 3.14159

def pi2(number, error):
    s = 0
    i = 1
    while abs(number-s)>error:
        s = s + 1.0*(-1)**(i+1)*4/(2*i-1)
        i+=1
    return error, s, i

print '%10s %20s %10s' % pi2(number, 0.1)
print '%10s %20s %10s' % pi2(number, 0.01)
print '%10s %20s %10s' % pi2(number, 0.001)
print '%10s %20s %10s' % pi2(number, 0.0001)
print '%10f %20s %10s' % pi2(number, 0.00001)
print '%10f %20s %10s' % pi2(number, 0.000001)

A oto wynik działania tego programu:

     0.1        3.04183961893         11
    0.01        3.13159290356        101
   0.001        3.14059064983        999
  0.0001        3.14149000526       9743
0.000010        3.14158000017      79031
0.000001        3.14158900001     273705

Trzeba więc 273705 iteracji by nasza obliczana wartość liczby π, która wyniosła 3.14158900001, różniła się mniej niż o `1/1000000` od zadanej wartości: 3.14159.

    Trzeba pamiętać o zainicjowaniu zmiennej s i i przed pętlą.


...
    s = 0
    i = 1
    while abs(number-s)>error:
        s = s + 1.0*(-1)**(i+1)*4/(2*i-1)
        i+=1
    ...

Warto zakomentować te inizjalizacje (wystarczy jędną) by zobaczyć co się stanie). Na przykład jeśli zmienna s nie będzie zainicjalizowana, program się wysypie i dostaniemy komunikat błędu: UnboundLocalError: local variable 's' referenced before assignment

Zobaczymy na obrazku jak to wygląda to zbliżanie się do wartości 3.14 po 50 iteracjach.

Obrazek został wyprodukowany za pomocą następującego kodu. Nie należy się tym teraz przjmować. Do tworzenia wykresów przejdziemy póżniej,

# -*- coding: utf-8 -*-
"""
Created on Mon May 08 20:15:30 2017

@author: Andrzej
"""
#obliczanie liczby pi

def pi1(n):
    Y=[]
    X=[]
    Z=[]
    s = 0
    for i in range(1,n+1):
        s = s + 1.0*(-1)**(i+1)*4/(2*i-1)
        X.append(i)
        Y.append(round(s,2))
        Z.append(3.14)
    return X, Y, Z        

#50 iteracji            
X, Y, Z = pi1(50)

from bokeh.plotting import figure, output_file, show

output_file("liczba_pi1.html")

p = figure(title="Wyznaczanie liczby Pi", x_axis_label='x', y_axis_label='y')
p.line(X, Y, legend="Przybliżanie sie do Pi", line_width=2)
p.line(X, Z, legend="Pi = 3.14.", line_width=2, line_color="red")
# show the results
show(p)

Tak, to było takie błądzenie wokół liczby π.


12. Moduły