Seminarski rad: UĊenje algoritama u Pythonu

Seminarski rad:
Učenje algoritama u
Pythonu
Autor izvornog teksta koji sam koristio: Pai H.
Chou (University of California, Irvine)
U kojoj mjeri je Python pogodan za učenje izrade
algoritama?
Željko Čejvanović
Prof. Fizike i Tehnike, F-3245
3. god.
[email protected]
5/3/2011
Sadržaj:
Sažetak.......................................................................................2
Uvod............................................................................................2
Uvodna lekcija: Sortiranje............................................................3
HeapSort (gomila) poredaj i prioritet redovi.................................4
Binarna stabla i Hufmann kodiranje............................................5
Grafikon Algoritmi........................................................................7
Zaključak i zašto je Python pogodan za učenje izrade
algoritama?!.................................................................................9
7. Literatura.....................................................................................9
0.
1.
2.
3.
4.
5.
6.
1
Sažetak
Dizajn i analiza algoritama su temeljne teme u
računalnoj znanosti i obrazovanju
na
tehničkim fakultetima. Mnogi tečajevi algoritama uključuju programske zadatke kako bi
učenici bolje razumjeli algoritme. Nažalost, korištenje tradicionalnih programskih jezika
prisiljava studente da se bave detaljima strukture podataka i prateće rutine, a ne dizajnom
algoritama. Python predstavlja algoritam-orijentirani jezik
koji je jako potreban u
obrazovanju. Prednosti Pythona su
njegovi udžbenici poput sintakse i interaktivnost
koja potiče eksperimentiranje. Još važnije,
Python se
može
koristiti
za
zastupanje agregatne strukture
podataka kao što
su grafovi i mreže protoka u sažetom
tekstualnom obliku, koji ne samo da potiče studente da eksperimentiraju s algoritmima,
ali takoĎer dramatično smanjuje vrijeme
razvoja. Ove značajke su provedene
u studiju
algoritama naravno s uspješnim rezultatima.
1. Uvod
Algoritmi su najvažniji alati za
svakoga tko mora riješiti probleme pisanja računalnih
programa. Algoritmi se koriste ne samo od strane računalnih znanstvenika i inženjera
računala, nego i mnogi u drugim inženjerskim disciplinama i znanosti. Kao rezultat toga,
studenti raznih smjerova koriste tečajeve algoritama.
Iako je moguće proučavati algoritme samo čitajući knjige i gledajuće rješene primjere, učenici
će ih najbolje shvatiti tako da sami provedu neki algoritam. Kao rezultat toga, to
nije neuobičajeno za tečajeve algoritama
da uključuju programske
zadatke. Udžbenici koji uključuju programiranje kao sastavni dio algoritma obrazovanja
takoĎer su od autora tražili da zadovolji taj zahtjev [4]. Gotovo svi tečajevi i udžbenici su se
do
sada provodili u tradicionalnim jezikom kao što
su C ili C+ +
i Java (nedavno je
stekao popularnost) [5]. Argument za korištenje tih jezika je uglavnom praktična, jer su
studenti vjerojatno već iskusni u tim jezicima i to im daje praktične vještine.
1.1 Programiranje vs Algoritam Design (dizajn algoritama)
Nažalost, iskustva su pokazala da programski zadaci u klasi algoritma ne moraju uvijek
biti pedagoški korisni. Iako
je većina algoritama od nekoliko redaka do
pola
stranice u udžbeniku, njihove implementacije često zahtijevaju stotine redaka u C-u
ili Javi. Jedan
od razloga je da
ti jezici zahtijevaju deklaraciju globalnih varijabli,
lokalne varijable i parametre prije nego što oni mogu koristiti. Drugi razlog, što je još
važnije, je da su mnogi strukturama podataka kao što su popisi, povezane strukture
podataka, kao i specijalizirana polja moraju biti projektirani i izvedeni za podršku
algoritama, i složenost ovih vježbi brzo
raste kad agregatne strukture
podataka kao što
su grafikoni ili protok mreža uključeni. U stvari, većina objektno orijentiranih programera će
potrošiti većinu svojih
napora u projektiranju klase i sučelja, i
trošiti relativno
malo vremena za
punjenje u kodu za metode. Kao rezultat
toga, ovi zadaci
za
programiranje će
prisiliti studente
da
provedu mnogo vremena uz
pitanja
o
programiranju, nego pitanjima za algoritme. Zbog ovoga bi mogli imati problema studenti koji
nisu informatićkog smjera.
2
Neki instruktori pokušavaju ublažiti ovaj teret dajući studentima knjižicu rutine za strukture
podataka. MeĎutim, još
uvijek
postoje mnogi problemi koji su svojstveni ovim jezicima, uključujući ulaz/izlaz i ponovno. Npr.
, biblioteka može
pružiti API za izgradnju stabla ili grafa zazivajući algoritam iz strukture
podataka.
Studenti moraju shvatiti
njihov slučaj
uzastopna poziva na dodavanje
jednog čvora na vrijeme graf, ili pročitati opis grafikona iz datoteke. Bivši pristup može
biti neugodan, jer izvorni
kod ne sliči strukturi
podataka, ali smisao je vezan za API.
Potonji pristup, koji koristi prilagoĎeni jezik za zastupanje graf, može biti više koncizan,ali to
zahtijeva vježbanje rutine, što može smanjiti ponovnu uporabu i proširivost. Znaći, kada
shvatimo graf, možemo mijenjati neke postavke kako bi ga promijenili.
1.2 Python Edge (rub)
Python adrese tih problema i čini uvjerljiv jezik za algoritme obrazovanje. Prvo, njegove
uvlake-based sintaksa je toliko slična većini udžbenika da čak
i studenti,
bez puno
programiranja nemaju problema kodiranja algoritama samo slijedeći
knjigu. Zbog
toga, popularnost s drugim jezicima je dvojbena, osobito s
obzirom
na činjenicu da
njegov interaktivni način potiče studente da
eksperimentiraju s
njim bez dugog
ciklusa. Drugo, Python pruža temeljne strukture podataka kao što su popisi, torke, rječnici i
može se koristiti izravno algoritmima. Čak je i složenija struktura podataka kao što
su drveće i grafikoni sažeti
u
čitljivom
obliku.
Postoji nekoliko prednosti: test slučajeva za algoritme mogu biti
napisani
direktno u Pythonu bez poziva svih podataka, strukturu izgradnje API, i bez da se oslanjaju
na bilo koji konvertor. U bilo kojem trenutku, podatkovne strukture mogu se takoĎer
prikazati u tekstualnom obliku koji je čitljiv za ljude i za Python.
2. Uvodna Lekcija: Sortiranje
Većina udžbenika poćinje sa sortiranjem kao način da uvedu algoritme i složene
analize.
Naša strategija je da prikažemo algoritme rame uz rame s Python kodom kako bi pokazali
njihovu sličnost. Mi smo započeli s InsertionSort, koja kao rezultat prikazuje niz s desna na
lijevo:
Algoritam iz udžbenika [1]
Insertion-Sort(A) (umetanje-poredaj)
1 for j <- 2 to length[A]
2 do key <- A[j]
3
i <- j - 1
4
while i > 0 and A[i] > key
5
do A[i+1] <- A[i]
6
i <- i - 1
7
A[i + 1] <- key
3
Python kod
def InsertionSort(A):
for j in range(1, len(A)):
key = A[j]
i = j - 1
while (i >=0) and (A[i] > key):
A[i+1] = A[i]
i = i - 1
A[i+1] = key
Kada učenici vide sličnost, većina njihovog straha prema programiranju jednostavno nestaje.
Ona takoĎer pomaže da pokažu interaktivne prirode Python. Mi koristimo računalo-projektor i
zapravo tip u programu, što je samo 8 linija dugo. Najbolji
dio je, možemo
testirati algoritam jednostavno, upišete u slučaju teksta u obliku popisa:
>>>
>>>
>>>
[1,
x = [2,7,3,8,1]
# create test case (stvoriti test slučaj)
InsertionSort(x)
# call routine (poziva rutinu)
x
# look at result (pogled na rezultat)
2, 3, 7, 8]
U smislu, Python daje udžbenik važnosti jer algoritmi predstavljaju
u
udžbeniku nisu višesamo pseudocode ili koraka teorijskih interesa samo, mogu vidjeti kako
se lako ga jezapravo izvršiti algoritme koristeći podatke koje oni generiraju. U stvari, mi takoĎ
erpokazuju da
je isti broj, bez izmjena, radi sasvim
u
redu s drugim tipovima
podataka,uključujući i žice, torke, itd. Sortiranje je dobra polazna primjer, jer ne samo
dakonstruira karte izravno,
bez komplikacija sa memory
management (biti riječi
kasnije),ali parametar semantika takoĎer odgovara: duži donosi vrijednosti, dok su polja don
osireferencu.
3. HeapSort (gomila) poredaj i prioritet redovi
Naš uvod nastavlja s HeapSort
poredajem
i prioritet redovima. Heap je struktura
podataka koja predstavlja gotovo uravnoteženo binarno stablo koristeći niz A[1 .. n], gdje se
lijevo
i desno
nalazi
dječji element A[i] smješten na A[2i], A[2i+1], odnosno, i A[i]> = A[2i], A[2i+1]. HeapSort gradi
sortirani niz redova iz stražnjeg dijela polja prema naprijed jedan element u vrijeme
vaĎenja najvećeg elementa
iz
skupine. U
početku sortirani dio je prazan, i poziv na
BuildHeap pretvara A[1 .. n] u skupini. Budući da iz skupine stavlja najveći element u A[1],
u prvoj iteraciji smo ga izvaditi i staviti ga u A[n], koji je njegov ispravan sortirani položaj.
Sljedeća iteracija ekstrakti drugi najveći element (iz A[1] opet) i stavlja ga u A[n-1], i sl., a to
se nastavlja sve dok se sve ne sortira. Imajte na umu da se Heapify zove kao dio
svakog koraka ekstrakcije. To je zato što ako mi razmijenimo A[1] i A[h], a zatim A[1 .. h1] više
ne
zadovoljava hrpu nekretnina, ali
budući da
je još
uvijek "skoro" hrpa to jest, sve osim
korijena položaj je
još
uvijek ispod
gomile
- to
4
može biti fiksno učinkovito u O(lg h) vrijeme pozivom Heapify bez mogućnosti obnavljanja u
gomili O(h) na vrijeme.
Jedna
od
razlika je da algoritam u udžbeniku poprima 1-na
temelju niza pokazatelja,dok Python pretpostavlja 0-temeljen polja. Python kod je:
def Parent(i): return i/2
def Left(i): return 2*i
def Right(i): return 2*i+1
def Heapify(A, i, n): # A is "almost a heap" (except root); fix it so all of A is a heap
l = Left(i)
r = Right(i)
if l <= n and A[l] > A[i]: largest = l
else: largest = i
if r <= n and A[r] > A[largest]:
largest = r
if largest != i:
A[i], A[largest] = A[largest], A[i]
Heapify(A, largest, n)
def HeapLength(A): return len(A)-1
def BuildHeap(A): # build a heap A from an unsorted array
n = HeapLength(A)
for i in range(n/2,0,-1):
Heapify(A,i,n)
def HeapSort(A): # use a heap to build sorted array from the end
BuildHeap(A)
HeapSize=HeapLength(A)
for i in range(HeapSize,1,-1):
A[1],A[i]=A[i],A[1] # largest element is a root of heap, put it at the end of array
HeapSize=HeapSize-1 # shrink heap size by 1 to get next largest element
Heapify(A,1,HeapSize)
Gomila i prioritet redovi su usko povezani, jer gomila može implementirati prioritetne redove
učinkovito sa O(lg n)-vrijeme umetanja i vaĎenja. Jedna
od
razlika je,
meĎutim,
dinamička memorija upravljanja: u gomilu vrsta, veličina polja ostaje ista, dok u prioritet
redovima veličina reda raste i smanjuje. Koristimo ovu priliku da se uvedu dvije konstrukti.
Prvo, mi pokazuju da A.append () i A.pop () može se koristiti za rast i smanjiti liste A, dok
su len (A) vraća trenutnu duljinu popisa. Drugo, u slučaju ispod
granice (i prelijevanje po
želji),
pokazuju studentima kako podići i uhvatiti izuzetak. Ovi konstrukti ne mogu biti
jedinstveni za Python, no Python olakšava eksperiment.
4. Binarna stabla i Huffman kodiranje
Huffman algoritam proizvodi prefiks-free, promjenjive duljine
kodne
riječi na
temelju frekvencije svakog lika. Često se
koriste slova
kodirana
pomoću kraćeg niza
bitova, a manje često
korištenih
pomoću dužeg
niza
5
bitova. Pohlepni algoritam koristi prioritet red za
izdvajanje dva čvora (list ili interni), s
najnižim frekvencijama, dodjeljuje novi čvor čija težina je zbroj dva, i umeće novi čvor
natrag u red prioriteta. Algoritam završava kad prioritet red uklanja zadnji čvor, koji
postaje korijen stabla Huffman. Niz
bitova za
svako slovo može biti
proizveden
od
strane poprijeko Huffman binarno stablo, gdje je uzimanje lijevoga kraka rezultat u '0',i desna
grana rezultat u '1'.
Na primjer, pretpostavimo da je naš ulaz skup znakova s pripadajućim frekvencija:
'a':
45%
'b': 13%
'c': 12%
'd': 16%
'e': 9%
'f': 5%
Huffman algoritam konstruira stablo usporeĎujući dva elementa s najmanje
frekvencije, stvara novi unutarnji čvor čija
je frekvencija jednaka
njihovom
zbroju, u red prioriteta. Rezultat je stablo (sl. 1) koje definira promjenljive duljine koda
za svaki znak. Lijevo grane su označene 0, a desno grane su označeni 1, a Huffman kod
za znak se jednostavno niz stazu natpiše iz korijena do listova. Npr., kodiranje:
'a':
'b':
'c':
'd':
'e':
'f':
0
1
1
1
1
1
0
0
1
1
1
0
1
0
1 0
1 1
Slika 1 Primjer Huffman stabla
Budući da
smo
stablo. Zahtjevi su:
već prioritet red, ono
što nam
nedostaje je specijalizirano binarno
čvor nultog stupnja mora biti u stanju da predstavlja pismo, bude kodirana, a njegova
učestalost.
unutarnji čvor mora imati dvoje djece, i ona takoĎer mora imati težinu koja je jednaka
zbroju za svoju djecu.
red prvenstva mora biti u
stanju „enqueue“ i „dequeue“ i lišće i unutarnje čvorove i
usporediti ih na temelju težine
6
Ako smo za provedbu ovog s tradicionalnim jezikom kao što je C ili Java, trebali bismo
naučiti kako definirati strukturu ili klasu s nazivom polja težina; list čvorova,
potrebno karakter polje, dok unutarnji čvorovi zahtijevaju leftchild i rightchild polja. Budući da
je prioritet red, mora biti u
mogućnosti
da ih usporedi, to će
biti potrebno
modificirati prioritet reda
i pozvati odgovarajuću metodu usporedbe umjesto koristeći
izgraĎen-u odnosu na operatora, i oba lišća i unutarnji čvorovi moraju biti ili u istoj klasi
ili biti podrazred
u
istoj
bazi klase koja implementira usporedna
metoda.
MeĎutim, ni postojeće ispravljanje
pogrešaka nema znanja da
može automatski ispisati
čvorove zajedno kao stablo, i stoga je rutina pisati naredbu print, što zapravo može biti
prilično lukav i biti još jedan veliki izvor bugova.
Python implementacija može biti učinjeno elegantno,
bez
potrebe za pisanjem dodatne
rutine ili definiranje nove klase ili strukture za stabla čvorova. Predstavljaje binarnih stabla u
Pythonu, u duhu sličan Lisp:
Leaf čvorovi su zastupljeni kao (frekvencija, karakter) torke:
[(45, 'a'), (13, 'b'), (12, 'c'), (16, 'd'), (9, 'e'), (5,
'f')].
Unutarnji čvorovi su zastupljeni kao u nalog 3 torke: (frekvencija, lijevo, desno): Na
primjer, u donjem desnom podstablu na slici 1 može se prikazati kao
(14, (5, 'f'), (9, 'e'))
koja predstavlja unutarnji čvor čija težina je 14%, čija je lijevo dijete (5, 'F'), te čije je
pravo dijete (9, 'e').
Stablo je konstruirano funkcionalno s torka stvaranje, bez potrebe da se koristi bilo koji
čvor stabla strukture
podataka, i nema potrebe da manipuliraju lijevo/desno. Štoviše, to
je lako koristiti s postojećim prioritetnim redom strukture podataka, bez izmjena! To je zato
što se
torke može usporediti na leksikografski poredak koristeći istu
usporedbu operatora. Na
taj
način, unutarnji čvorovi i lišće može biti u
odnosu, iako
oni kodiraju različite informacije. Razlika izmeĎu njih je da je len () = 2 za leaf (list), i = 3 za
unutarnji čvor.
5. Grafikon Algoritmi
Graf ima više prikaza, a većina algoritama može pretpostaviti ili susjedstva liste ili matrice
susjedstva
reprezentacije.
Za provedbu graf u tradicionalnom sustavu programskog jezika kao što su C ili Java, prvo bi
morao definirati strukture podataka za vrhove, za rubove, a za graf, koji služi kao front-end
za stvaranje i brisanje svoje vrhove i rubove. Dizajn takvih podataka struktura lako može
dominirati kodiranje vrijeme i nije lako za višekratnu upotrebu, uglavnom zato što ti tipovi
podataka moraju biti projektirani kao „kontejneri“. Iako paketi poput LEDA [3] kako bi se
poboljšao pokušaj ponovne upotrebe objektno orijentiranog izvorni kod u C++ predloške, oni
i dalje zahtijevaju da učenici usvoje cijeli paket prije nego što mogu početi raditi nešto
korisno. Kontejneri su često dizajnirani za zaobilaženje problema s jakim, statički tipkanje, ali
to zahtijeva reimplementaciju dinamičkog tipa provjere u kraj-korisnik kod. Čak i gori
nedostatak da je uporaba C-pokazivača ili Java-reference čini neugodan da biste vidjeli ove
predmete. Iako „debugger“ može prikazati te objekte u nekom tekstualnom obliku, to
prikazuje
previše
informacija
ili
nije
izravno
od
koristi
u
programu.
Python nudi mnoge prednosti kao što je naglašeno od strane strukture grafa podataka. Mi
7
koristimo vrlo kompaktan, rječnik-of-rječnika (DD) provedbu zastupljenosti susjedstvo popis
grafa. Uglavnom graf je prikazan kao Python rječnik, čije su tipke niz imena vrhova, a svaki
vertex ime preslikanog na svoje susjedstvo popis. Npr., graf prikazan na slici. 2:
Sl.2: Primjer usmjereni graf
To može biti zastupljena sa sljedećim Python kodom:
H = {'A': ['C', 'D'], 'B': ['D', 'A'], 'C': ['D', 'E'],
'D': ['E'], 'E': [] }
Gore predstavlja jednostavan, usmjereni, neponderirani graf. Ako ponderirani graf
kao što sl. 3 prikazuje, onda možemo
jednostavno zamijeniti liste susjedstva vrha s
rječnicima koji su na karti susjedna vrha njihove težine:
Sl. 3: Primjer grafa ponderirane (težine)
L = {'A': {'C':2, 'D':6}, 'B': {'D':8, 'A':3},
'C': {'D':7, 'E':5}, 'D': {'E':-2}, 'E': {}}
Popis vrhova V je jednostavno H.keys () ili L.keys (). Susjedstvo popis H [v] zaneponderirani
m grafikone, i L [v]. tipke () za ponderirani grafova. Težina ruba mase w(u, v) je L[u] [v]. Da bi
se olakšalo programiranje, možemo završiti pojedinosti provedbe unutar objekta.
Grafikon klase:
def __init__(self, g):
self.g = g
def V(self):
return self.g.keys()
def Adj(self,v):
return self.g[v].keys()
def w(self,u,v):
return self.g[u][v]
Možemo stvoriti grafikon objekt sa G = Graf (L). Prednosti s
tim pristupom uključuju
kompaktni tekstualni oblik i proširivost. Prvo, stvarno
ne
postoje strukture
podataka
za projektiranje. Tekstualni prikaz grafa je Python izvršna. Student može
upisati u ovoj
strukturi interaktivno ili u tekstualnoj datoteci bez korištenja posebnih graf-urednika. Struktura
8
podataka se
može ispitati samo tako
da
upišete svoje
ime. To onda može biti
cut/lijepiti na drugi prozor Python tumača ili na drugi Python program, bez ikakvih sintaktičkih
izmjena.
Još važnije, ovaj prikaz je iznimno proširiv. Različiti algoritmi koriste dodatne atribute, ali se
mogu dodati prema potrebi. Npr., jedan izvor, najkraći put algoritama ili breadth-first/depthfirst traversals zahtijevaju dodatne atribute,
kao
što
prethodnik
upućuje. U Python, algoritam jednostavno mogu dodati atribut prethodnik graf-objekt
(kao
što G.pred [v]), bez
potrebe za definiranje podrazred za svaki
algoritam. Ove novo
dodano, atributi takoĎer mogu biti pregledani i izravno mijenjati bez potrebe nove rutine.
6. Zaključak i zašto je Python pogodan za učenje izrade
algoritama?!
Kao algoritam orijentirani jezik, Python omogućava učenje ključnih koncepata u dizajnu
algoritama, umjesto da se bori s nama sa osebujnim obilježjima koje imaju konvencionalni
programski jezici. Python način obraĎuje vrste podataka, predstavlja savršeno
odgovarajućim načinom na koji potiče da sa sami zainteresiramo za njega i onda
eksperimentiramo. Jednako je važno naše korištenje struktura podataka za drveće i grafove,
koji su kao kompaktna što je više moguće i još ljudskih čitljiv i spremno prihvatio Python
prevodioca.
Sve u svemu, mislim da je Python jako dobar program za izradu algoritama, budući da nas
on dosta često uči vještine rješavanja problema, a ne samo programiranje, kao što su
dodatni moduli koje možemo koristiti bilo u matematici ili u fizici, crtanje svakakvih grafova i
krivulja i puno toga.
7. Literatura
1. Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein, Introduction to
Algorithms, Second Edition, McGraw-Hill Press, September 2001. ISBN 0-262-03293-7.
2. Pai H. Chou, ECE 235 Website, University of California, Irvine, Fall
2000. http://e3.uci.edu/00f/15545/ See also Fall 2001 edition at http://e3.uci.edu/01f/15545/.
3. Algorithmic Solutions Software GmbH, homepage, http://www.algorithmic-solutions.com/,
2001.
4. Sara Baase, Allen Van Gelder, Computer Algorithms: Introduction to Design and Analysis,
Addison Wesley, 2000.
5. Mark Allen Weiss, Data Structures and Algorithm Analysis in JAVA, Addison-Wesley, 1999.
6. Algorithm Education in Python: http://www.ece.uci.edu/~chou/py02/python.html
7. 20.2.5. Funkcije:
http://docs.python.org/release/2.6.6/library/cgi.html?highlight=making%20algorithms
8. The Python Profilers:
http://docs.python.org/release/2.6.6/library/profile.html?highlight=making%20algorithms
9. bisect — Array bisection algorithm:
http://docs.python.org/release/2.6.6/library/bisect.html?highlight=making%20algorithms
10. decimalni - Decimalni fiksne točke i s pomičnim zarezom aritmetika:
http://docs.python.org/release/2.6.6/library/decimal.html?highlight=making%20algorithms
9