Algorithmik mit Python - Prof. Dr. Tobias Häberlein

Werbung
Algorithmik mit Python
Prof. Dr. Tobias Häberlein
Hochschule Albstadt-Sigmaringen
Studiengang Kommunikations- und Softwaretechnik“
”
Leipzig, 04.04.2011
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
1 / 57
Überblick
1
2
3
4
5
Implementierung von Algorithmen
Sortieren
Insertion-Sort
Quicksort
Suchen
Heaps
Skip-Listen
Bloomfilter
Suchmaschinen
Graphen
Grundlagen
Kürzeste Wege
Minimaler Spannbaum
Schwere Probleme
Lösung des Traveling Salesman Problems
Greedy-Heuristiken zur Lösung des TSP
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
2 / 57
Implementierung von Algorithmen
Rekursion
1
Eine Funktion heißt rekursiv,
wenn Sie sich selbst ein oder
mehrmals aufruft.
2
3
4
5
Pro: Oft einfacher zu
implementieren, als iterative
Ansätze.
Con: I. A. mehr
Speicherverbrauch
T. Häberlein HS AlbSig
1
2
3
4
5
def facIter(n):
erg = 1
for i in range(1,n+1)
erg = erg*i
return erg
def facRec(n):
if n==0:
return 1
else:
return n*fac(n-1)
Algorithmik mit Python
Leipzig, 04.04.2011
3 / 57
Implementierung von Algorithmen
Rekursive vs. Iterativ?
Kochrezept“
”
1
2
Rekursionsabbruch:
Was ist der einfache“ Fach
”
(Größe n = 0 oder n = 1).
Rekursionsschritt:
I
I
Gedankentrick: Angenommen,
Aufgabe für alle kleineren“
”
Probleme gelöst . . .
. . . wie kann man dann aus den
Lösungen der kleineren Aufgaben,
die Lösung der Gesamtaufgabe
konstruieren.
T. Häberlein HS AlbSig
1
2
3
4
5
6
7
8
9
10
def rekAlg(x):
if len(x) is kleingenug:
return loesung(x)
else:
...
y1 = rekAlg(x1)
y2 = rekAlg(x2)
...
loesung = kombiniere(y1,y
return loesung
Algorithmik mit Python
Leipzig, 04.04.2011
4 / 57
Implementierung von Algorithmen
Aufgaben: Rekursion (1)
Aufgabe 1
1
Definieren Sie die Funktion sum(n), die die Summe der Zahlen von 1
bis n berechnen soll, rekursiv.
2
Definieren Sie die Funktin len(lst), die die Länge der Liste lst
berechnen soll, rekursiv.
Aufgabe 2
Implementieren Sie die Funktion ins(x,lst), die die Liste aller möglichen
Einfügungen des Elements x in die Liste lst zurückliefert.
Beispielanwendung:
>>> ins(1,[2,3,4,5])
>>> [[1,2,3,4,5], [2,1,3,4,5], [2,3,1,4,5], [2,3,4,1,5], [2,3,4,5,1]
Tipp: Das geht über eine rekursive Implementierung. Es empfiehlt sich
auch die Verwendung einer Listenkomprehension.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
5 / 57
Implementierung von Algorithmen
Aufgaben: Rekursion (2)
... und noch etwas schwieriger:
Aufgabe 3
Implementieren sie eine rekursive Funktion perms(lst), die die Liste aller
Permutationen der als Argument übergebenen Liste lst zurückliefert.
Tipp: Verwenden Sie die eben definierte Funktion ins. Beispielanwendung:
>>> perms([1,2,3])
>>> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Aufgabe 4
Implementierung Sie die rekursive Funktion choice(lst,k), die eine Liste
aller k-elementigen Teil mengen“ der Elemente aus lst zurückliefert.
”
Beispielanwendung:
>>> choice([1,2,3],2)
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
6 / 57
Implementierung von Algorithmen
Rekursiv gehts einfacher
Die rekursive Implementierung vieler Probleme ist viel einfacher!
Beispiel: Zeichnen der Striche auf einem Lineal
Rekursive Implementierung:
1
2
from graphics import *
linealCanv = GraphWin("Ein Lineal",1000,50)
3
4
5
6
7
def strich(x,h):
'''Zeichne Strich an Position x mit Laenge h'''
l = Line(Point(x,0),Point(x,h))
l.draw(linealCanv)
8
9
10
11
12
def lineal(l,r,h):
''' Zeichne Lineal zwischen Pos l und r
laengster Strich (in der Mitte) hat Hoehe h'''
...
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
7 / 57
Implementierung von Algorithmen
Aufgabe 5
Zeichnen Sie durch eine
rekursiv definierte
Python-Funktion und unter
Verwendung der
graphics-Bibliothek
folgenden Stern:
Aufgabe 6
Schreiben Sie eine rekursive Prozedur
baum(x,y,b,h) zum Zeichnen eines
(binären) Baumes derart, dass die Wurzel
sich bei (x,y) befindet, der Baum b breit
und h hoch ist. Definieren Sie hierzu eine
Python-Prozedur line(x1,y2,x2,y2), die
eine Linie (x1,y2) zu (x2,y2) zeichnet.
Beispiel für die Ausgabe von
baum(0,0,16,4).
1
2
3
4
(0,0)
16
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
8 / 57
Implementierung von Algorithmen
Destruktiv vs. Nicht-Destruktiv
sorted(list) ist
list.sort() ist destruktiv:
>>> l = list('hallo')
>>>
l.sort() # l wird veraendert
>>> l
['a', 'h', 'l', 'l', 'o']
nicht-destruktiv:
>>> l = list('hallo')
>>>
sorted(l) # l unveraendert
['a', 'h', 'l', 'l', 'o']
Vor-/Nachteile
Pro Nicht-Destruktiv:
Jeder destruktive Update verändert internen Zustand des Programms.
Viele Zustände ⇒ viele Abfragen ⇒ viele mögliche Fehler
Wenige Zustände ⇒ besserer Überblick ⇒ weniger mögliche Fehler
Con Nicht-Destruktiv:
... ? ...
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
9 / 57
Sortieren
Insertion-Sort
Insertion-Sort – Funktionsweise
So sortiert der Kartenspieler:
Sukzessive werden Karten vom Stapel in die schon sortierten Karten
auf der Hand eingefügt.
1. [53,6,63,94,56,8,72,44,70]
5. [6,53,56,63,94,8,72,44,70]
2. [6,53,63,94,56,8,72,44,70]
6. [6,8,53,56,63,94,72,44,70]
3. [6,53,63,94,56,8,72,44,70]
7. [6,8,53,56,63,72,94,44,70]
4. [6,53,63,94,56,8,72,44,70]
8. [6,8,44,53,56,63,72,94,70]
Ergebnis: [6,8,44,53,56,63,70,72,94]
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
10 / 57
Sortieren
Insertion-Sort
Insertion-Sort – Implementierung
Wir teilen auf ins das Einfügen:
1
2
3
def insAtRightPos(alst, key):
return [x for x in alst if x <=key] +[key] +
[x for x in alst if x>key]
und das eigentliche Sortieren – rekursiv implementiert.
1
2
3
def insertionSort(alst):
if len(alst)<=1: return alst
else: return ...
Aufgabe 7
Ersetzen sie die ...“-Stelle in obigem Listing durch den notwendigen
”
rekursiven Aufruf.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
11 / 57
Sortieren
Insertion-Sort
Insertion-Sort – Implementierung in-place
1
2
3
4
5
6
7
8
def insertionSort(lst):
for j in range(1,len(lst)):
key = lst[j]
i = j-1
while i >= 0 and lst[i] > key:
lst[i+1] = lst[i]
i = i -1
lst[i+1] = key
Zwar schneller, aber . . .
. . . schwieriger zu implementieren!
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
12 / 57
Sortieren
Quicksort
Quicksort – Funktionsweise + Implementierung
Wähle beliebiges Element lstj mit 0 ≤ j ≤ n − 1 aus (Pivot-Element).
Zerteile lst in lstl (alle Elemente < lstj ) und lstr (alle Elemente ≥ lstj )
lstl und lstr werden rekursiv sortiert.
Die rekursiv sortierten Teillisten werden einfach zusammengehängt.
1
2
3
4
5
6
def quicksort(lst):
if len(lst)<=1: return lst # Rekursionsabbruch
pivot = lst[0]
lst_l = [a for a in lst[1:] if a <= pivot]
lst_r = [a for a in lst[1:] if a > pivot]
return ...
Aufgabe 8
Vervollständigen Sie die Implementierung von Quicksort an der
...“-Stelle.
”
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
13 / 57
Sortieren
Quicksort
Quicksort – Implementierung in-place
Schneller, aber schwieriger zu implementieren!
Die in-place-Partitionierung
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def partitionIP(lst,l,r):
pivot=lst[l]
i=l-1
Das eigentliche
j=r+1
in-place-Sortieren.
while True:
while True:
1 def quicksortIP(lst,l,r):
j=j-1
2
if r>l:
if lst[j]<=pivot: break
3
i= partitionIP(lst,l,r)
while True:
4
quicksortIP(lst,l,i)
i=i+1
5
quicksortIP(lst,i+1,r)
if lst[i]>=pivot: break
if i<j:
lst[i],lst[j]=lst[j],lst[i]
else:
return j
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
14 / 57
Suchen
Heaps
Die Heap-Struktur
Ein Max-Heap ist . . .
. . . ein fast vollständiger Binärbaum.
Max-Heap-Eigenschaft: Der Schlüssel eines Knotens ist größer als die
Schlüssel seiner beiden Kinder
Ein Max-Heap:
Ein Min-Heap:
23
13
18
21
9
2
7
4
3
19
6
23
5
29
95
33
64
38
77
98
71
39
76
82
99
Der Max-Heap kann repräsentiert werden als:
[None,23,18,21,9,7,19,5,2,4,3,6]
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
15 / 57
Suchen
Heaps
Heap – Aufgaben Implementierung
Aufgabe 9
1
Implementieren Sie die Funktion leftChild(heap,i), die den Wert
des linken Kindes von heap[i] zurückgibt. Gibt es kein solches, soll
None zurückgeliefert werden.
2
Implementieren Sie die Funktion rightChild(heap,i) entsprechend.
3
Implementieren Sie eine Funktion father(heap,i), die als Argument
einen Heap heap und einen Index i übergeben bekommt und den
Wert des Vaters von heap[i] zurückliefert.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
16 / 57
Suchen
Heaps
Heaps – Einfügen
Für viele Anwendungen wichtig: Effizientes Extrahieren des größten
(kleinsten) Elements.
Optimal dafür: Heaps!
Einfügen:
13
13
23
29
95
33
77
23
64
38
98
71
39
76
82
13
47
29
95
33
38
77
23
64
47
71
39
76
82
98
29
95
33
47
38
77
64
71
39
76
82
98
Aufgabe 10
Vervollständigen Sie den folgenden Code zur Implementierung der
Einfüge-Operation in Heaps.
1
2
3
def insertH(heap, x):
heap.append(x)
...
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
17 / 57
Suchen
Heaps
Heaps – Min-Extrakt
13
1.
23
29
95
33
47
38
77
39
76
72
29
47
38
77
76
47
72
64
71
39
23
3.
23
33
82
72
2.
95
64
71
82
29
95
33
38
77
39
23
82
5.
47
29
38
72
33
76
23
4.
95
64
71
77
71
39
T. Häberlein HS AlbSig
76
29
64
82
33
95
72
47
38
77
64
71
39
Algorithmik mit Python
76
82
Leipzig, 04.04.2011
18 / 57
Suchen
Heaps
Heaps – Min-Extrakt-Implementierung
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def minExtrakt(heap):
returnVal=heap[1]
n=len(heap)-1
heap[1]=heap[n] # letztes Element an die Wurzel
del(heap[n])
n-=1
# n soll weiterhin auf das letzte Element zeigen
i=1
while i<=n/2:
j=2*i
if j<n and heap[j]>heap[j+1]: j+=1 # waehle kleineres der beiden
if heap[i]<=heap[j]: break
heap[i],heap[j]=heap[j],heap[i]
i=j
return returnVal
Laufzeit: O(log n)
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
19 / 57
Suchen
Heaps
Heaps – build-Heap-Implementierung
Hintere Hälfte der Liste (also lst[len(lst)/2:]): Sammlung von
len(lst)/2 Heaps;
noch über den vorderen Teil der Liste laufen und alle verletzten
Heap-Bedingungen wiederherstellen.
1
2
3
def buildHeap(lst): # Es muss lst[0]==None gelten
for i in range(len(lst)/2,0,-1):
minHeapify(lst,i)
Laufzeit: O(n)
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
20 / 57
Suchen
Heaps
Heaps – in Python
Die Standard-Modul heapq implementiert Heaps.
heapq.heapify(lst): Transformiert die Liste lst in-place in
Min-Heap;
heapq.heappop(lst): Enfernt kleinestes Element aus Heap lst;
heapq.heappush(lst,x): Fügt ein neues Element x in Heap lst ein;
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
21 / 57
Suchen
Skip-Listen
Skip-Listen
Sind ähnlich zu verketteten Listen, . . .
. . . außer, dass ein Element mehrere Vorwärtszeiger haben kann.
Beispiel:
7
13
19
30
34
32
44
39
62
76
81
91
93
Für Skip-Liste muss gelten:
Wkeit, dass zufällig gewählter
Eintrag i Vorwärtszeiger hat:
1
2
3
p
i−1
· (1 − p)
4
5
Randomisierte Impl. ⇒
T. Häberlein HS AlbSig
6
from random import random
p = ... # Wkeit mit 0<p<1
def randHeight():
i=0
while random()<=p: i+=1
return min(i,MaxHeight)
Algorithmik mit Python
Leipzig, 04.04.2011
22 / 57
Suchen
Skip-Listen
Skip-Listen – Implementierung
1
2
3
class SLEntry(object):
def __init__(self, key, ptrs=[], val=None):
self.key = key ; self.ptrs = ptrs ; self.val = val
4
5
6
7
8
9
class SkipList(object):
def __init__(self):
self.tail = SLEntry(Infty)
self.head = SLEntry(None,[self.tail]*(MaxHeight+1))
self.height = -1
⇒ Eine leere Skipliste hat ein
tail – Ein Ende mit key=∞
head – Ein Kopf mit allen Vorwärtszeigern auf tail
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
23 / 57
Suchen
Skip-Listen
Skip-Listen – Suche
Suche nach einem Eintrag mit Schlüssel key:
1
2
3
4
5
6
7
8
9
class SkiptList(object):
...
def search(self, key):
x = self.head
for i in range(self.height,-1,-1):
while x.ptrs[i].key < key: x = x.ptrs[i]
x = x.ptrs[0]
if x.key == key: return x.val
else: return None
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
24 / 57
Suchen
Skip-Listen
Skip-Listen – Einfügen
Wähle Höhe i durch Zufallsentscheidung (randHeight)
Es müssen i Zeiger anderer Elemente umgebogen“ werden.
”
updatePtrs[3]
updatePtrs[2]
updatePtrs[1]
13
19
7
30
updatePtrs[0]
34
32
39
44
62
76
81
91
93
Der i-te Vorwärtszeiger von updatePtrs[i] muss umgebogen“ werden.
”
13
7
19
30
34
32
39
44
79
62
76
81
91
93
Aufgabe 11
Implementieren Sie eine Methode insert(key,val) der Klasse SkipList
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
25 / 57
Suchen
Skip-Listen
Skip-Listen – Aufgaben (1)
Aufgabe 12
Implementieren Sie die Funktion __str__, so dass Skip-Listen
folgendermaßen ausgegeben werden:
>>> print skiplist
>>> [ (30|1), (33|4), (40|3), (77|1), (98|1), (109|1), (193|3) ]
Ausgegeben werden soll also der Schlüssel jedes Elements zusammen mit
der Höhe des Elements.
Aufgabe 13
1
Schreiben Sie eine Methode keys(), die eine Liste der in der
Skip-Liste gespeicherten Schlüsselwerte zurückliefert.
2
Schreiben Sie eine Methode vals(), die eine Liste der in der
Skip-Liste gespeicherten Werte zurückliefert.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
26 / 57
Suchen
Skip-Listen
Skip-Listen – Aufgaben (2)
Aufgabe 14
Oft wird eine effiziente Bestimmung der Länge einer Skip-Liste benötigt.
Erweitern Sie die Klasse SkipList um ein Attribut length, passen Sie
entsprechend die Methoden insert und delete an und geben Sie eine
Implementierung der Methode __len__ an, so dass die len-Funktion auf
Skip-Listen anwendbar ist.
Aufgabe 15
1
Schreiben Sie eine Funktion numHeights(h), die die Anzahl der
Elemente mit Höhe n zurückliefert.
2
Schreiben Sie eine Funktion avgHeight(s), die die durchschnittliche
Höhe eines Elementes der Skip-Liste s berechnet.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
27 / 57
Suchen
Bloomfilter
Bloomfilter – Grundlegendes
Sehr platz- und zeiteffiziente Möglichkeit des Membership-Tests.
Bloomfilter sind probabilistisch: Möglichkeit falsch-positiver
Antworten.
I
I
Datensatz in Bloomfilter ⇒ Antwort immer korrekt!
Datensatz nicht in Bloomfilter ⇒ Antwort nicht immer korrekt!
Eine Anwendung – unter vielen:
w ∈ S?
nein
Bloomfilter
ja
y ∈ S?
nein
langsamer
Speicher
ja
x ∈ S?
T. Häberlein HS AlbSig
nein
ja
Algorithmik mit Python
z ∈ S?
Leipzig, 04.04.2011
28 / 57
Suchen
Bloomfilter
Bloomfilter – Funktionsweise
Bsp:
h0 (eine) = 3, h0 (Einführung) = 1, h0 (Informatik) = 6
h1 (eine) = 1, h1 (Einführung) = 8, h1 (Informatik) = 7
0
1
2
3
4
5
6
7
8
9
false false false false false false false false false false
Einfügen von
eine
)
ne
ei
h 1(
ne
ei
h 0(
)
0
1= 2
3= 4
5
6
7
8
9
false true false true false false false false false false
)
)
ng
ng
Einfügen von
ru
ru
h
h
ü
ü
nf
nf
Einführung
Ei
Ei
(
(
0
1
h
h
0
1= 2
3
4
5
6
7
8= 9
false true false true false false false false true false
k) k)
ti ati
a
m
Einfügen von
r
rm
fo nfo
n
I
I
Informatik
h 0( h 1(
0
1
2
3
4
5
6= 7= 8
9
false true false true false false true true true false
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
29 / 57
Suchen
Bloomfilter
Bloomfilter – Implementierung
1
2
3
4
5
class BloomFilter(object):
def __init__(self, h, m):
self.k = len(h) ; self.h = h
self.A = [False]*m
self.m = m
6
7
8
9
10
def insert(self,x):
... # Siehe Aufgabe
def elem(self,x):
... # Siehe Aufgabe
Aufgabe 16
Implementieren Sie insert und elem.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
30 / 57
Suchen
Suchmaschinen
Suchmaschinen
Aufbau einer Suchmaschine
Web
Datenbank
Crawler
Dateisystem
Indexer
Index
Suchanfrage
Bearbeitung
GUI
Der invertierte Index
. . . das Herz“ jeder Suchmaschine!
”
Liste aller Wörter
Algorithmik mit Python
[10,...]
T. Häberlein HS AlbSig
[430,102,344,982, ...]
[101,72,...]
...
Hashtabelle
Heap
Heapsort
Hornerschema
Insertion Sort
...
Leipzig, 04.04.2011
31 / 57
Suchen
Suchmaschinen
Implementierung
1
1
2
3
4
5
6
class Index(object):
2
def __init__(self, path=''): 3
self.docId = 0
4
self.ind = {}
5
self.docInd = {}
6
if path!='': self.crawl(path)7
8
1
2
3
4
5
6
7
8
9
10
11
def crawl(self, path):
def tupl(x,y): return (x,y)
for dirpath, dirnames, filenames \
in os.walk(path):
for file in filenames:
f = os.path.join(dirpath, file)
if isTxt(f):
self.addFile(f)
def addFile(self, file):
def tupl(x,y): return (x,y)
self.docInd[self.docId] = file
fileHandle = open(file)
fileCont = fileHandle.readlines() ; fileHandle.close()
fileCont = map(tupl, xrange(0,len(fileCont)), fileCont)
words = [(word.lower(),pos) for (pos,line) in fileCont
for word in line.split()
if len(word) >=3 and word.isalpha() ]
for word,pos in words: self.toIndex((word,pos), self.docId)
self.docId+=1
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
32 / 57
Suchen
Suchmaschinen
Aufgaben
Aufgabe 17
Implementieren Sie die fehlende Methode toIndex((word,pos),docId)
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
33 / 57
Graphen
Grundlagen
Repräsentation von Graphen
Der folgende Graph . . .
1
mit:
2
5
3
4
G = (V , E ) mit
V = {1, 2, 3, 4, 5},
E = {(1, 2), (2, 2), (2, 3), (1, 3), (1, 4), (3, 4), (4, 5)}
. . . kann repräsentiert werden als . . .
Adjazenzliste
Adjazenzmatrix

0 1 1
 0 1 1

 0 0 0

 0 0 0
1 0 0
T. Häberlein HS AlbSig
1
0
1
0
0
0
0
0
1
0
1
2
3
4
5






Algorithmik mit Python
{2, 3, 4}
{2, 3}
{4}
{5}
{1}
Leipzig, 04.04.2011
34 / 57
Graphen
Grundlagen
Repräsentation in Python
1
2
3
4
5
6
class Graph(object):
def __init__(self,n):
self.vertices = []
self.numNodes = n
for i in range(0,n+1):
self.vertices.append({})
self.vertices ist die Adjazenzliste...
. . . deren Einträge dict-Objekte sind . . .
. . . die adjanzente Knoten (inkl. evtl. Gewichte) enthalten.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
35 / 57
Graphen
Grundlagen
Wichtige Methoden (1)
Aufgabe 18
Implementieren Sie die Graph-Methoden addEdge, isEdge, G, V, E und
füllen sie hierzu die Lücken in folgendem Listing:
1
2
3
4
5
6
7
8
9
10
11
12
class Graph(object):
...
def addEdge(self,i,j,weight=None):
...
def isEdge(self,i,j):
...
def G(self,i):
...
def V(self):
...
def E(self):
...
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
36 / 57
Graphen
Grundlagen
Wichtige Methoden (2)
Aufgabe 19
Erweitern Sie die Klasse Graph um die Methode Graph.w(i,j), die das
Gewicht der Kante (i, j) zurückliefert (bzw. None, falls die Kante kein
Gewicht besitzt).
Aufgabe 20
Erweitern Sie die Klasse Graph um die folgenden Methoden:
1
Eine Methode Graph.isPath(vs), die eine Knotenliste vs übergeben
bekommt und prüft, ob es sich hierbei um einen Pfad handelt.
2
Eine Methode Graph.pathVal(vs), die eine Knotenliste vs übergeben
bekommt. Handelt es sich dabei um einen gültigen Pfad, so wird der
Wert“ dieses Pfades (d. h. die Summe der Gewichte der Kanten des
”
Pfades) zurückgeliefert. Andernfalls soll der Wert ∞ (in Python:
float('inf')) zurückgeliefert werden.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
37 / 57
Graphen
Kürzeste Wege
Algorithmus von Warshall
Berechnet die kürzesten Wege zwischen allen Knotenpaaren.
Berechnungsschema:
1
2
3
Berechne W0 : alle kürzesten Wege mit keinen Zwischenknoten“
”
Aus Wk−1 berechnet Wk : alle kürzesten Wege mit Zwischenknoten
∈ {1, . . . , i}
Lösung: Wn
Schritt von Wk−1 nach Wk :
Pfad mit Knoten aus {1, . . . , k}
k
i
j
Pfad mit Knoten aus {1, . . . , k − 1}
Es gilt:
Wk [i, j] := min{ Wk−1 [i, j], Wk−1 [i, k] + Wk−1 [k, j] }
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
38 / 57
Graphen
Kürzeste Wege
Warshall Implementierung
1
2
3
4
def warshall(graph):
n = graph.numNodes+1
W = [ [graph.w(i,j) for j in graph.V()] for i in graph.V() ]# W_0
...
W: Adjazenzmatrix des Graphen, also W0 .
Aufgabe 21
Vervollständigen Sie die Implementierung des Warshall-Algorithmus, d. h.
ersetzen sie die ...-Stelle durch Code, der Sukzessive W1 , W2 , . . . , Wn
berechnet.
Aufgabe 22
Was ist die Laufzeit des Warshall-Algorithmus? D. h. wie viele
Berechnungsschritte – in der O-Notation – benötigt der
Warshall-Algorithmus zur Berechnung der kürzesten Wege eines Graphen
mit n Knoten?
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
39 / 57
Graphen
Kürzeste Wege
Der Dijkstra-Algorithmus
Edsger Dijkstra (1930 - 2002)
Berechnet nicht alle
Abstände zwischen allen
Knoten, sondern . . .
. . . berechnet – ausgehend
von einem Knoten v – die
Abstände l[u] (Länge der
kürzesten Wege) zu allen
Knoten u ∈ V .
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
40 / 57
Graphen
Kürzeste Wege
Der Dijkstra-Algorithmus – Funktionsweise
l[c]=11 l[u]=0
l[u]=0
a
3
Dijkstra ist greedy:
Knoten werden
sukzessive
abgehakt“:
”
I
I
Es kommt immer
derjenige Knoten
an die Reihe, der
momentan den
geringsten l-Wert
hat.
Es wird versucht,
die Abstände aller
seiner Nachbarn zu
verbessern.
2
11
c
4
4
5
u
2
3
7
1
5
b
10
d
2
e
l[c]=11 l[u]=0
3
2
11
c
4
4
5
u
10
d
2
l[d]=4
e
f
1
g
8
l[c]=9
3
2
4
5
4
u
10
l[b]=14
d
l[f ]=2
2
2
l[d]=4
f
3
4
l[g ]=5
5
4
u
l[f ]=2
2
f
3
7
1
5
10
d
2
l[d]=4
l[a]=11
a
1
8
l[e]=6
g
l[g ]=3
W = {a, b, c, e}
T. Häberlein HS AlbSig
8
l[e]=7
11
c
2
e
g
8
l[e]=7
l[c]=9
l[g ]=3
Algorithmik mit Python
3
4
l[u]=0
11
c
3
7
e
2
g
l[c]=11 l[u]=0
2
l[g ]=3
5
b
e
W = {a, b, c, d, e}
l[u]=0
11
c
d
10
b
W = {a, b, c, d, e, g }
a
1
5
a
3
l[e]=7
f
3
7
4
5
l[f ]=2
2
u
W = {a, b, c, d, e, f , g }
5
b
4
l[f ]=2
2
7
11
c
l[d]=4
W = {a, b, c, d, e, f , g , u}
a
3
b
g
8
2
a
f
5
4
u
l[f ]=2
2
f
3
7
1
5
b
10
l[b]=13
d
2
l[d]=4
e
8
l[e]=6
g
l[g ]=3
W = {a, b}
Leipzig, 04.04.2011
41 / 57
Graphen
Kürzeste Wege
Der Dijkstra-Algorithmus – Implementierung
1
2
3
4
5
6
7
8
9
10
def dijkstra(u,graph):
n = graph.numNodes
l = { u : 0 } ; W = graph.V()
F = [] ; k = {}
for i in range(0,n):
lv,v = min([ (l[lk],lk) for lk in l.keys() if lk in W ])
W.remove(v)
if v!=u: F.append(k[v])
...
return l,F
W: Menge der noch zu bearbeitenden Knoten
k[v]: Vorgängerknoten von v auf kürzestem Weg nach v (vorläufig).
F: Vorgängerknoten von v auf kürzestem Weg nach v (final).
Aufgabe 23
Vervollständigen Sie die Implementierung des Dijkstra-Algorithmus.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
42 / 57
Graphen
Minimaler Spannbaum
Minimaler Spannbaum – Kruskal-Algorithmus
Spannbaum =
ˆ Teilgraph GT = (VT , ET ) eines ungerichteten
zusammenhängenden Graphen G = (V , E ), der ein Baum (also
kreisfrei und zusammenhängend) ist.
a
d
b
f
g
c
e
h
a
d
b
c
f
g
e
h
a
d
b
c
f
h
g
e
Anwendungen:
I
I
Möglichst preisgünstiges zusammenhängiges Netzwerk.
Vermeidung von redundanten Sendepfaden.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
43 / 57
Graphen
Minimaler Spannbaum
Kruskal-Algorithmus – Funktionsweise
Algorithmus ist greedy:
In jedem Schritt wird immer genau eine Kante hinzugenommen, für
die gilt:
I
minimales Gewicht + ohne dass Kreis entsteht.
. . . solange, dass bis dies nicht mehr geht.
⇒ Minimaler Spannbaum gefunden.
3
1
2
2
2
3
4
2
4
5
1
3
1
1
3
5
8
7
1
1
2
2
1 9
3
2
3
4
4
2
5
1
1
6
3
1
2
3
2
2
1
4
2
1
3
3
5
8
7
1
1
1 9
3
2
2
2
3
4
2
3
1
3
3
5
1
8
7
1
1 9
6
T. Häberlein HS AlbSig
2
3
2
2
1
4
2
1
5
1
8
5
3
7
1
1 9
3
6
4
5
4
1
1
6
4
5
1
1
3
3
3
5
8
7
1
1
1 9
6
Algorithmik mit Python
2
3
2
2
1
4
2
4
5
1
1
3
5
8
7
3
1
1 9
6
Leipzig, 04.04.2011
44 / 57
Graphen
Minimaler Spannbaum
Kruskal-Algorithmus – Implementierung
1
2
3
4
5
6
7
8
9
def kruskal(graph):
allEdges = [(graph.w(i,j),i,j) for i,j in graph.E_undir()]
allEdges.sort(reverse=True) # absteigend
spannTree = []
while len(spannTree) < len(graph.V())-1 and allEdges!=[]:
(w,i,j) = allEdges.pop()
if not buildsCircle(spannTree,(i,j)):
spannTree.append((i,j))
return spannTree
Aber:
1
Sortieren aller Kanten:O(|E | log |E |) ⇒ ineffizienter als Verwendung
eines Heap: O(|E | + |V | log |E |)
2
Implementierung von buildsCircle?
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
45 / 57
Graphen
Minimaler Spannbaum
Union-Find-Datenstruktur
1
2
3
4
5
6
7
8
9
1
2
3
4
5
7
8
9
3
4
5
7
8
3
4
5
(a):find(3) ∪ find(6)
6
1
Bietet effiziente Implementierung
der Mengenoperationen . . .
1
2
. . . Vereinigung“ (zweier Mengen)
”
union(x,y) – x und y eindeutige
Repräsentanten einer Menge
. . . Suche“ eines Elementes in
”
einer Menge –
find(x) – liefert eindeutigen
Repräsentanten der Menge, die x
enthält.
2
6
1
9
2
7
6
2
3
5
8
9
2
5
(e):find(5) ∪ find(6)
7
3
4
8
6
9
(f):find(1) ∪ find(3)
2
5
1
7
3
4
8
6
Gleichzeitig: Effizienter Test, ob
durch das Hinzufügen einer Kante
(i, j) ein Kreis entsteht.
(d):find(4) ∪ find(8)
7
4
6
1
(c):find(7) ∪ find(9)
8
9
1
(b):find(8) ∪ find(9)
9
(g):find(2) ∪ find(1)
5
2
1
7
3
4
6
8
9
(h):find(6) ∪ find(7)
7
4
5
2
1
3
8
9
6
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
46 / 57
Graphen
Minimaler Spannbaum
Union-Find im Kruskal-Algorithmus
Kante {i, j} hinzufügen?
Zwei Fälle:
1 Falls find(i)==find(j):
Nicht hinzufügen!
Denn: i und j befinden sich in derselbsen Zusammenhangskomponente.
⇒ i und j verbunden.
⇒ Es würde ein Kreis entstehen.
I
I
2
Falls find(i)!=find(j):
Hinzufügen!
Denn: i und j bisher nicht verbunden.
⇒ Es entsteht kein Kreis
I
I
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
47 / 57
Graphen
Minimaler Spannbaum
Union-Find Implementierung
self.parent: Speichert für
jeden Knoten den Elternknoten.
1
self.parent[i]== 0 gdw. i hat
2
keinen Elternknoten.
3
class UF(object):
def __init__(self,n):
self.parent = [0]*n
4
find(x): Liefert Wurzel des
Baumes, der x enthält.
union(x,y): fügt zwei Bäume
zusammen, indem die Wurzel
des einen Baumes (der y
enthält) als Kind unter die
Wurzel des anderen Baumes
(der x enthält) gehängt wird.
T. Häberlein HS AlbSig
5
6
7
8
def find(self,x):
while self.parent[x] > 0:
x = self.parent[x]
return x
9
10
11
def union(self,x,y):
self.parent[y] = x
Algorithmik mit Python
Leipzig, 04.04.2011
48 / 57
Graphen
Minimaler Spannbaum
Union-Find – Aufgaben/Verbesserungen
Aufgabe 24
Implementieren Sie für die Klasse UF die str-Funktion, die ein Objekt der
Klasse in einen String umwandelt. Beispiel-Ausgabe:
>>>
>>>
>>>
>>>
uf = UF(10)
uf.union(1,2) ; uf.union(1,3) ; uf.union(5,6) ; uf.union(8,9)
str(uf)
'{1, 2, 3} {4} {5, 6} {7} {8, 9} '
Eine sehr nützliche Verbesserung: Balancierung!
Aufgabe 25
Verbessern Sie die Union-Find-Impl. indem Sie auf die Balancierung der
Bäume achten. find(x) sollte nur dann als Kind unter die Wurzel von
find(y) gehängt werden, wenn Höhe(find(x)) < Höhe(find(y));
andernfalls: find(y) unter die Wurzel von find(x) hängen.
Tipp: (Negative) Höhe in der Wurzel speichern.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
49 / 57
Graphen
Minimaler Spannbaum
Union-Find – Aufgaben/Verbesserungen
Eine weitere sehr nützliche Verbesserung: Pfad-Komprimierung!
Aufgabe 26
find(x) findet immer den Pfad von x zur Wurzel.
⇒ füge danach Direktkante von x zur Wurzel ein.
. . . und auch Direktkanten für alle Knoten auf dem Pfad.
Implementieren Sie diese Verbesserung.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
50 / 57
Graphen
Minimaler Spannbaum
Kruskal-Algorithmus – Aufgabe
Aufgabe 27
Implementieren Sie den Kruskal-Algorithmus unter Verwendung der
Union-Find-Datenstruktur.
Aufgabe 28
Man kann den minimalen Spannbaum auch finden, indem man genau
umgekehrt wie der Kruskal-Algorithmus vorgeht: Man beginne mit allen im
Graphen enthaltenen Kanten und entfernt Kanten mit dem momentan
höchsten Gewicht – nur dann aber, wenn man dadurch den Graphen nicht
auseinander bricht.
Geben Sie eine Implementierung des umgekehrten“ Kruskal-Algorithmus
”
an.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
51 / 57
Schwere Probleme
Lösung des Traveling Salesman Problems
Das TSP-Problem
Hamburg
Bremen
TSP =
ˆ Travelling Salesman
Problem.
Gegeben:
n Städte
Gesucht:
Kürzeste Rundtour, die jede
Stadt genau einmal besucht.
Berlin
Hannover
Bielefeld
Dortmund
Bochum
Duisburg
Essen
Wuppertal
Düsseldorf
Köln
Bonn
Leipzig
Dresden
Frankfurt am Main
Mannheim
Nürnberg
Stuttgart
München
Lösung des TSP für die 20
größten Deutschen Städte.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
52 / 57
Schwere Probleme
Lösung des Traveling Salesman Problems
TSP-Lösung durch Ausprobieren
Lösung des TSP =
ˆ Permutation der n Städte.
⇒ Durchprobieren aller Permutationen perms(graph.V())
Aufgabe 29
Implementieren Sie den Brute-Force-Lösungsansatz Durchprobieren aller
”
Permutationen“, um die optimale Lösung des TSP zu finden und
vervollständigen Sie hierzu den folgenden Code:
1
2
3
def TSPBruteForce(graph):
nodeList = graph.V()[1:]
return ...
Tipp: Verwenden Sie graph.pathVal um die Länge eines Pfades zu
bestimmen.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
53 / 57
Schwere Probleme
Lösung des Traveling Salesman Problems
TSP-Lösung durch Dynamische Programmierung
Fürs TSP gilt das sog. (Bellmannsche) Optimalitätsprinzip:
Eine optimale Lösung setzt sich zusammen aus . . .
. . . kleineren“ optimalen Lösungen.
”
⇒ Lösung durch Dynamische Programmierung möglich:
Zuerst: Lösungen der kleinen“ Teilprobleme berechnen . . .
”
. . . und Zwischenergebnisse in Tabelle speichern.
Bei Berechnung der größeren Teilprobleme: Auf Tabelle zurückgreifen.
Fürs TSP gilt:
T (i, S): Wert der kürzesten Tour, startend bei Knoten i, die alle
Knoten aus S genau einmal besucht und bei Knoten 1 endet
Dann gilt:
T (i, S) = min w (i, j) + T (j, S \ {j})
j∈S
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
54 / 57
Schwere Probleme
Lösung des Traveling Salesman Problems
TSP-Lösung durch Dynamische Programmierung (2)
Die Formel
T (i, S) = min w (i, j) + T (j, S \ {j})
j∈S
Entspricht in Python (T ist Dict-Objekt):
T[(i,S)] = min( graph.w(i,j)+T[(j,diff(S,[j]))] for j in S)
1
2
3
4
5
6
def tsp(graph):
n = graph.numNodes
T = {}
for i in range(1,n+1): T[(i,())] = graph.w(i,1)
for k in range(1,n-1):
...
Aufgabe 30
Vervollständigen Sie die Implementierung und ersetzen Sie die ... durch
den entsprechenden Code.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
55 / 57
Schwere Probleme
Greedy-Heuristiken zur Lösung des TSP
Nearest-Neighbor-Heuristik
Von der aktuellen Stadt aus . . .
. . . wählt man einfach immer die nächste aus.
Aufgabe 31
Implementieren Sie die Nearest-Neighbor-Heuristik für das TravelingSalesman-Problem und testen Sie diese durch Berechnung der kürzesten
Tour durch die . . .
1
. . . größten 20 deutschen Städte.
2
. . . größten 40 deutschen Städte.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
56 / 57
Schwere Probleme
Greedy-Heuristiken zur Lösung des TSP
Insertion-Heuristiken
Man beginnt mit sehr kurzer (2 Städte) Tour . . .
. . . und fügt sukzessive weitere Knoten hinzu.
Folgende Strategien:
Nearest Insertion“: Als nächtes wird derjenige Knoten hinzugefügt,
”
der zur momentanen Tour den geringsten Abstand hat.
”Farthest Insertion”: Als nächtes wird derjenige Knoten hinzugefügt,
der zur momentanen Tour den größten Abstand hat.
”Random Insertion”: Als nächtes wird zufällig ein noch nicht in der
Tour befindlicher Knoten zur Tour hinzugfügt.
Aufgabe 32
Implementieren Sie die Nearest/Farthest/Random-Insertion-Heuristik.
T. Häberlein HS AlbSig
Algorithmik mit Python
Leipzig, 04.04.2011
57 / 57
Herunterladen