Diskrete Modellierung Wintersemester 2016/17 Martin Mundhenk Uni Jena, Institut für Informatik 12. Februar 2017 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra 1 Suche nach kürzestem Weg von a nach d: e c 3 f 4 a 2 1 4 5 10 3 d b 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 8 e 4 a 0 1 3 2 1 8 f 5 4 8 c 10 3 8 d b 8 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 8 e 1 3 8 f 5 8 c 3 8 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 8 0 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 8 e 1 3 8 f 5 8 c 3 8 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 8 0 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 8 e 1 3 8 f 5 8 c 3 8 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 8 e 1 3 8 f 5 8 c 3 8 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 8 e 1 3 8 f 5 8 c 3 8 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 6 c 5 3 11 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 6 c 5 3 11 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 6 c 5 3 11 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 11 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 11 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 11 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 7 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 7 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 7 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 7 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 7 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 4.6 Suche nach kürzesten Wegen in gewichteten Graphen Der Algorithmus von Dijkstra Suche nach kürzestem Weg von a nach d: zu Beginn hat a Entfernung 0 und alle anderen Knoten haben Entfernung 8 3 e 1 3 5 f 4 c 5 3 7 wiederhole, bis alle“ Knoten beendet sind: d 4 ” 10 4 2 suche den nicht-beendeten Knoten x mit 1 kleinster Entfernung a b jetzt ist Knoten x beendet 0 1 markiere alle Nachbarn von x wie folgt: wenn ein Nachbar mit 8 markiert ist: markiere ihn mit Markierung von x + Gewicht der Kante von x zu ihm sonst: wenn der Nachbar nicht beendet ist: markiere ihn mit dem Minimum von (1) seiner bisherigen Markierung und (2) Markierung von x + Gewicht der Kante 4.6.1 Der Algorithmus formuliert mit MinHeap (Prioritätswarteschlange) Eingabe: ein Graph, ein Startknoten a und ein Zielknoten d pw ist ein leerer MinHeap (Prioritätswarteschlange, kleinstes Element zuerst) (die Knoten in pw werden gemäß ihrer Entfernung eingefügt) Knoten a wird mit Entfernung 0 und als gefunden markiert (d.h. Entfernungă 8) pw .insertpaq solange pw nicht leer ist wiederhole: x “ pw .extractMinpq (das entfernt den Knoten mit der kleinsten Entfernung aus pw ) markiere x als beendet für alle Nachbarn nb von x wiederhole: wenn nb noch nicht gefunden war: markiere nb als gefunden der Vorgänger von nb ist x markiere nb mit Entfernung von x + Gewicht der Kante von x zu b pw .insertpnbq sonst: wenn nb noch nicht beendet ist und seine Entfernungsmarkierung größer ist als die Entfernung von x + Gewicht der Kante von x zu nb: markiere ihn mit Entfernung von x + Gewicht der Kante von x zu nb der Vorgänger von nb ist x pw .updatepnbq (aktualisiere die Position von nb in pw ) falls d einen Vorgänger hat, dann kann der Pfad von a zu d ausgegeben werden Die Größenordnung der Rechenzeit des Algorithmus Wir bezeichnen die Anzahl der Knoten des Graphen mit n und die Anzahl der Kanten mit m. § In jedem Durchlauf der Schleife wird ein Knoten mit minimaler Entfernungsmarkierung gesucht. § Dabei werden alle von ihm ausgehenden Kanten betrachtet. Der Endpunkt jeder Kante wird mit Entfernungs- und Vorgängerinformation markiert. Zum Suchen nach dem Knoten mit minimaler Entfernungsmarkierung bietet sich die Datenstruktur MinHeap an. Bei einem MinHeap mit ` Elementen benötigt man maximal § log ` Schritte zum Einsortieren eines Knotens § log ` Schritte zum Entfernen des Minimum-Knotens und zur darauffolgenden Reorganisation des MinHeaps § log ` Schritte zur Reorganisation des MinHeaps nach Änderung der Entfernungsmarkierung eines Knotens im MinHeap Letztlich ist für jede Kante eine dieser MinHeap-Operationen nötig. Damit kommt man auf eine Rechenzeit der Größenordnung m ¨ log n. Das ist etwas mehr als bei der Breitensuche in ungewichteten Graphen (Größeno. m). Die Datenstrukturen für den Algorithmus von Dijkstra Wir erweitern die Datenstruktur Graph für gewichtete Graphen dadurch, dass jede Kante ein Gewicht erhält. Wir brauchen eine Datenstruktur, mit der wir schnell den Knoten mit der geringsten Entfernung vom Startknoten finden können. Dazu benutzen wir eine Erweiterung von MinHeap (auch: Prioritätswarteschlange, priority queue), bei der man auch die Priorität von Elementen, die bereits im Heap sind, verkleinern kann. Die Implementierung dieses dynamischen MinHeaps ist das Aufwendigste an der Implementierung des Algorithmus. Die API für dynMinHeap Operation dynMinHeap() Beschreibung ein neuer leerer dynamischer MinHeap h.insert(name,prioritaet) trage ein neues Element mit Namen name und Priorität prioritaet in den Heap ein h.extractMin() entferne das Element mit der kleinsten Priorität aus dem Heap und gib seinen Namen als Ergebnis zurück h.decreaseKey(elem) elem ist bereits im Heap, muss nach einer Änderung seiner Attribute aber wieder an die richtige Stelle gebracht werden h.isEmpty() gibt True zurück, falls der Heap leer ist, und False sonst Die Operationen insert(), extractMin() und decreaseKey() haben für einen MinHeap aus ` Knoten eine Rechenzeit der Größenordnung log `. Wir werden dynMinHeap als Erweiterung von MinHeap implementieren. Zusätzlich zum Heap von MinHeap benutzen wir ein Dictionary für die Zuordnung von Knoten (Schlüssel) zu den Knoteninformationen Priorität und Index im Heap. Im Heap stehen dann nur noch Referenzen auf die Knoteninformationen im Dictionary. Um decreaseKey(knoten) auszuführen, wird im Dictionary der Index k von knoten nachgeschlagen. Nun kann im Heap der Eintrag mit Index k an die richtige Stelle aufsteigen. Dabei muss der sich ändernde Index in den Knoteninformationen protokolliert“ werden. ” Faszinierenderweise kann man das alles recht einfach auf MinHeap draufsetzen“ – selbst das o.g. Protokollieren“ geht mittels ” ” Überschreiben der Methode setitem . 4.6.6 Die Implementierung von dynMinHeap #-----------------------------------------------------------------------------# dynMinheap.py # dynMinHeap mit den Operationen insert, extractMin, decreaseKey #-----------------------------------------------------------------------------from Minheap import MinHeap class _eintrag: # Hier steht die Information fuer einen Eintrag im dynamischen Heap: # sein Wert (nach dem er im MinHeap eingeordnet wird) und sein Index im MinHeap. # Letzteres benoetigt man, wenn sich der Wert verkleinert # und der Eintrag im MinHeap aufsteigen muss. def __init__(self, name, prioritaet=0, index=0): self._name = name self._prior = prioritaet self._index = index def setzeIndex(self,i): self._index = i def index(self): return self._index def prioritaet(self): return self._prior 4.6.7 class dynMinHeap(MinHeap): # Der dynMinHeap ist # ein Dictionary _d aus Knotennamen und _eintrag-Objekten # und ein MinHeap aus Referenzen auf die _eintrag-Objekte in _d. def __init__(self): # Am Anfang ist _d leer und der MinHeap ist leer. self._d = dict() MinHeap.__init__(self) def __setitem__(self, index, eintrag): # Wenn Element eintrag im MinHeap _h an Stelle index gesetzt wird, # muss der Index im Element entsprechend eingetragen werden. MinHeap.__setitem__(self, index, eintrag) eintrag.setzeIndex(index) def insert(self, name, prioritaet=0): # Das neue Element mit Key name wird in _d eingetragen. # Anschließend wird es in den MinHeap eingefügt. self._d[name] = _eintrag(name, prioritaet, len(self)) MinHeap.insert(self,self._d[name]) 4.6.8 def extractMin(self): # Das kleinste Element des MinHeaps wird entfernt und # sein Name wird als Ergebnis zurückgegeben. element = MinHeap.extractMin(self) return element.name() def decreaseKey(self,name,e): # Wenn der Wert eines Heap-Elements verkleinert wird, # dann muss es anschließend im Heap aufsteigen. self._d[name].setzePrioritaet(e) MinHeap._update(self, self._d[name]._index) def prioritaet(self, name): return self._d[name].prioritaet() def eingetragen(self): return self._d 4.6.9 Die Datenstruktur für gewichtete Graphen Wir erweitern die Datenstruktur Graph für gewichtete Graphen dadurch, dass jede Kante ein Gewicht erhält. Operation Beschreibung gewGraph(dateiname=None,trennzeichen=’ ’) ein neuer gewichteter Graph. Falls dateiname als Argument übergeben wird, wird der Graph aus der Datei dateiname eingelesen; die Datei enthält in jeder Zeile zwei Knoten, die durch trennzeichen getrennt sind. Anderenfalls ist der Graph leer, d.h. er enthält weder Knoten noch Kanten. g[knotenname] ein Dictionary aus Zielknoten/Gewicht-Paaren der Kanten mit Startpunkt knotenname g.random(knotenzahl,dichte) macht Graph g zu einem zufälligen Graph mit knotenzahl Knoten und Kantenwahrscheinlichkeit dichte. Die Kantengewichte werden zufällig aus dem Intervall p0, 1q gewählt. class gewGraph: def __init__(self, dateiname=None, trennzeichen=' '): self._adj = dict() if dateiname is not None: eingabe = InStream(dateiname) while eingabe.hasNextLine(): zeile = eingabe.readLine() k = zeile.split(trennzeichen) for i in range(1,len(k),2): self.kanteHinzufuegen(k[0],k[i],float(k[i+1])) def kanteHinzufuegen(self,von,nach,gewicht): if not self.hatKnoten(von): self.knotenHinzufuegen(von) if not self.hatKnoten(nach): self.knotenHinzufuegen(nach) if not self.hatKante(von,nach): self._adj[von][nach] = gewicht # bei ungerichteten Graphen muss die folgende Zeile hinzugefügt werden # self._adj[nach][von] = gewicht def knotenHinzufuegen(self,k): if not self.hatKnoten(k): def hatKnoten(self,v): return v in self._adj def hatKante(self,v,n): if self.hatKnoten(v): return n in self._adj[v] return False self._adj[k] = dict() 4.6.11 def gewicht(self,v,n): return self._adj[von][nach] def __getitem__(self, k): return self._adj[k] def random(self,knotenzahl=1000,dichte=0.1): # macht aus dem Graphen einen zufälligen Graphen mit knotenzahl Knoten. # Es werden genau dichte*knotenzahl*knotenzahl Kanten zufällig eingefügt. # Die Kantengewichte sind zufällig aus den Intervall (0,1) gewählt. self._adj = dict() for j in range(int(dichte*knotenzahl*knotenzahl)): erfolg = False while not erfolg: a = str(stdrandom.uniformInt(0,knotenzahl)) b = str(stdrandom.uniformInt(0,knotenzahl)) if not a==b and not self.hatKante(a,b): self.kanteHinzufuegen(a,b,stdrandom.uniformFloat(0,1)) erfolg = True def __str__(self,k=2): s = "" for knoten in self._adj: s += knoten + ': ' for n in self._adj[knoten]: s += n + " (" + str(round(self._adj[knoten][n],k)) + ") " s += "\n" return s 4.6.12 Ein Test-Klient für gewGraph # gewgraphtest.py import sys, stdio from Graph import gewGraph dateiname = sys.argv[1] g = gewGraph(dateiname) stdio.write(g) stdio.writeln('-----------------') g = gewGraph() g.random(10,0.1) stdio.writeln(g.__str__(3)) ======================================== $ 0 1 1 1 1 4 4 2 5 3 more GraphVL13gew.txt 1 1.0 2 5.0 3 10.0 4 2.0 5 4.0 5 3.0 2 1.0 3 3.0 6 3.0 6 4.0 $ python gewgraphtest.py GraphVL13gew.txt 1: 3 (10.0) 2 (5.0) 5 (4.0) 4 (2.0) 0: 1 (1.0) 3: 6 (4.0) 2: 3 (3.0) 5: 6 (3.0) 4: 2 (1.0) 5 (3.0) 6: ----------------1: 5 (0.092) 0: 3: 9 (0.361) 7 (0.364) 6 (0.897) 2: 0 (0.871) 7 (0.189) 5: 7: 6: 7 (0.802) 9: 6 (0.601) 8: 9 (0.271) 3 (0.325) Die Implementierung des Algorithmus von Dijkstra #------------------------------------------------------------------------------------# Dijkstra.py # # Die Klasse Dijkstra implementiert den Algorithmus von Dijkstra. # graph ist vom Typ gewGraph (aus graph.py), # Von startknoten aus werden kürzeste Wege zu den Knoten des Graphen gesucht. # # Der Test-Klient liest einen Dateinamen und zwei Knotennummern von der Kommandozeile, # liest aus der Datei einen gerichteten gewichteten Graphen ein, # führt den Algorithmus von Dijkstra auf dem Graphen für den ersten Knoten durch # und gibt abschließend den kürzesten Weg zum zweiten Knoten aus. #------------------------------------------------------------------------------------import sys, stdio from dynMinHeap import dynMinHeap from Graph import gewGraph class Dijkstra: 4.6.14 def __init__(self, graph, startknoten): # Die Suche wird initialisiert. # Es wird ein dynMinHeap erzeugt, in den der Startknoten mit Entfernung 0 eingetragen wird. self._startknoten = startknoten heap = dynMinHeap() heap.insert(startknoten,0) # In _vorgaengerTabelle steht am Ende zu jedem gefundenen Knoten der Vorgaenger auf dem kuerzes self._vorgaengerTabelle = dict() self._vorgaengerTabelle[startknoten] = None self._beendet = set() # Die Suche wird gestartet. while not heap.isEmpty(): aktuellerKnoten = heap.extractMin() self._beendet.add(aktuellerKnoten) # Durchsuche alle Nachbarn des aktuellen Knotens. for nachbar in graph[aktuellerKnoten]: if not nachbar in heap.eingetragen(): # Falls der Nachbar erstmals gefunden wurde, dann setze aktuellerKnoten als seinen # Vorgänger und sortiere ihn mit der richtigen Entfernung im Heap ein. self._vorgaengerTabelle[nachbar] = aktuellerKnoten heap.insert(nachbar,heap.prioritaet(aktuellerKnoten)+graph[aktuellerKnoten][nachbar]) elif nachbar not in self._beendet and \ heap.prioritaet(nachbar) > heap.prioritaet(aktuellerKnoten) + graph[aktuellerKnoten][ # Falls der Nachbar bereits gefunden wurde, dann muss seine # Vorgängermarkierung geändert werden, falls aktuellerKnoten # ein "besserer" Vorgänger ist als der bisher gefundene. # In dem Fall muss die Stelle des Knotens im Heap auch aktualisiert werden. self._vorgaengerTabelle[nachbar] = aktuellerKnoten heap.decreaseKey(nachbar,heap.prioritaet(aktuellerKnoten)+graph[aktuellerKnoten][nachba 4.6.15 def ausgabeWeg(self, ziel): # gibt den Weg von startknoten zum Knoten ziel als Array zurück. erg = [ziel] while not self._vorgaengerTabelle[ziel] is None: ziel = self._vorgaengerTabelle[ziel] erg += [ str(ziel) ] erg.reverse() return erg def wegnach(self,k): return k in self._vorgaengerTabelle #-- Ende von Dijkstra ---------------------------------------------------- 4.6.16 Zwei Test-Klienten für Dijkstra # dijdatei.py import ... # Lies Dateiname für den Graph und zwei Knotennamen von der Kommandozeile ein. dateiname = sys.argv[1] start = sys.argv[2] ziel = sys.argv[3] # Der Graph wird eingelesen und der Algorithmus von Dijkstra wird ausgeführt. graph = gewGraph(dateiname) d = Dijkstra(graph, start) # Falls ein Weg von start nach ziel gefunden wurde, dann wird er ausgegeben. if d.wegnach(ziel): print d.ausgabeWeg(ziel) else: stdio.writef('Es gibt keinen Weg von %s nach %s.\n', start, ziel) $ python dijdatei.py GraphVL13gew.txt 0 3 ['0', '1', '4', '2', '3'] $ python dijdatei.py GraphFlughaefen.txt ERF MUN ['ERF', 'LGW', 'UVF', 'GND', 'PMV', 'MUN'] # dijzufall.py # führt den Algorithmus von Dijkstra auf einem zufälligen Graphen aus. import sys, stdio from Graph import gewGraph from Dijkstra import Dijkstra # Lies die Knotenzahl, Dichte und zwei Knotennummern von der Kommandozeile ein. knotenzahl = int(sys.argv[1]) dichte = float(sys.argv[2]) start = sys.argv[3] ziel = sys.argv[4] # Der zufällige Graph wird erzeugt und der Algorithmus von Dijkstra wird darauf ausgeführt. graph = gewGraph() graph.random(knotenzahl,dichte) d = Dijkstra(graph, start) # Falls ein Weg gefunden wurde, dann wird er ausgegeben. if d.wegnach(ziel): print d.ausgabeWeg(ziel) else: stdio.writef('Es gibt keinen Weg von %s nach %s.\n', start, ziel) $ python dijzufall.py 100 0.1 20 30 ['20', '65', '91', '19', '45', '1', '51', '30'] $ python dijzufall.py 100 0.1 20 30 ['20', '26', '99', '30'] $ python dijzufall.py 1000 0.002 20 30 Es gibt keinen Weg von 20 nach 30. $ python dijzufall.py 1000 0.002 20 30 ['20', '897', '736', '133', '273', '156', '713', '8', '735', '508', '30'] 4.6.18 Zusammenfassung Der Algorithmus von Dijkstra ist ein typisches Beispiel für die Lösung eines Problems mit Informatik-Methoden. § es ist ein Optimierungsproblem zu lösen § die Lösung erfordert ein geschicktes Zusammenspiel von Algorithmen und Datenstrukturen 4.6.19 Abschlussprojekte für Diskrete Modellierung Wintersemester 2016/17 Asteroids Seam Carving Igel ärgern Magnetpendel 5.0.1