Graphen und ihre Verarbeitung Klaus Becker 2010 2 Graphen 3 Teil 1 Vernetzte Strukturen 4 Routenplanung Typisches Routenproblem: Wie kommt man am besten mit dem Auto von Worms nach Speyer? 5 Routenplanung mit dem Computer Modellierungsproblem: Wie wird das Verkehrsnetz im Computer repräsdentiert? Berechnungsproblem: Wie wird die optimale Route ermittelt? 6 Beschreibung vernetzter Strukturen Modellierung: Will man die Information über ein solches Straßennetz beschreiben, so konzentriert man sich auf die relevanten Fakten und lässt alles andere erst einmal weg. Ist man z.B. nur an den Autobahnverbindungen um Ludwigshafen / Mannheim interessiert, so könnte eine abstrahierende Beschreibung des Autobahnnetzes wie folgt aussehen: 7 Fachkonzept Graph Ein Graph besteht aus einer Menge von Knoten und einer Menge von Kanten (die jeweils zwei Knoten miteinander verbinden). Mathematisch: G = (V, E) V = {WO, KR-FR, ..., SP, ...} ist die Menge der Knoten. E = {{WO, KR-FR}, ... {SP, HO}, ...} ist die Menge der Kanten. Graphen dienen dazu, vernetzte Strukturen zu beschreiben. Die Knoten repräsentieren die Objekte des Anwendungsbereichs, die Kanten die Beziehungen zwischen den Objekten. Graphen sind ein wichtiges und häufig benutztes Modellierungsmuster in der Informatik (und Mathematik), das in ganz unterschiedlichen Anwendungsbereichen zum Einsatz kommt. 8 Graph-Editor siehe: http://www.yworks.com/en/products_yed_about.html 9 Graphen in Anwendungssituationen Aufgabe (a) Wie sind die Verbindungslinien zwischen den Fährhäfen hier zu deuten? Warum beschreiben sie nicht die tatsächlichen Schifffahrsrouten? Warum reicht hier eine abstrahierende Darstellung der Fährverbindungen? (b) Man möchte zusätzlich die Streckenlängen der Fährverbindungen angeben. Wie könnte man solche Informationen in der gezeigten Darstellung integrieren? siehe auch: http://www.ferrylines.com/de/europaeisch es-buchungsportal-fuer-faehren/ 10 Graphen in Anwendungssituationen Aufgabe Wie kommt man über Land von Deutschland nach Finnland? Wie viele Ländergrenzen muss man dabei mindestens passieren? (a) Der (noch unfertige Graph) zeigt Deutschland und seine Nachbarländer. Trage erst einmal sämtliche noch fehlenden Nachbarländer in die bereits vorgegebenen Knoten ein. (b) Wie würde man die Kanten in der gezeigten Darstellung deuten? (c) Erweitere das Diagramm um die Nachbarländer von Polen usw., bis du das oben beschriebene Problem mit Hilfe des Graphen lösen kannst. (d) Wie kommt man über Land von Finnland nach Irland? Wie würde man das am Graphen erkennen? siehe auch: http://moritz.stefaner.eu/projects/relation-browser/relationViewer.swf 11 Graphen in Anwendungssituationen Aufgabe In Königsberg (heute Kaliningrad) teilt der Fluss Pregel die Stadt in mehrere Stadtteile auf. Der Stadtplan hebt die Brücken hervor, die die Stadtteile miteinander verbinden. Der Mathematiker Euler beschäftigte sich mit der Frage, ob es einen Rundgang durch Königsberg gibt, der jede der sieben Brücken über den Fluss Pregel genau einmal benutzt. (a) Versuche erst einmal, das Problem von Euler zu lösen. (b) Der folgende Graph zeigt die Brückensituation in Königsberg in einer abstrahierenden Darstellung. Was stellen die Knoten des Graphen, was die Kanten dar? (c) Wie viele Kanten gehen von den jeweiligen Knoten aus? Was hat das mit der Lösung des Problems von Euler zu tun? 12 Graphen in Anwendungssituationen An: Daniel Hallo Daniel, Rebekka hat dich als Freund/Freundin auf ... hinzugefügt. Wir benötigen deine Bestätigung, dass du Rebekka kennst, damit ihr Freunde/Freundinnen auf ... sein könnt. Viele Grüße vom ... Aufgabe (a) Beschreibe die Struktur des folgenden (siehe rechts Mitte) wer-kennt-wenNetzwerks mit Hilfe eines Graphen. (b) Noch ein soziales Netzwerk (siehe rechts unten). Wie würde man hier die Vernetzungungsstruktur darstellen? (c) Bist du Mitglied in einem sozialen Netzwerk? Wie hoch ist dein Mitgliedsbeitrag? Nimm Stellung zu folgender Aussage: "Die Mitglieder zahlen mit ihren Daten." Rebekka kennt Daniel, Florian, Lisa, Greta, Sophie, Maria Daniel kennt Tim, Rebekka, Jonas Florian kennt Rebekka, Sophie Lisa kennt Rebekka, Sophie, Jonas Greta kennt Rebekka, Sophie, Tim Sophie kennt Rebekka, Florian, Lisa, Greta Maria kennt Rebekka Tim kennt Daniel, Greta Jonas kennt Daniel, Lisa Martin kennt Markus Markus kennt Martin Anna liebt Ben Ben liebt Clara Clara liebt Daniel Daniel liebt Anna 13 Graphen in Anwendungssituationen Aufgabe Zum Nudelkochen im Ferienlager werden genau 2 Liter Wasser benötigt. Zum Abmessen stehen nur ein kleiner Eimer, der 3 Liter fasst, und einen etwas größerer Eimer, der 4 Liter fasst, zur Verfügung. Kann das funktionieren? Um systematisch alle durch Umfüllen erzeugbaren Wassermengen zu bestimmen, kann man einen Zustandsgraphen erstellen. Die Knoten des Graphen sind die aktuellen Füllinhalte der beiden Eimer. Die Kanten des Graphen stellen die Umfüllvorgänge dar. Vervollständige den bereits begonnenen Graphen. Ermittle mit diesem Graphen verschiedene Möglichkeiten, eine 2-LiterWassermenge durch Umfüllen zu erzeugen. 14 Graphen in Anwendungssituationen Aufgabe Am Flussufer befinden sich ein Wolf, eine Ziege, ein Kohlkopf und ein Fährmann. Der Fährmann soll Wolf, Ziege und Kohlkopf auf die andere Seite des Flusses bringen. Er kann aber immer nur einn der drei mit an das andere Ufer nehmen und muss die beiden anderen so lange allein lassen. Da nun der Wolfe gerne die Ziege frisst und die Ziege auf den Kohlkopf scharf ist, kann der Fährmann diese Paarungen nicht allein lassen. Kann der Fährmann das Transportproblem lösen? Versuche, das Transportproblem systematisch zu lösen, indem du den bereits begonnenen Transport-Graphen vervollständigst. Graphenterminologie 15 Je nach Anwendung ist es sinnvoll, Kanten gerichtet oder ungerichtet zu modellieren. Bei gerichteten Graphen werden in der Regel auch sog. Schlingen zugelassen. Werden die Kanten mit zusätzlichen Gewichten versehen, so spricht man von gewichteten oder bewerteten Graphen. E B E B 5 12 8 F A 11 F A D D 17 G C gerichteter, unbewerteter Graph 3 H C 6 8 G 5 ungerichteter, bewerteter Graph H 16 Graphenterminologie In einem Multigraphen können Knoten über mehrere Kanten miteinander verbunden werden. Multigraph 17 Graphenterminologie Ist in einem gerichteten Graphen ein Knoten über eine Kante mit einem anderen Knoten verbunden, so heißen dieser andere knoten Nachbar des Ausgangsknoten. In ungerichteten Graphen sind zwei Knoten Nachbarn, wenn sie über eine Kante miteinander verbunden sind. Ein Knoten, der keine Nachbarn hat, wird auch isolierter Knoten genannt. Ein Weg innerhalb eines Graphen ist eine Folge von Knoten des Graphen, die sukzessive über eine Kante miteinander verbunden sind. Ein einfacher Weg ist ein Weg, in dem - außer gegebenenfalls Startknoten und Endknoten kein Knoten mehrfach vorkommt. Ein einfacher Weg heißt Kreis oder Zyklus, wenn der Startknoten des Weges gleichzeitig Endknoten ist. 18 Übungen Aufgabe (a) Welche Nachbarn hat Knoten 1 (Knoten 2) im Beispielgraphen? (b) Gibt es im Beispielgraphen einen isolierten Knoten? (c) Verdeutliche am Beispielgraphen den Weg [1,4,1,3,2,2]. (d) Gib einen möglichst langen Weg im Beispielgraphen an. (e) Gib es Kreise im Beispielgraphen? Wenn ja, welche? 19 Übungen Aufgabe "Die Leute vom Planeten Chator schreiben gern Schlechtes übereinander.Wer vielen über andere Schlechtes schreibt, gilt als besonders charmant. Aber natürlich nur, wenn die Kompromittierten nichts davon erfahren. Chatonen schreiben nur an Leute, die ihnen sympathisch sind. Doch die können den Tratsch weitertragen, und eventuell genau an den Falschen. Ein Chatone muss also gut aufpassen, dass er keinen Charmefehler macht. Dieses Missgeschick passierte unlängst Ator, als er Btor Schlechtes über Dtor schrieb. Zu dumm: Dtor ist dem Ctor sympathisch, der wiederum Btor sympathisch ist. Und so landete der Tratsch bei Dtor, der über Ator verständlicherweise sehr verärgert war. Dies hätte Ator mit ein wenig Übersicht vermeiden können, denn schließlich wissen alle Chatonen voneinander, wer wem sympathisch ist." (Quelle: Bundeswettbewerb Informatik 2004/2005 - 1. Runde) (a) Stelle die Sympathiebeziehungen der Chatonen mit einem gerichteten Graphen dar. (b) Welches Problem muss hier gelöst werden. Beschreibe es mit Hilfe der Graphenterminologie. 20 Teil 2 Implementierung von Graphen Graphen als Daten 21 Darstellen Deuten Verarbeiten >>> g = >>> sucheWeg(g, 'A', 'C') ['A', 'B', 'C'] def sucheWeg(graph, start, ziel): ... return ... 22 Graphen als Daten Aufgabe Betrachte den folgenden (gerichteten) Graphen. Wie könnte man diesen Graphen in Tabellenform / mit Hilfe von Listen beschreiben? Entwickle hierzu selbst Ideen. Darstellen >>> g = Nachbarschaftstabelle 23 Sämtliche Informationen eines Graphen lassen sich in einer Nachbarschaftstabelle darstellen. Nachbarschaftstabelle Darstellen A B C D A 0 1 0 0 0 1 0 0 B 0 1 1 1 0 1 1 1 C 1 1 0 0 1 1 0 0 D 0 0 0 0 0 0 0 0 knotenliste = ['A', 'B', 'C', 'D'] kantenmatrix = [[0, 1, 0, 0],[0, 1, 1, 1],[1, 1, 0, 0],[0, 0, 0, 0]] testgraph = (knotenliste, kantenmatrix) Kantenmatrix Adjazenzmatrix 24 Nachbarschaftstabelle Aufgabe: (a) (einfach) Entwickle eine Funktion existiertKante(graph, vonKnoten, nachKnoten), die überprüft, ob es eine Kante zwischen den übergebenen Knoten gibt. Ein möglicher Testfall sollte so verlaufen: >>> existiertKante(testgraph, 'A', 'D') False Tipp: Mit knotenliste.index('A') kann man den Index des übergebenen Bezeichners in der Knotenliste bestimmen. (b) (etwas schwieriger) Entwickle eine Funktion getAlleNachbarn(graph, knoten), die sämtliche Nachbarn eines vorgegebenen Knotens ermittelt. Ein möglicher Testfall sollte so verlaufen: >>> getAlleNachbarn(testgraph, 'B') ['B', 'C', 'D'] Nachbarschaftslisten 25 Sämtliche Informationen eines Graphen lassen sich in einer Nachbarschaftstabelle darstellen. Darstellen Nachbarschaftslisten Adjazenzlisten testgraph [ ['A', ['B', ['C', ['D', ] = \ ['B']], ['B', 'C', 'D']], ['A', 'B']], []] A: B B: B C D C: B 1 0 D: 26 Nachbarschaftslisten Aufgabe: (a) (einfach) Entwickle eine Funktion getAlleNachbarn(graph, knoten), die sämtliche Nachbarn eines vorgegebenen Knotens ermittelt. Ein möglicher Testfall sollte so verlaufen: >>> getAlleNachbarn(testgraph, 'B') ['B', 'C', 'D'] (b) (etwas schwieriger) Entwickle eine Funktion existiertKante(graph, vonKnoten, nachKnoten), die überprüft, ob es eine Kante zwischen den übergebenen Knoten gibt. Ein möglicher Testfall sollte so verlaufen: >>> existiertKante(testgraph, 'A', 'D') False Aufgabe: (c) (offen) Entwickle weitere Funktionen zur Verwaltung und Verarbeitung von Graphen, z.B.: addKnoten(...) delKnoten(...) addKante(...) ... # fügt einen neuen Knoten hinzu # löscht einen Knoten # fügt eine neue Kante hinzu 27 Graph als Objekt Bisher wurde gezeigt, dass man Graphen auf unterschiedliche Weise mit Hilfe elementarer Datenstrukturen repräsentieren kann. Für die Verarbeitung von Graphen ist die gewählte Darstellung durchaus wichtig. Noch wichtiger ist es aber, über geeignete Operationen zur Verwaltung und Veränderung von Graphen zu verfügen, die - unabhängig von der zu Grunde liegenden Darstellung - die erforderiche Verarbeitung der Daten durchführen. Günstig ist es, hier objektoerientiert vorzugehen und eine Klasse Graph zu konzipieren, die die benötigten Operationen bereitstellt. 28 Modellierung der Klasse Graph Ein Objekt der Klasse Graph soll die Knoten und Kanten eines Graphen verwalten. Zum einen soll es geeignete Operationen bereit stellen, um einen Graphen aufzubauen und zu verändern. Mit diesen Operationen soll man z.B. neue Knoten hinzufügen oder bereits vorhandene Knoten entfernen können. Zum anderen soll es Operationen bereit stellen, um an Informationen über den Graphen zu gelangen. So soll man mit einer geeigneten Operation ermitteln können, ob es den Knoten ... gibt oder ob es eine Kante von Knoten ... zu Knoten ... gibt. Aufgabe: Erstelle zunächst eine Liste mit den Operationen, die ein Objekt der Klasse Graph zur Verfügung stellen sollte. Ergänze dann das Klassendiagramm. Beschreibe die gewünschten Operationen möglichst genau. Implementierung der Klasse Graph 29 Aufgabe: Ergänze die Implementierung der Klasse Graph. Du kannst auch eine Nachbarschaftstabelle zur Repräsentation des Graphen benutzen. Teste natürlich auch deine Implementierung der Klasse Graph. class Graph(object): def __init__(self): self.knotenMitNachbarn = [] def getAlleKnoten(self): knotenListe = [] for eintrag in self.knotenMitNachbarn: knotenListe = knotenListe + [eintrag[0]] return knotenListe def existiertKnoten(self, nameKnoten): if nameKnoten in self.getAlleKnoten(): return True else: return False # ... 30 Nutzung der Klasse Graph Das Testprogramm unten zeigt, wie man eine (gegebene) Implementierung der Klasse Graph nutzen kann. from graph import * # Erzeugung des Graph-Objekts g = Graph() # Hinzufügen von Knoten und Kanten g.addKnoten('A') g.addKnoten('B') g.addKante('A', 'B') # Ausgabe der Nachbarschaftslisten for knoten in g.getAlleKnoten(): print(knoten, ':', g.getAlleNachbarn(knoten)) Aufgabe: Nutze die Implementierung der Klasse Graph, um den Graphen in der Abbildung zu erstellen. Nimm anschließend folgende Veränderungen am Graphen vor und kontrolliere die Ergebnisse: (1) Kante von B nach C löschen (2) Kante von C nach B löschen (3) Kante von C nach C hinzufügen (4) Knoten B löschen (5) Knoten C löschen 31 Erweiterung der Klasse Graph Erweiterung: Verwaltung zusätzlicher Daten Zunächst einmal sind hier alle Kanten mit Gewichten versehen. Die Zahlen an den Kanten repräsentieren die Entfernung zwischen den Autobahnknotenpunkten. An einer der Kanten ist zusätzlich die Autobahnbezeichnung eingetragen. Auch diese Information ist für praktische Anwendungen oft wichtig. Interessant könnten auch die GeoKoordinaten von Autobahnknotenpunkten sein. Diese würde man benötigen, wenn eine Darstellung auf einer Landkarte angestrebt würde. An einem der Knoten sind solche zusätzlichen GeoInformationen eingetragen. 32 Erweiterung der Klasse Graph Erweiterung: Speicherung der Daten in einer externen Datei Die Daten von Graphen werden während der Verarbeitung von einem Objekt der Klasse Graph verwaltet. Nach der Verarbeitung (wenn das Programm beendet ist) wird das Objekt vernichtet. Danach sind sämtliche Daten des verwalteten Graphen nicht mehr zugänglich. Wünschenswert ist eine Möglichkeit, die Daten eines Graphen in einer externen Datei abspeichern zu können und Daten einer externen Datei wieder einlesen zu können. Als externes Speicherformat benutzen wir im Folgenden GraphML, ein XMLbasiertes Standardformat zur Repräsentation von Graphen. <?xml version="1.0" encoding="iso-8859-1"?> <graph id="bundesautobahnen Lu Ma"> <!--Knoten--> <node id="Kreuz Frankenthal"> <data key="lat">49.5534891</data> <data key="lon">8.3199207</data> </node> ... <!--Kanten--> <edge source="Kreuz Frankenthal" target="Viernheimer Dreieck"> <data key="A">A 6</data> <data key="entfernung">19</data> </edge> ... </graph> 33 Erweiterung der Klasse Graph Die Klasse Graph wird um geeignete Operationen erweitert. 34 Erweiterung der Klasse Graph Aufgabe Teste die erweiterte Klasse mit den Daten zum Autobahnnetz (siehe inf-schule). Aufgabe Teste die Klasse GraphMitDaten. Erzeuge ein Objekt, mit dem der folgende gewichtete Graph verwaltet wird. Die Daten des Graphen sollen auch in einer externen Datei gespeichert werden. 35 Teil 3 Kürzeste Wege in Graphen 36 Viele Wege führen vom Kreuz Frankenthal zum Kreuz Heidelberg. Aber welcher dieser Wege ist der kürzeste? allgemein: Gegeben ist ein (gerichteter oder ungerichtetet, gewichteter) Graph. Gegeben ist zusätzlich ein Startund ein Zielknoten. Welcher Weg des Graphen vom Startknoten zum Zielknoten ist der kürzeste. Dabei soll die Länge des Weges durch die Summe der Gewichte der Kanten längs des Weges erfasst werden. Das Problem 37 Ein vereinfachtes Problem Viele Wege führen vom Kreuz Frankenthal zum Kreuz Heidelberg. Aber welcher dieser Wege benötigt die wenigsten "Hops"? allgemein: Gegeben ist ein (gerichteter oder ungerichtetet) Graph. Gegeben ist zusätzlich ein Start- und ein Zielknoten. Welcher Weg des Graphen vom Startknoten zum Zielknoten führt über die geringste Anzahl an Zwischenknoten? 38 Lösung des vereinfachten Problems Im Alltag kommt es gelegentlich vor, dass jemand sagt: "Den kenne ich um mehrere Ecken". Das heißt dann wohl, dass derjenige jemanden kennt, der wiederum jemanden kennt, der ... Adriana kennt Clara, Erik, Greta Ziel ist es, den "Bekanntheitsabstand" von Adriana zu Betül, Clara, Dominik, ..., Hannah zu bestimmen. Dabei soll direkte Bekanntschaft durch den Bekanntheitsabstand 1 beschrieben werden. Erik kennt Dominik, Greta Aufgabe: Versuche (ohne den Bekanntschaftsgraphen zu zeichnen), die gesuchten Bekannheitsabstände systematisch zu ermitteln. Beschreibe dein Vorgehen. Betül kennt Clara, Dominik Clara kennt Adriane, Dominik, Erik Dominik kennt Betül, Erik Fabian kennt Hannah Greta kennt Dominik, Hannah Hannah kennt Fabian 39 Lösungsverfahren - Abstand 40 Ein Lösungsverfahren # Vorbereitung für alle Knoten knoten des Graphen: knoten.abstand = 'u' startKnoten.abstand = 0 füge startKnoten in eine Liste zuVerarbeiten ein (hier grün) 41 Ein Lösungsverfahren # Verarbeitung der Knoten SOLANGE die Liste zuVerarbeiten nicht leer ist: bestimme ausgewaehlterKnoten aus zuVerarbeiten entferne ausgewaehlterKnoten aus zuVerarbeiten (hier blau) für alle Nachbarn nachbarKnoten von ausgewaehlterKnoten: wenn nachbarKnoten.abstand == 'u': füge nachbarKnoten in die Liste zuVerarbeiten ein nachbarKnoten.abstand = ausgewaehlterKnoten.abstand + 1 42 Ein Lösungsverfahren # Verarbeitung der Knoten SOLANGE die Liste zuVerarbeiten nicht leer ist: bestimme ausgewaehlterKnoten aus zuVerarbeiten entferne ausgewaehlterKnoten aus zuVerarbeiten (hier blau) für alle Nachbarn nachbarKnoten von ausgewaehlterKnoten: wenn nachbarKnoten.abstand == 'u': füge nachbarKnoten in die Liste zuVerarbeiten ein nachbarKnoten.abstand = ausgewaehlterKnoten.abstand + 1 43 Ein Lösungsverfahren # Verarbeitung der Knoten SOLANGE die Liste zuVerarbeiten nicht leer ist: bestimme ausgewaehlterKnoten aus zuVerarbeiten entferne ausgewaehlterKnoten aus zuVerarbeiten (hier blau) für alle Nachbarn nachbarKnoten von ausgewaehlterKnoten: wenn nachbarKnoten.abstand == 'u': füge nachbarKnoten in die Liste zuVerarbeiten ein nachbarKnoten.abstand = ausgewaehlterKnoten.abstand + 1 44 Ein Lösungsverfahren # Verarbeitung der Knoten SOLANGE die Liste zuVerarbeiten nicht leer ist: bestimme ausgewaehlterKnoten aus zuVerarbeiten entferne ausgewaehlterKnoten aus zuVerarbeiten (hier blau) für alle Nachbarn nachbarKnoten von ausgewaehlterKnoten: wenn nachbarKnoten.abstand == 'u': füge nachbarKnoten in die Liste zuVerarbeiten ein nachbarKnoten.abstand = ausgewaehlterKnoten.abstand + 1 45 Lösungsverfahren - Abstand / Herkunft 46 Algorithmus von Moore ALGORITHMUS # von Moore Übergabedaten: Graph, startKnoten, zielKnoten # Vorbereitung des Graphen für alle knoten des Graphen: setze abstand auf 'u' setze herkunft auf None setze abstand von startKnoten.abstand auf 0 füge startKnoten in eine Liste zuVerarbeiten ein # Verarbeitung der Knoten SOLANGE die Liste zuVerarbeiten nicht leer ist: bestimme einen Knoten ausgewaehlterKnoten aus zuVerarbeiten (z.B. den ersten in der Liste) entferne ausgewaehlterKnoten aus zuVerarbeiten für alle nachbarKnoten von ausgewaehlterKnoten: wenn abstand von nachbarKnoten == 'u': füge nachbarKnoten in die Liste zuVerarbeiten ein (z.B. am Listenende) neuerAbstand = (abstand von ausgewahlterKnoten) + 1 setze abstand von nachbarKnoten auf neuerAbstand setze herkunft von nachbarKnoten auf ausgewaehlterKnoten weglaenge = abstand von zielKnoten # Erzeugung des Weges zum zielKnoten weg = [] knoten = zielKnoten SOLANGE knoten != startKnoten und herkunft von knoten != None: weg = [(herkunft von knoten, knoten)] + weg knoten = herkunft von knoten Rückgabedaten: (weg, weglaenge) 47 Exkurs "Graph durchlaufen" {Eingabe: Graph G; Startknoten s des Graphen} für alle Knoten w markiere w als nicht besucht füge s in eine (zunächst leere) Datenstruktur D ein solange D nicht leer ist entnimm einen Knoten w aus D für alle Kanten {w,u} falls u nicht markiert ist markiere u füge u in D ein {Ausgabe: Markierungen für alle Knoten w von G; ein Knoten ist genau dann als 'besucht' markiert, wenn er üben einen Weg mit s verbindbar ist} Quelle: http://www.matheprisma.uni-wuppertal.de/Module/Graphen/index.htm Beispiel 48 B 0 F D C 0 B D C E A S Vorbereitungsschritt markiere alle Knot. als nicht besucht (hier 0) markiere S als besucht (hier 1) füge s in e. Datenstruktur D (hier grün) ein 0 B F D C 1 0 0 0 0 A 0 0 1 entnimm einen Knoten w (hier blau) aus D für alle Kanten {w,u} falls u nicht markiert ist markiere u füge u in D ein Wiederholungsschritt 0 1 B F D C E S 0 E S 0 F 0 E A S 0 1 1 A 0 0 Beispiel 49 0 1 B F D C 0 0 0 1 B D C E S 1 A S 0 1 B F D C 1 1 1 B 1 1 B D C A S 0 1 F D C S 0 1 1 0 1 1 A 1 1 Auswahlstrategie Last In First Out -> Tiefensuche 1 0 1 B F D C E A F E 1 0 0 E S 1 entnimm einen Knoten w (hier blau) aus D für alle Kanten {w,u} falls u nicht markiert ist markiere u füge u in D ein Auswahlstrategie First In First Out -> Breitensuche 1 0 0 E 1 0 F 1 E A S 1 1 1 A 1 0 Beispiel 50 1 1 B F D C 1 0 1 1 B D C E S F 0 1 E 1 1 A S 1 1 A 1 1 entnimm einen Knoten w (hier blau) aus D für alle Kanten {w,u} falls u nicht markiert ist markiere u füge u in D ein 1 1 B F D C 1 1 0 1 1 1 F D C E S B Ergebnis: Von S aus gibt es Wege zu den folgenden Knoten: A, B, C, D, E. 1 E A S 1 1 1 A 1 0 51 Das verallgemeinerte Problem Gegeben ist ein (gerichteter oder ungerichtetet, gewichteter) Graph. Gegeben ist zusätzlich ein Startknoten (z.B. A) und ein Zielknoten (z.B. C). Welcher Weg des Graphen vom Startknoten zum Zielknoten ist der kürzeste. Dabei soll die Länge des Weges durch die Summe der Gewichte der Kanten längs des Weges erfasst werden. 52 Lösungsverfahren Aufgabe: Benutze das Simulationsprogramm "Shortest Path Animation", um die Grundidee des Lösungsverfahrens von Dijkstra herauszufinden. 53 Lösungsverfahren - Abstand / Herkunft 54 Ein Lösungsverfahren # Verarbeitung der Knoten SOLANGE die Liste zuVerarbeiten nicht leer ist: bestimme e. Knoten minKnoten a. zuVerarbeiten mit min. Abstand entferne minKnoten aus zuVerarbeiten (hier blau) für alle Nachbarn nachbarKnoten von minKnoten: wenn nachbarKnoten.abstand == 'u': füge nachbarKnoten in zuVerarbeiten ein nachbarKnoten.abstand = minKnoten.abstand+gewicht((minknoten, nachbarKnoten)) nachbarKnoten.herkunft = minKnoten sonst: wenn nachbarKnoten in zuVerarbeiten liegt: wenn minKnoten.abstand+ gewicht((minknoten, nachbarKnoten)) < nachbarKnoten.abstand: aktualisiere nachbarKnoten.abstand aktualisiere nachbarKnoten.herkunft 55 Algorithmus von Dijkstra Übergabedaten: Graph, startKnoten, zielKnoten # Vorbereitung des Graphen für alle knoten des Graphen: setze abstand auf 'u' setze herkunft auf None setze abstand von startKnoten.abstand auf 0 füge startKnoten in eine Liste zuVerarbeiten ein # Verarbeitung der Knoten SOLANGE die Liste zuVerarbeiten nicht leer ist: bestimme einen Knoten minKnoten aus zuVerarbeiten mit minimalem Abstand entferne minKnoten aus zuVerarbeiten für alle nachbarKnoten von minKnoten: gewicht = Gewicht der Kante von minKnoten zu nachbarKnoten neuerAbstand = (abstand von minKnoten) + gewicht WENN abstand von nachbarKnoten == 'u': füge nachbarKnoten in die Liste zuVerarbeiten ein (z.B. am Listenende) setze abstand von nachbarKnoten auf neuerAbstand setze herkunft von nachbarKnoten auf minKnoten SONST: WENN nachbarKnoten in zuVerarbeiten liegt: WENN abstand von nachbarKnoten > neuerAbstand: setze abstand von nachbarKnoten auf neuerAbstand setze herkunft von nachbarKnoten auf minKnoten weglaenge = abstand von zielKnoten # Erzeugung des Weges zum zielKnoten weg = [] knoten = zielKnoten SOLANGE knoten != startKnoten und herkunft von knoten != None: weg = [(herkunft von knoten, knoten)] + weg knoten = herkunft von knoten # Rückgabedaten: (weg, weglaenge) 56 Implementierung des Moore-Alg. Aufgabe: Die folgende Implementierung dieses Algorithmus beruht auf einer Darstellung von Graphen mit Nachbarschaftlisten. Sie benutzt eine Reihe von Hilfsfunktionen (siehe inf-schule). Teste zunächst die Hilfsfunktionen. Was leisten sie jeweils? def existiertKnoten(graph, knoten): # ... def getAlleKnoten(graph): # ... def getAlleNachbarn(graph, knoten): # ... def getAbstand(erweiterterGraph, knoten): # ... def getHerkunft(erweiterterGraph, knoten): # ... def setAbstand(erweiterterGraph, knoten, wert): # ... def setHerkunft(erweiterterGraph, knoten, wert): # ... def initErweiterterGraph(graph, startKnoten): # ... 57 Implementierung des Moore-Alg. def kuerzesterWegMoore(graph, startKnoten, zielKnoten): if existiertKnoten(graph, startKnoten) and existiertKnoten(graph, zielKnoten): # Vorbereitung erweiterterGraph = initErweiterterGraph(graph, startKnoten) zuVerarbeiten = [startKnoten] # Verarbeitung der Knoten while zuVerarbeiten != []: # bestimme einen Knoten ausgewaehlterKnoten aus zuVerarbeiten ausgewaehlterKnoten = zuVerarbeiten[0] # entferne ausgewaehlterKnoten aus zuVerarbeiten zuVerarbeiten = zuVerarbeiten[1:] # bearbeite die Nachbarn von ausgewaehlterKnoten for nachbarKnoten in getAlleNachbarn(graph, ausgewaehlterKnoten): if getAbstand(erweiterterGraph, nachbarKnoten) == 'u': zuVerarbeiten = zuVerarbeiten + [nachbarKnoten] neuerAbstand = getAbstand(erweiterterGraph, ausgewaehlterKnoten)+ 1 erweiterterGraph = setAbstand(erweiterterGraph, nachbarKnoten, neuerAbstand) erweiterterGraph = setHerkunft(erweiterterGraph, nachbarKnoten, ausgewaehlterKnoten) weglaenge = getAbstand(erweiterterGraph, zielKnoten) # Erzeugung des Weges zum zielKnoten weg = [] knoten = zielKnoten while knoten != startKnoten and getHerkunft(erweiterterGraph, knoten) != None: weg = [(getHerkunft(erweiterterGraph, knoten), knoten)] + weg knoten = getHerkunft(erweiterterGraph, knoten) else: weg = [] weglaenge = 'u' return (weg, weglaenge) 58 Implementierung des Moore-Alg. # Erzeugung des Testgraphen testgraph_ohne_gewichte = \ [ ['A', ['C', 'E', 'G']], ['B', ['C', 'D']], ['C', ['A', 'D', 'E']], ['D', ['B', 'E']], ['E', ['D', 'G']], ['F', ['B', 'D', 'H']], ['G', ['D']], ['H', ['F', 'G']] ] # Test des Moore-Algorithmus start = 'A' ziel = 'B' print('kürzester Weg von', start, 'nach', ziel, ':') (weg, laenge) = kuerzesterWegMoore(testgraph_ohne_gewichte, start, ziel) for w in weg: print(w) print('Weglänge:', laenge) Aufgabe: Ergänze Ausgaben in der Funktion kuerzesterWegMoore so, dass die Verarbeitung des Graphen Schritt für Schritt (wie in den Abbildungen zum Algorithmus von Moore ) nachvollzogen werden kann. Führe auch weitere Tests der Funktion kuerzesterWegMoore aus. 59 Implementierung des Moore-Alg. Aufgabe: Eine weitere Implementierung des Algorithmus von Moore findest du in der Klasse GraphMoore (siehe inf-schule). Objekte der Klasse GraphMoore stellen die Operation kuerzesterWegMoore zur Verfügung, mit der man Wege mit geringster Anzahl von Zwischenknoten in Graphen bestimmen kann. Teste auch diese Implementierung. from graph_moore import * # Erzeugung des Graph-Objekts g = GraphMoore() # Erzeugung der Knoten und Kanten aus einer GraphML-Datei datei = open("graph_ohne_gewichte.xml", "r") xml_quelltext = datei.read() g.graphmlToGraph(xml_quelltext) # Kontrollausgabe des Graphen print('Graph:') for knoten in g.getAlleKnoten(): print(knoten, ':', g.getAlleNachbarn(knoten)) print() # Test des Moore-Algorithmus start = 'A' ziel = 'B' print('kürzester Weg von', start, 'nach', ziel, ':') (weg, laenge) = g.kuerzesterWegMoore(start, ziel) for w in weg: print(w) print('Weglänge:', laenge) 60 Implementierung des Dijkstra-Alg. Aufgabe: Mit den Hilfsfunktionen initErweiterterGraph, getAbstand, getHerkunft, setAbstand, setHerkunft, ... lässt sich auch der Algorithmus von Dijkstra implementieren. Du kannst dich an der Implementierung des Algorithmus von Moore orientieren. Zum Testen kannst du das folgende Testprogramm nutzen und variieren. # Erzeugung des Testgraphen testgraph_mit_gewichten = \ [ ['A', [('C', 20), ('E', 2), ('G', 9)]], ['B', [('C', 1), ('D', 8)]], ['C', [('A', 20), ('D', 10), ('E', 13)]], ['D', [('B', 8), ('E', 16)]], ['E', [('D', 16), ('G', 5)]], ['F', [('B', 3), ('D', 4), ('H', 5)]], ['G', [('D', 2)]], ['H', [('F', 5), ('G', 7)]] ] # Test des Dijkstra-Algorithmus start = 'A' ziel = 'B' print('kürzester Weg von', start, 'nach', ziel, ':') (weg, laenge) = kuerzesterWegDijkstra(testgraph_mit_gewichten, start, ziel) for w in weg: print(w) print('Weglänge:', laenge) 61 Implementierung des Dijkstra-Alg. Aufgabe: Eine weitere Implementierung des Algorithmus von Dijkstra findest du in der Klasse GraphDijkstra (siehe inf-schule). Objekte der Klasse GraphDijkstra stellen die Operation kuerzesterWegDijkstra zur Verfügung, mit der man kürzeste Wege in gewichteten Graphen bestimmen kann. Teste auch diese Implementierung. from graph_dijkstra import * # Erzeugung des Graph-Objekts g = GraphDijkstra() # Erzeugung der Knoten und Kanten aus einer GraphML-Datei datei = open("graph_mit_gewichten.xml", "r") xml_quelltext = datei.read() g.graphmlToGraph(xml_quelltext) # Kontrollausgabe des Graphen print('Graph:') for knoten in g.getAlleKnoten(): print(knoten, ':', g.getAlleNachbarn(knoten)) print() # Test des Dijkstra-Algorithmus start = 'A' ziel = 'B' print('kürzester Weg von', start, 'nach', ziel, ':') (weg, laenge) = g.kuerzesterWegDijkstra(start, ziel) for w in weg: print(w) print('Weglänge:', laenge) 62 Routenplanung mit dem Dijkstra-Alg. Aufgabe: Zunächst betrachten wir das vereinfachte Autobahnnetz rund um Ludwigshafen und Mannheim. Benutze die Implementierung des Algorithmus von Dijkstra, um den kürzesten Weg vom Kreuz Frankenthal zum Kreuz Heidelberg zu ermitteln. Die Daten zum Autobahnnetz findest du in der Datei graph_bab_lu_ma.xml (siehe inf-schule). 63 Routenplanung mit dem Dijkstra-Alg. Aufgabe: Benutze die Daten zum Autobahnnetz in Deutschland, um den kürzesten Weg vom 'KREUZ FRANKENTHAL' zum 'KREUZ HEIDELBERG' bzw. von 'TRIER - VERTEILERKREIS (A 602)' nach 'KREUZ MÜNCHEN WEST' zu bestimmen. Vergleiche die Ergebnisse auch mit denen, die ein professioneller Routenplaner liefert. Die Daten zum Autobahnnetz findest du in der Datei graph_bab.xml (siehe infschule). Quelle: http://de.wikipedia.org/wiki/Autobahn_%28Deutschland%29 Autor der Karte: M. Stadthaus 64 Routenplanung mit dem Dijkstra-Alg. Aufgabe: Teste auch die GUI-Version des Routenplanerprogramms (siehe infschule). Vergleiche auch die vom Simulationsprogramm gelieferten Daten auch mit den Ergebnissen realer Routenplaner. 65 Routenplanung mit dem Dijkstra-Alg. Aufgabe: Die Bestimmung kürzester Wege mit dem Algorithmus von Dijstra in der bisher gezeigten Form dauert bei einer großen Datebasis sehr lange. Woran liegt das? Entwickle Ideen, wie man den Suchaufwand verringern könnte. Versuche eventuell auch, diese Ideen umzusetzen. 66 Routenplanung mit dem Dijkstra-Alg. Aufgabe: Die Aufbereitung der Ergebnisse (Auflistung der Stationen der Route) kann ebenfalls noch verbessert werden. ('TRIER - VERTEILERKREIS (A 602)', 'TRIER - EHRANG (A 602)') ('A 602', 5.0) ('TRIER - EHRANG (A 602)', 'TRIER - RUWER (A 602)') ('A 602', 2.0) ('TRIER - RUWER (A 602)', 'SCHWEICH (A 602)') ('A 602', 2.0) ('SCHWEICH (A 602)', 'DREIECK MOSELTAL') ('A 602', 1.0) ('DREIECK MOSELTAL', 'MEHRING (A 1)') ('A 1', 8.0) ('MEHRING (A 1)', 'REINSFELD (A 1)') ('A 1', 14.0) ('REINSFELD (A 1)', 'HERMESKEIL (A 1)') ('A 1', 2.0) ('HERMESKEIL (A 1)', 'BIERFELD (A 1)') ('A 1', 7.0) ('BIERFELD (A 1)', 'DREIECK NONNWEILER') ('A 1', 2.0) ('DREIECK NONNWEILER', 'OTZENHAUSEN (A 62)') ('A 62', 0.3) ('OTZENHAUSEN (A 62)', 'NOHFELDEN - TÜRKISMÜHLE (A 62)') ('A 62', 8.0) ('NOHFELDEN - TÜRKISMÜHLE (A 62)', 'BIRKENFELD (A 62)') ('A 62', 6.0) ('BIRKENFELD (A 62)', 'FREISEN (A 62)') ('A 62', 9.0) ('FREISEN (A 62)', 'REICHWEILER (A 62)') ('A 62', 4.0) ('REICHWEILER (A 62)', 'KUSEL (A 62)') ('A 62', 7.0) ('KUSEL (A 62)', 'GLAN - MÜNCHWEILER (A 62)') ('A 62', 9.0) ('GLAN - MÜNCHWEILER (A 62)', 'HÜTSCHENHAUSEN (A 62)') ('A 62', 6.0) ('HÜTSCHENHAUSEN (A 62)', 'KREUZ LANDSTUHL - WEST') ('A 62', 3.0) ... 67 Teil 4 Rundreisen in Graphen 68 Wenn ein neuer Außenminister im Amt ist, dann versucht er (oder sie), möglichst rasch Antrittsbesuche bei den wichtigsten Bündnispartnern zu machen. Zu diesen Partner gehören natürlich auch die Länder der Europäischen Union (EU). Wir werden hier folgende Situation durchspielen: Nach einer Bundestagswahl soll der neue Außenminister alle Hauptstädte der EU besuchen. Die Reihenfolge soll sich nicht nach der Bedeutung der Länder oder anderen Kriterien richten. Sie soll so gewählt werden, dass der gesamte Reiseweg für den Minister möglichst gering wird. Das Problem Das Problem 69 Als Willy Brand 1966 Außenminister wurde, bestand die EU (damals EWG) aus nur 6 Ländern: Deutschland, Frankreich, Italien, Niederlande, Belgien und Luxemburg. Die Tabelle zeigt die Entfernungen der jeweiligen Hauptstädte. Bonn Bonn 0 Paris 401 Rom 1066 Den Ha. 245 Brüssel 195 Luxemb. 145 Paris 401 0 1108 383 262 287 Rom 1066 1108 0 1289 1174 989 Den Ha. 244 383 1289 0 137 302 Brüssel 195 262 1174 137 0 187 Luxemb. 145 287 989 302 187 0 Aufgabe: Versuche, eine möglichst kurze Rundreise mit Start- und Zielort Bonn zu finden. Bestimme auch die Gesamtlänge der Rundreise. 70 Das Problem Die EU (bzw. EWG bzw. EG) ist seit ihrer Gründung 1957 mehrfach erweitert worden. Zu Beginn bestand sie aus 6 Mitgliedsstaaten. Diese 6-er-Gemeinschaft ist 1973 zu einer 9-erGemeinschaft, 1981 bis 1986 zu einer 12-er-Gemeinschaft, 1995 zu einer 15-er-Gemeinschaft, 2004 zu einer 25-er-Gemeinschaft und 2007 zu einer 27-er-Gemeinschaft gewachsen. Weitere Staaten warten auf den baldigen Beitritt zu dieser Gemeinschaft. Aufgabe: Welche Schwierigkeiten sind bei der Bestimmung der kürzesten Rundreise zu erwarten? 71 Ein einfacher Lösungsalgorithmus Idee: Erzeuge alle möglichen Rundreisen und bestimme jeweils die Länge der Rundreise. Ermittle aus den erzeugten Längenwerten die optimale Route. Aufgabe: Teste das beschriebene Verfahren mit dem Simulationsprogramm (siehe inf-schule) und bestimme den kürzesten Rundweg bei den vorgegebenen Hauptstädten. 72 Ein einfacher Lösungsalgorithmus Idee: Erzeuge alle möglichen Rundreisen und bestimme jeweils die Länge der Rundreise. Ermittle aus den erzeugten Längenwerten die optimale Route. ALGORITHMUS Rundreise startRouteOhneStartKnoten = Liste aller Knotennamen ohne den startKnoten startRoute = [startKnoten] + startRouteOhneStartKnoten + [startKnoten] routeOhneStartKnoten = Kopie von startRouteOhneStartKnoten route = startRoute minLaenge = Länge von route minRoute = route endePermutationen = False while not endePermutationen: routeOhneStartKnoten = naechste_permutation(routeOhneStartKnoten) route = [startKnoten] + routeOhneStartKnoten + [startKnoten] if self.laenge(route) < minLaenge: minLaenge = self.laenge(route) minRoute = route endePermutationen = (routeOhneStartKnoten == startRouteOhneStartKnoten) 73 Erzeugung von Permutationen def naechste_permutation(L): # best. max. Index i mit L[i] < L[i+1] i = len(L)-2 gefunden = False while not gefunden: if i < 0: gefunden = True else: if L[i] < L[i+1]: gefunden = True else: i = i-1 if i >= 0: # best. max. Index j mit L[j] > L[i] j = i+1 m = j while j < len(L)-1: j = j+1 if L[j] > L[i]: m = j j = m ... ... # vertausche L[i] und L[j] h = L[i] L[i] = L[j] L[j] = h # kehre Restliste ab Position i+1 um i = i+1 j = len(L)-1 while i < j: h = L[i] L[i] = L[j] L[j] = h i = i+1 j = j-1 else: # Liste ist absteigend sortiert # kehre Liste um i = 0 j = len(L)-1 while i < j: h = L[i] L[i] = L[j] L[j] = h i = i+1 j = j-1 return L 74 Erzeugung von Permutationen Aufgabe: Teste die Funktion naechste_permutation durch Funktionsaufrufe wie die folgenden. Versuche auch zu verstehen, wie die jeweils nächste Permutation gebildet wird. >>> naechste_permutation(['A', 'B', 'C', 'D', 'E']) ['A', 'B', 'C', 'E', 'D'] >>> naechste_permutation(['A', 'B', 'C', 'E', 'D']) ['A', 'B', 'D', 'C', 'E'] Aufgabe: Entwickle ein Testprogramm, mit dem man ermitteln kann, wie viele verschiedene Permutationen bei einer Liste mit 5 (bzw. 6, ...) verschiedenen Elementen möglich sind. 75 Suche nach der kürzesten Rundreisen from graph import * def naechste_permutation(L): # siehe oben def laenge(g, rundreise): weglaenge = 0 anzahlKnoten = len(rundreise) i = 1 while i < anzahlKnoten: weglaenge = weglaenge + float(g.getGewichtKante(rundreise[i-1], rundreise[i])) i = i+1 return weglaenge def minRundreise(g, startKnoten): # siehe nächste Seite # Erzeugung des Graph-Objekts g = GraphMitDaten() # Erzeugung der Knoten und Kanten aus einer GraphML-Datei datei = open("graph_eu_6.xml", "r") xml_quelltext = datei.read() g.graphmlToGraph(xml_quelltext) # Test des Rundreise-Algorithmus (minRoute, minLaenge) = minRundreise(g, 'Bonn') print('Route:', minRoute) print('Länge:', minLaenge) 76 Suche nach der kürzesten Rundreisen def minRundreise(g, startKnoten): startRouteOhneStartKnoten = [] for knoten in g.getAlleKnoten(): if knoten != startKnoten: startRouteOhneStartKnoten = startRouteOhneStartKnoten + [knoten] startRoute = [startKnoten] + startRouteOhneStartKnoten + [startKnoten] routeOhneStartKnoten = startRouteOhneStartKnoten[:] route = startRoute minLaenge = laenge(g, route) minRoute = route endePermutationen = False while not endePermutationen: routeOhneStartKnoten = naechste_permutation(routeOhneStartKnoten) route = [startKnoten] + routeOhneStartKnoten + [startKnoten] if laenge(g, route) < minLaenge: minLaenge = laenge(g, route) minRoute = route endePermutationen = (routeOhneStartKnoten == startRouteOhneStartKnoten) return (minRoute, minLaenge) Aufgabe: Führe das Testprogramm aus. Ändere es auch wie folgt ab: Es sollen alle erzeugten Routen mit ihren Längen ausgegeben werden. Es soll zusätzlich mitgezählt werden, wie viele Routen erzeugt werden (zur Kontrolle: 120). Bevor du anfängst, bei 27 (bzw. 25) Hauptstädten alle Rundreisen zu erzeugen, berechne erst einmal, wie viele das sind. 77 Aufwandsanalyse Die Problemgröße wird durch die Anzahl n der Mitgliedsländer der EU (Knoten des Graphen) festgelegt. Je größer n ist, desto größer ist auch der Aufwand zur Berechnung der Problemlösung. Als Kostenmaß für den Berechnungsaufwand wählen wir die Anzahl der zu erzeugenden und verarbeitenden Rundreisen R(n). Wir gehen dabei davon aus, dass jede zu erzeugende und verarbeitende Rundreise in etwa den gleichen Berechnungsaufwand hat. Für R(n) kann man einen recht einfachen Berechnungsterm entwickeln: Bei n Knoten gibt es vom Startknoten aus n-1 Möglichkeiten, den nächsten Knoten zu wählen. Von jedem dieser n-1 Knoten gibt es dann noch n-2 Möglichkeiten, den nächsten Knoten zu wählen, usw.. Insgesamt erhält man die folgende Formel: R(n) = (n-1)*(n-2)*...*2*1 = (n-1)! 0 ! = 1 1 ! = 1 2 ! = 2 3 ! = 6 4 ! = 24 5 ! = 120 6 ! = 720 7 ! = 5040 8 ! = 40320 9 ! = 362880 10 ! = 3628800 11 ! = 39916800 12 ! = 479001600 13 ! = 6227020800 14 ! = 87178291200 15 ! = 1307674368000 16 ! = 20922789888000 17 ! = 355687428096000 18 ! = 6402373705728000 19 ! = 121645100408832000 20 ! = 2432902008176640000 21 ! = 51090942171709440000 22 ! = 1124000727777607680000 23 ! = 25852016738884976640000 24 ! = 620448401733239439360000 25 ! = 15511210043330985984000000 78 Anwendbarkeit des Algorithmus Aufgabe: Man kann recht einfach ermitteln, wie lange ein Rechner zur Erzeugung und Verarbeitung einer Rundreise benötigt: Ergänze hierzu einen Zähler und eine Ausgabe des Zählers nach z.B. 1000 oder 10000 oder 100000 Zählschritten. Aus der grob gemessenen Zeit kann man dann die gesuchte Zeit abschätzen. Wir gehen im folgenden davon aus, dass man für 1000 Rundreisen etwa 1 Sekunde benötigt. Schätze mit den oben gezeigten Daten ab, wie lange es dauert, bis die kürzeste Rundreise mit dem vorliegenden Algorithmus bei 25 Knoten gefunden ist. Beurteile mit dem Ergebnis die praktische Anwendbarkeit des Algorithmus. 0 ! = 1 1 ! = 1 2 ! = 2 3 ! = 6 4 ! = 24 5 ! = 120 6 ! = 720 7 ! = 5040 8 ! = 40320 9 ! = 362880 10 ! = 3628800 11 ! = 39916800 12 ! = 479001600 13 ! = 6227020800 14 ! = 87178291200 15 ! = 1307674368000 16 ! = 20922789888000 17 ! = 355687428096000 18 ! = 6402373705728000 19 ! = 121645100408832000 20 ! = 2432902008176640000 21 ! = 51090942171709440000 22 ! = 1124000727777607680000 23 ! = 25852016738884976640000 24 ! = 620448401733239439360000 25 ! = 15511210043330985984000000 79 Aufgabe: Überlege dir eine Strategie, wie man "mit Köpfchen" statt der "Holzhammermethode" bei der Suche nach der kürzesten Rundreise vorgehen könnte. Näherungsverfahren 80 Stategie: zum am nächst lieg. Knoten Aufgabe: Eine naheliegende Strategie besteht darin, vom jeweils aktuellen Knoten zum am nächst liegenden Knoten zu gehen. Die Abbildung zeigt die Route, die nach der Strategie "zum am nächst liegenden Knoten" berechnet wurde. Versuche, die Route / Strategie zu bewerten: Gibt es kürzere Routen? Welche Nachteile hat die Strategie? 81 Stategie: Integration entfernter Knoten In einem ersten Schritt wird die Stadt gesucht, die am weitesten von Berlin entfernt ist. Im vorliegenden Fall ist es die Hauptstadt Nikosia von Zypern. Das Rundreisegerüst besteht jetzt aus Berlin - Nikosia - Berlin. 82 Stategie: Integration entfernter Knoten In einem nächsten Schritt wird die Stadt gesucht, die zu den Knoten des aktuellen Rundreisegerüsts den größten Abstand hat. Im vorliegenden Fall ist es die Hauptstadt Lissabon von Portugal. Diese Stadt wird jetzt so in das Rundreisengerüst eingebaut, dass das neue Rundreisegerüst eine möglichst geringe Länge hat. Es ergibt sich Berlin - Nikosia Lissabon - Berlin. 83 Stategie: Integration entfernter Knoten Es wird die Stadt gesucht, die zu den Knoten des aktuellen Rundreisegerüsts den größten Abstand hat. Im vorliegenden Fall ist es Dublin, die die Hauptstadt von Irland. Diese Stadt wird jetzt so in das Rundreisengerüst eingebaut, dass das neue Rundreisegerüst eine möglichst geringe Länge hat. Es ergibt sich Berlin - Nikosia - Valetta - Lissabon - Dublin Berlin. 84 Stategie: Integration entfernter Knoten Wenn man auf diese Weise weiterverfährt, ergibt sich insgesamt die folgende Rundreise. Aufgabe: Vergleiche das Ergebnis der Stategie "Integration der am weitesten entfernten Knoten" mit dem Ergebnis der Strategie "zum am nächst liegenden Knoten". Kann man mit Sicherheit behaupten, dass die Strategie "Integration der am weitesten entfernten Knoten" die kürzeste Rundreise liefert? 85 Näherungslösungen statt exakter Lös. Bei 27 Hauptstädten ist es unmöglich, die exakte Lösung nach der Holzhammer-Methode in der zur Verfügung stehenden Zeit zu bestimmen. In vertretbarem Aufwand (mehrere Stunden) geht das noch bei 12 Hauptstädten. Hier ist dann ein Vergleich der exakten Lösung mit Näherungslösungen möglich. Methode: zum am nächst lieg. Knoten Methode: Integration entfernter Knoten Methode: Erzeugung aller Rundreisen Länge der Rundreise: 10444.6 Länge der Rundreise: 9012.6 Länge der Rundreise: 8945.1 86 Literaturhinweise U. Schöning: Ideen der Informatik. Oldenbourg Verlag 2002. P. Gritzmann, R. Brandenburg: Das Geheimnis des kürzesten Weges. Springer 2002. Mathe-Prisma: Graphen http://www.matheprisma.uni-wuppertal.de/Module/Graphen/index.htm D. Jonietz: Graphen http://informatik.bildung-rp.de/fileadmin/user_upload/informatik.bildungrp.de/Weiterbildung/pdf/WB-VIII-6-Graphen.pdf ... Die Darstellung hier orientiert sich an den Materialien auf den Webseiten: http://www.inf-schule.de