Rheinische Friedrich-Wilhelms-Universität Bonn Institut für Informatik III Professor Dr. Rainer Manthey Professor Dr. Lutz Plümer Eine Variante des Verfahrens von Floyd zur Bestimmung kürzester Wege in einem hierarchischen Straßennetz Diplomarbeit von Lasse Asbach Bonn, im Juli 2002 i ii Inhalt Inhalt......................................................................................................................................i Abbildungsverzeichnis ........................................................................................................v Tabellenverzeichnis ............................................................................................................vi Kurzfassung/Abstract ...................................................................................................... vii Vorwort............................................................................................................................. viii 1 Einleitung........................................................................................................................1 2 Kürzeste Wege in der klassischen Graphentheorie ....................................................4 2.1 Einleitung und grundlegende Definitionen ..............................................................4 2.2 Algorithmen zur Berechnung kürzester Wege .........................................................8 2.2.1Übersicht ................................................................................................................................... 8 2.2.2Im Detail: Algorithmus von Dijkstra....................................................................................... 10 2.2.3Im Detail: Algorithmus von Floyd .......................................................................................... 20 3 Kürzeste Wege in Straßennetzen................................................................................27 3.1 Einleitung und Definitionen ...................................................................................27 3.2 Modellierung eines realen Straßennetzes...............................................................28 3.2.1Granularität.............................................................................................................................. 28 3.2.2Redundanz ............................................................................................................................... 30 3.2.3Korrektheit der Modellierung.................................................................................................. 31 3.2.4Kantenkosten ........................................................................................................................... 32 3.3 Kürzeste Wege .......................................................................................................32 4 Variante der Floyd-Wege-Matrix für Straßennetze.................................................34 4.1 Die Floyd-Wege-Matrix genauer betrachtet ..........................................................34 4.2 Variante der Floyd-Wege-Matrix für Straßennetze ...............................................38 4.3 Algorithmus zur Transformierung der Floyd-Wege-Matrix ..................................41 4.4 Beispielanwendung ................................................................................................45 iii 5 Anwendung der Variante der Floyd-Wege-Matrix in einem hierarchischen Strassennetz ..................................................................................................................51 5.1 Grundmodell...........................................................................................................51 5.1.1Idee 52 5.1.2Straßensubnetze....................................................................................................................... 53 5.1.3Algorithmus zur Ermittlung kürzester Wege........................................................................... 55 5.2 Herleitung der Straßensubnetze .............................................................................58 5.2.1Optimierungskriterien.............................................................................................................. 59 5.2.2Heuristik: Landkreise und Autobahnunternetz als Grundlage für die Subnetze ..................... 62 5.2.3Güte der Heuristik ................................................................................................................... 74 5.3 Verwendung der Variante der Floyd-Wege-Matrix ...............................................81 5.3.1Datenstrukturen ....................................................................................................................... 81 5.3.2Performanz und Komplexität................................................................................................... 83 5.3.3Mögliche praktische Anwendungen ........................................................................................ 87 6 Implementation ............................................................................................................89 6.1 Bedienung und Funktionalität des Straßennetz-Editors.........................................89 6.2 Algorithmen ...........................................................................................................91 7 Zusammenfassung........................................................................................................93 Literatur .............................................................................................................................96 Erklärung ...........................................................................................................................97 iv Abbildungsverzeichnis Abb. 1 Abb. 2 Abb. 3 Abb. 4 Abb. 5 Abb. 6 Abb. 7 Abb. 8 Abb. 9 Abb. 10 Abb. 11 Abb. 12 Abb. 13 Abb. 14 Abb. 15 Abb. 16 Abb. 17 Abb. 18 Abb. 19 Abb. 20 Abb. 21 Abb. 22 Abb. 23 Abb. 24 Abb. 25 Abb. 26 Beispiel-Graph 1 7 Beispiel-Graph 2 12 Anwendung von Dijkstra auf Beispiel-Graph 2 12 Kreuzung mit Modellierung in niedriger und hoher Granularität 29 Redundanz. 31 Matrix-Wegbaum für Matrix aus Tab. 2 37 Matrix-Wegbaum für Matrix aus Tab. 3 37 Matrix-Wegbaum für Matrix aus Tab. 3 38 Ein Beispiel-Weg zur Veranschaulichung der Straßentypwechselpunkte 39 Ein möglicher Matrix-Wegbaum für den Pfad aus Abb. 9 40 Ein weiterer möglicher Matrix-Wegbaum für den Pfad aus Abb. 9 40 Beispiel-Straßennetz 43 Straßenkarten-Ausschnitt in der Nähe von Mayen 47 Beispiel Fall 1 a, Fall 1 b 62 Beispiel Fall 2 63 Beispiel Fall 3 64 Beispiel Fall 4 65 Beispiel Fall 5 66 Beispiel Fall 6 67 Beispiel für Fall 2 nach AUTOBAHNNETZ_ERWEITERUNG 71 Abbildung für Beispiel 1 73 Abbildung für Beispiel 2 74 Abbildung für Beispiel 3 75 Abbildung für Beispiel 4 76 Abbildung für Beispiel 5 76 Screenshot des Straßennetz-Editors 85 v Tabellenverzeichnis Tab. Tab. Tab. Tab. Tab. Tab. 1 2 3 4 5 6 Anwendung von Floyd auf Beispiel-Graph 2 Floyd-Wege-Matrix für einen Pfad von 0 nach 7 Eine Variante der Floyd-Wege-Matrix für einen Pfad von 0 nach 7 Eine weitere Variante der Floyd-Wege-Matrix für einen Pfad von 0 ... Anwendung des Algorithmus FLOYD-WEGE-MATRIX-OPTIMIERUNG... Gegenüberstellung asymptotischer Eigenschaften von kürzeste-Weg-... vi 21 35 35 36 43 83 Kurzfassung/Abstract In dieser Diplomarbeit ist ein hierarchisches Verfahrens entwickelt worden zur Bestimmung exakter kürzester Wege in einem Straßennetz – eine bestimmte Art von gerichteten, gewichteten Graphen. Dazu verwenden wir unter anderem eine in dieser Diplomarbeit hergeleitete Variante der Wege-Matrix des Floyd-Algorithmus für Straßennetze, welche auch allein weitere sinnvolle Anwendungen hat. Diese Matrix ermöglicht die Beantwortung bestimmter Anfragen bzgl. eines kürzesten Weges P in einem Straßennetz in o(|P|) Zeit bei Θ(n2) Speicherplatzbedarf. Das, in Bezug auf Speicherplatzverbrauch und Laufzeit heuristische, hierarchische Verfahren generiert im Best-Case Datenstrukturen mit Speicherplatzbedarf von Θ(|V[H]|2), wobei |V[H]| der Komplexität des Autobahn(unter)-netzes entspricht. Diese Datenstrukturen können zur Beantwortung von Anfragen nach kürzesten Wegen benutzt werden bei einer Laufzeit von Θ(|P|). Es gibt gute Argumente dafür, dass der Best-Case bei Modellierungen von Straßennetzen aus der realen Welt eintritt. Spezielle, zu der Variante der Floyd-WegeMatrix passende Anfragen, können mit den Datenstrukturen sogar in o(|P|) Zeit beantwortet werden. Schlagwörter: Hierarchisch, Straßennetz, kürzeste, exakte, Weg, Floyd, Matrix, Heuristik In this thesis a certain hierarchical method has been developed which can answer shortestpath-queries in a road network – a certain type of directed, weighted graph. For that purpose we present a new variant of the shortest-path-matrix of Floyd’s algorithm, which has also applications stand-alone. This matrix is capable of anwering certain queries concerning a shortest path P in a road network in o(|P|) time and Θ(n2) space. The hierarchical method, which is a heuristic in terms of running time and space, generates in the best-case datastructures of Θ(|V[H]|2) space with |V[H]| being the complexity of the motorway network of the original road network. These datastructures can be used to answer shortest-path-queries in Θ(|P|) time. There are good arguments that road networks of the real world result in the best-case of the hierarchical method. The datastructures can also answer certain queries, that concern shortest-paths and fit to the variant of Floyd’s shortest-path-matrix, in time o(|P|). Keywords: hierarchical, road, network, shortest, exact, path, floyd, matrix, heuristic vii Vorwort Das Vorwort möchte ich nutzen, um allen, die mir bei der Diplomarbeit und dem bisherigen Studium geholfen haben, zu danken. Insbesondere möchte ich Christian Vogt und Jörg Wagner für ihr Korrekturlesen danken. Außerdem möchte ich den Professoren Rainer Manthey und Lutz Plümer für die gute Betreuung danken. Nicht zuletzt möchte ich mich natürlich auch bei meinen Eltern, die mir das Studium ermöglicht haben, bedanken. viii 1 Einleitung Das Problem, von Punkt A möglichst schnell nach Punkt B zu kommen, ist wohl schon seit der Steinzeit eines der wichtigen und häufig auftretenden Probleme der Menschheit. In der Steinzeit wurde dies sehr intuitiv gelöst und wahrscheinlich noch kaum als „Problem“ aufgefasst: Man geht oder rennt möglichst entlang der Luftlinie von Punkt A nach Punkt B. Wenn das Ziel nicht zu sehen war, mussten die Sonne, die Sterne und die Erinnerung den Weg weisen. Mit der Erfindung von Karte und Kompass wurde dies, wenn man einmal damit umgehen konnte, einfacher. Es wurde dann sogar möglich, Meere zu überqueren, auf denen man nichts als Wasser sah. Mit der Zeit wurden immer mehr Transportmittel erfunden, mit denen man sich immer schneller bewegen konnte. Viele dieser Transportmittel benötigten einen ebenen Boden und es wurden Straßen und später Schienen gebaut. Da diese Transportmittel, die sich auf Straßen und Schienen bewegten, viel schneller als jene wurden, die sich über fast jedes Gelände bewegen konnten, lohnte es sich bald nicht mehr, das inzwischen gut ausgebaute Straßen- und Schienennetz auf dem Weg von Punkt A nach Punkt B zu verlassen. Durch die zunehmende Erweiterung und Verdichtung der Straßennetze, wurden Karten für jedermann immer wichtiger. Die Beschriftung der Straßen mit Straßennamen macht die Orientierung und Wegsuche anhand einer Karte ungemein einfacher. Heutzutage ist ein gut ausgebautes Straßennetz ubiquitär, genauso wie gute Karten – zumindest in den Industrienationen. Die Preise für ein Notebook oder eine Uhr mit GPS sind für jedermann erschwinglich. Daher kommt der Routenplanung mit Hilfe des Computers eine immer größere Bedeutung zu. Offensichtlich haben Straßennetze viel Ähnlichkeit mit Graphen. Inzwischen ist das Problem, in einem gegebenen Graphen kürzeste Wege zwischen Knoten zu finden, eines der klassischen Probleme der Graphentheorie und wird bereits seit mehr als 40 Jahren intensiv studiert. Ein Grund liegt darin, dass dieses Problem in sehr vielen Anwendungen – entweder als direktes stand-alone Problem oder als Unterproblem eines komplexeren Problems – auftritt. Beispiele für Anwendungen, die mehr oder weniger nur aus diesem Problem bestehen, sind natürlich die Routenplanung, Transport-Probleme, ProjektManagement und DNA-Sequenzierung. Beispiele für Anwendungen, bei denen kürzesteWeg-Probleme nur als kleines Teilproblem auftreten, sind die Approximation von bestimmten Funktionen und das Knapsack Problem. (siehe [HASS00], S. 3). Obwohl im Gebiet der kürzeste-Weg-Probleme in der Graphentheorie schon lange geforscht wird, ist es noch nicht abgegrast. Zwar werden wahrscheinlich keine neuen Algorithmen gefunden werden können, deren worst-case-Laufzeiten bei der Ausgabe exakter kürzester Wege signifikant unter denen der schon gefundenen sind, aber es gibt Forschungsbedarf im Bereich der Heuristiken und spezieller Verfahren für bestimmte Arten von Graphen (wie z.B. Straßennetze). 1 Die Zielsetzung dieser Diplomarbeit ist die Entwicklung eines hierarchischen Verfahrens zur Bestimmung exakter kürzester Wege in einem Straßennetz – eine bestimmte, in dieser Diplomarbeit definierte, Art von gerichteten, gewichteten Graphen. Dazu verwenden wir unter anderem eine in dieser Diplomarbeit hergeleitete Variante der Wege-Matrix des Floyd-Algorithmus. Dieses Verfahren teilt das gesamte Straßennetz anhand von Verwaltungsstrukturen, wie z.B. Landkreisen, in einzelne, möglichst disjunkte Teile auf und ein möglichst das ganze Straßennetz überdeckendes, aber nicht besonders dichtes Netz von Straßen, auf denen man sich schnell bewegen kann – normalerweise Autobahnen. Mit Hilfe dieser Aufteilung ist bei modellierten realen Straßennetzen zu erwarten, dass sich kürzeste Wege zwischen Knoten ähnlich effizient wie bei der Verwendung einer vorberechneten Floyd-WegeMatrix bestimmen lassen, aber bei geringerem Speicherplatzbedarf. Der Teil des Verfahrens, der das Straßennetz aufteilt, ist heuristisch und beinflusst den Speicherplatzbedarf für die Datenstrukturen zur kürzesten-Weg-Bestimmung und die Laufzeit der Bestimmung kürzester Wege. Es werden aber immer exakte kürzeste Wege bestimmt. Die Diplomarbeit gliedert sich folgendermaßen: In Kapitel 2 führe ich grundlegende Definitionen der Graphentheorie bzgl. der Bestimmung kürzester Wege ein. Danach wird eine kleine Übersicht über Unterklassen der kürzeste-Weg-Probleme gegeben und über bekannte Algorithmen zur Lösung dieser. Im Anschluss daran betrachten wir den Algorithmus von Dijkstra und den Algorithmus von Floyd sehr detailliert. Der Abschnitt über den Algorithmus von Dijkstra ist nicht zum Verständnis der folgenden Kapitel nötig und kann bei der Lektüre weggelassen werden. Das Kapitel über den Algorithmus von Floyd sollte aber zumindest „überflogen“ werden, weil das Kapitel 4 und später auch das Kapitel 5 viel damit zu tun hat. Kapitel 3 definiert eine spezielle Art von Graph: das Straßennetz. Mit diesem beschäftigen sich die nachfolgenden Kapitel intensiv. Dieses Straßennetz ist natürlich nur ein theoretisches Gebilde, um reale Straßennetze zu modellieren. Was bei einer solchen Modellierung zu beachten ist, wird in Abschnitt 3.2 behandelt und ist eher leicht verdaulicher Stoff, den man aber bei den späteren Kapiteln im Hinterkopf haben sollte. Zuletzt gehen wir kurz auf die Unterschiede und auf die Gemeinsamkeiten von Graphen und Straßennetzen und die dadurch bedingten Konsequenzen für uns ein. Kapitel 4 ist neben Kapitel 5 ein Schwerpunkt dieser Diplomarbeit. Hier geht es um die Herleitung einer bestimmten Variante der Floyd-Wege-Matrix, welche interessante Eigenschaften vorweist. Nach der Beobachtung, dass die Floyd-Wege-Matrix auch zu einer etwas anderen Weise der Speicherung kürzester Wege fähig ist, als in Kapitel 2 vorgestellt, leiten wir eine für unsere Zwecke günstige Variante der Matrix her. Den endgültigen Existenzbeweis und Startschuss für die Anwendung dieser Variante liefert der Algorithmus aus Abschnitt 4.3, welcher eine gegebene Floyd-Wege-Matrix in eine mit 2 optimaler Struktur transformiert. In Abschnit 4.4 betrachten wir eine Anwendung für diese, welche wir im folgenden Kapitel auch noch einmal aufgreifen werden: In Kapitel 5 geht es um die Herleitung einer bestimmten hierarchischen Datenstruktur zur Speicherung eines Straßennetzes. Wir beginnen dazu mit der sehr intuitiven Idee für die Datenstruktur, die wir dann im nächsten Abschnitt formal auf den Punkt bringen. Im Abschnitt 5.1.3 entwickeln wir einen Algorithmus zur Bestimmung exakter kürzester Wege in dem hierarchischen Straßennetz. Abschnitt 5.2 beschäftigt sich mit der Frage, wie wir aus einem Straßennetz ein solches hierarchisches gewinnen können. Dazu überlegen wir uns in Abschnitt 5.2.1, welche Kriterien diese möglichst erfüllen sollten. Im darauf folgenden Abschnitt betrachten wir eine Heuristik, die aus einem Straßennetz ein hierarchisches generiert. In Abschnitt 5.2.3 gehen wir auf das worst-case-Verhalten der Heuristik ein. In Abschnitt 5.3 legen wir uns erstmals auf eine Datenstruktur zur Speicherung des hierarchischen Netzes fest: Die Variante der Floyd-Wege-Matrix. Dazu analysieren wir neben dem worst-case das erhoffte Verhalten der hergeleiteten Algorithmen mit dieser Datenstruktur für Straßennetze. In Kapitel 6 gehen wir kurz auf die in der Bearbeitungszeit dieser Diplomarbeit entstandene Java-Implementation der hergeleiteten Algorithmen und Datenstrukturen und eines graphischen Editors für Straßennetze ein. 3 2 Kürzeste Wege in der klassischen Graphentheorie In diesem Kapitel möchte ich die für die nachfolgenden Kapitel benötigten Grundlagen bzgl. kürzester Wege in Graphen wiederholen, so dass jene ohne weiteres Nachschlagen in sonstiger Literatur verständlich sein sollten. Dazu fangen wir in Abschnitt 2.1 mit grundlegenden Definitionen und zwei Lemmata der Graphentheorie in Hinblick auf kürzeste Wege und kürzeste-Weg-Probleme an. In Abschnitt 2.2.1 betrachten wir eine Klassifikation von kürzeste-Weg-Problemen bzgl. verschiedener Graphenklassen und geben eine Übersicht von Algorithmen zur Lösung dieser. Danach beschäftigen wir uns in den Abschnitten 2.2.2 und 2.2.3 mit zweien dieser Algorithmen und insbesondere mit den von ihnen zurückgegebenen Datenstrukturen im Detail: Dem Algorithmus von Dijkstra und dem von Floyd. Das Lesen des Kapitels über den Algorithmus von Dijkstra ist nicht zum Verständnis der späteren Kapitel notwendig1; das Kapitel über den Algorithmus von Floyd wahrscheinlich schon – abhängig vom individuellen Kenntnisstand des Lesers in dieser Thematik. Als Literatur diente mir in diesem Kapitel [LEDA99], [BLUM98], [BRAN94] und [KRUM00]. 2.1 Einleitung und grundlegende Definitionen Bevor wir jetzt auf die Algorithmen zur Lösung von kürzeste-Weg-Probleme zu sprechen kommen, werden wir in diesem Abschnitt zuerst Grundlegendes definieren (die meisten der folgenden Definitionen sind aus [LEDA99]): Sei G = (V, E, w) ein (endlicher, schlichter,)2 gewichteter und gerichteter Graph mit E ⊆ (V × V ) \ {(v, v) | v ∈ V } (G enthält also keine Schlingen) und mit Kostenfunktion w : E → ℜ der Kanten auf die reellen Zahlen. Es gelte |V| = n < ∞ und |E| = m < ∞. Die Kosten oder das Gewicht einer Kante werde ich in Zukunft synonym auch als Länge bezeichnen. Aus ästhetischen Gründen und weil es allgemein so üblich ist, schreiben wir w(u, v) für Kanten e = (u, v) anstatt w((u, v)) wie es eigentlich richtiger ist. 1 Anfangs, zu Beginn der schriftlichen Ausarbeitung der Diplomarbeit, war dies etwas anders gewesen und später wollte ich das Kapitel nicht einfach löschen. Der interessierte Leser ist herzlich zur Lektüre dieses Kapitels eingeladen. 2 Wenn ich im folgenden von gewichteten, gerichteten Graphen spreche, dann sind sie auch immer – wie es auch sonst fast immer so ist – endlich und schlicht, d.h. Knotenmenge und Kantenmenge sind endlich, enthalten keine Schlingen und keine parallelen Kanten (siehe [BRAN94], S. 12 zu einer allgemeineren Definition von Graphen, welche auch Schlingen und parallele Kanten zulässt). 4 Unter einem Weg oder einem Pfad P von v0 nach vk verstehen wir eine Folge von Knoten und Kanten [v0, e1, v1, e2, ..., vk-1, ek, vk] mit ei = (vi-1, vi) für i = 1,...,k. Anstatt dieser Notation werde ich auch die verkürzten (und ebenfalls eindeutigen) Notationen [v0, v1,..., vk-1, vk] oder [e1, e2, ..., ek-1, ek] verwenden. Wir sagen, Knoten w ist von Knoten v erreichbar, gdw. es einen Pfad von v nach w im betrachteten Graphen gibt. Ein solcher Pfad P heißt einfach, falls vi ≠ vj für 0 ≤ i < j ≤ k. Die Kardinalität oder ungewichtete Länge |P| eines Pfades P ist die Anzahl der Kanten von P. Wir erweitern die Kostenfunktion auf Pfade P folgendermaßen: w( P ) := ∑ w(e) . Für alle Knoten Kanten e ∈P v ∈ V definieren wir die Kosten des trivialen Pfades von v zu v – der Pfad, der keine Kanten enthält – als Null. Wie oben nennen wir die Kosten w(P) eines Pfades P auch Länge von P (welche i.A. nicht mit der ungewichteten Länge übereinstimmt). Ein Kreis ist ein nicht-trivialer Pfad, dessen Startknoten auch sein Zielknoten ist. Ein negativer Kreis ist ein Kreis, welcher negative Länge hat. Für alle zwei Knoten v, w definieren wir mit δ (v, w) die minimale / kürzeste Weg- / Pfadlänge eines Pfades von v nach w folgendermaßen: ⎧inf({w( P) : P ist (endlicher ) Pfad von v nach w}), falls w von v erreichbar ⎩+ ∞, sonst δ (v, w) := ⎨ Da die Menge der Pfade von v nach w i.A. unendlich ist (weil die Pfade ja nicht notwendigerweise einfach sein müssen), ist nicht direkt klar, ob es immer einen (endlichen) Pfad gibt, dessen Länge δ (v, w) beträgt. Folgendes Lemma, basierend auf [LEDA99], S. 317, verschafft darüber Klarheit: Lemma 1: Sei G = (V, E, w). Für alle Knoten v, w ∈ V gilt:1 a) δ (v, w) = +∞ gdw. es keinen Pfad von v nach w im betrachteten Graphen gibt. b) δ (v, w) = −∞ gdw. es einen Pfad von v nach w gibt, welcher einen negativen Kreis enthält. c) − ∞ < δ (v, w) < +∞ gdw. w von v aus erreichbar ist und es keinen Pfad von v nach w gibt, welcher einen negativen Kreis enthält. δ (v, w) entspricht dann der (minimalen) Länge eines einfachen Pfades von v nach w. Beweis: Zu a) 1 In diesem Kapitel verwenden wir die in der Mathematik und Informatik üblichen Rechenregeln und Ordnung für ℜ ∪ {−∞, + ∞} : −∞ < x < +∞, − ∞ + x = −∞, + ∞ + x = +∞ für alle x ∈ ℜ 5 ⇒) Folgt direkt aus der Definition und der Tatsache, dass kein endlicher Pfad unendliche Länge haben kann. ⇐) Folgt direkt aus der Definition. Zu b) ⇒) Falls δ (v, w) = −∞ , dann ist w von v aus erreichbar. Da kein endlicher Pfad (negativ) unendliche Länge haben kann, kann es nur so sein, dass sich einfache, endliche Pfade durch Hinzufügen von Kreisen im Grenzwert ins (negativ) Unendliche verlängern lassen. Da es nur endlich viele Kreise im endlichen Graphen G gibt, muss zumindest einer dieser immer weiter (bis unendlich oft im Grenzwert) durchlaufen werden. Dessen Länge muss dann auch negativ sein, da sonst ja keine negativ unendliche Länge im Grenzwert auftauchen könnte. Natürlich muss dieser Kreis auch auf dem Weg von v nach w liegen. ⇐) Die Kosten eines solchen Pfades, der einen negativen Kreis enthält, kann man durch jeden weiteren Durchlauf dieses Kreises verkürzen und damit jede negative Kostenzahl unterschreiten. Also ist das Infinum über alle Pfade von v nach w gleich ∞. Zu c) ⇒) Da − ∞ < δ (v, w) < +∞ , folgt aus a) und b), dass es keinen Pfad von v nach w gibt, der einen negativen Kreis enthält und dass es einen Pfad von v nach w gibt. Zu zeigen ist noch, dass es einen solchen Pfad gibt, der einfach ist und der eine Länge von δ (v, w) hat. Dazu schauen wir uns einen Pfad P von v nach w mit Länge δ (v, w) an. Dieser existiert wegen der Definition von δ. Angenommen, dieser Pfad hätte einen (nichtnegativen) Kreis P, dann könnte man ja den Pfad von v nach w im ungewichteten Sinne dadurch verkürzen, indem man den Kreis P weglässt. Aus der Wahl des Pfades P und der Definition von δ folgt, dass dieser neue entsprechend verkürzte Pfad P’ auch Länge von δ (v, w) hat (und dementsprechend der Kreis (gewichtete) Länge von null). Auf diese Weise kann man alle Kreise entfernen und erhält so einen legalen einfachen Pfad von v nach w mit Länge δ (v, w) . Also existiert immer ein solcher. ⇐) w ist also von v aus erreichbar und es gibt keinen Pfad von v nach w, der einen negativen Kreis enthält. Aus den Äquivalenzen in a) und b) folgt dann direkt − ∞ < δ (v, w) < +∞ . Daraus und aus dem Beweis der Hinrichtung folgt dann, dass es einen einfachen Pfad von v nach w mit Länge δ (v, w) gibt. ڤ Einen kürzesten Pfad / Weg definieren wir dann folgendermaßen: Ein Pfad P von u nach v ist kürzester Pfad / kürzester Weg von u nach v gdw. w(P) = δ (v, w) . Daraus, aus Lemma 1 und den „Rechenregeln“ für endliche und unendliche Zahlen folgt dann, dass ein 6 kürzester Pfad von u nach v genau dann existiert, falls − ∞ < δ (v, w) < +∞ und dass es immer einen solchen gibt, der einfach ist. Nun aber erst einmal ein Beispiel (siehe Abb. 1): δ(a, f) = +∞ δ(a, b) = 3 1 b f δ(a, c) = -∞ δ(a, e) = -∞ -7 c 15 1 3 e 4 a 5 d δ(a, d) = -∞ δ(a, a) = 0 Abb. 1: Beispiel-Graph 1. In diesem Beispiel gibt es den negativen Kreis [d, (d, e), e, (e, d), d], welcher dafür sorgt, dass c, d und e negativ unendliche Distanz von a haben. Es nur zwei kürzeste Pfade vom Knoten a aus: den trivialen, leeren Pfad von a nach a und den Pfad [a, (a, b), b] von a nach b. Bevor wir auf die eigentlichen Algorithmen zur Berechnung der oben definierten kürzesten Wege zu sprechen kommen, ist es nützlich (und interessant), zuvor noch ein paar allgemeine Eigenschaften kürzester Wege zu betrachten, da diese in vielen Algorithmen – mehr oder weniger explizit – ausgenutzt werden. Nachfolgendes Lemma sagt aus, dass Teilpfade von kürzesten Pfade wiederum selbst kürzeste Pfade sind: Lemma 2: (aus [BLUM98], S. 240) Sei G = (V, E, w) und sei P = [v1,v2, ..., vk] ein kürzester Pfad von v1 nach vk in G. Dann gilt für alle 1 ≤ i ≤ j ≤ k, dass der Teilpfad Pij := [vi,vi+1, ..., vj] von P ein kürzester Pfad von vi nach vj ist. Beweis: Angenommen, es gäbe einen kürzeren Pfad Pij’ von vi nach vj in G, also w(Pij’) < w(Pij). Betrachte dann den Pfad P’ := P1i, Pij’, Pjk. Dann gilt: w(P’) = w(P1i) + w(Pij’) + w(Pjk) < w(P1i) + w(Pij) + w(Pjk) = w(P). Dies ist aber ein Widerspruch dazu, dass P ein kürzester Pfad von v1 nach vk ist. Also ist Pij ein kürzester Pfad von vi nach vj. ڤ 7 2.2 Algorithmen zur Berechnung kürzester Wege 2.2.1 Übersicht In diesem Abschnitt möchte ich eine kleine Übersicht der Algorithmen zur Lösung von kürzeste-Weg-Problemen1 geben. Als Literatur diente mir hier [BLUM98], [LEDA99], [KRUM00] und [BRAN94]. Unter einem kürzeste-Weg-Problem versteht man normalerweise die Berechnung eines oder mehrerer kürzester Wege in einem gegebenen Graphen. Dabei unterscheidet man üblicherweise zwischen folgenden Varianten von kürzeste-Weg-Problemen: • Das Einzelpaar-kürzeste-Weg-Problem (single-pair shortest-path problem oder single-source single-sink shortest-path problem): Gegeben G = (V, E, w) und zwei Knoten s, t ∈ V , möchte man einen kürzesten Pfad von s nach t finden. • Das Einzelquelle-kürzeste-Weg-Problem (single-source shortest-path problem): Gegeben G = (V, E, w) und einen (Quell-)Knoten s ∈ V , soll für jeden Knoten v ∈ V ein kürzester Pfad von s zu v gefunden werden. • Das Alle-Paare-kürzeste-Weg-Problem (all-pairs shortest-path problem): Hier möchte man, gegeben G = (V, E, w), für jedes Paar u, v ∈ V einen kürzesten Pfad von u nach v finden. Man sieht, dass man natürlich mit der Lösung des alle-Paare-kürzeste-Weg-Problems auch die beiden anderen gelöst hat. Ausserdem löst das Einzelquelle-kürzeste-Weg-Problem auch das Einzelpaar-kürzeste-Weg-Problem. Interessanterweise ist für das zweite Problem auch kein asymptotisch effizienteres Verfahren bekannt, als das erste Problem zu lösen. Je nachdem, welche Eigenschaften der betrachtete Graph erfüllt, lassen sich verschieden effiziente Algorithmen zur Lösung der oben genannten Probleme verwenden. Für das Einzelquelle-kürzeste-Weg-Problem kennt man folgende Verfahrensweisen2: Ist der Graph ungewichtet bzw. haben alle Kanten das gleiche positive Gewicht, so lassen sich kürzeste Wege in Linearzeit O(G) := O(n+m) des Graphen bestimmen. 1 Ich dekliniere das „kürzeste-Weg“ von „kürzeste-Weg-Probleme“ nicht, da es so am üblichsten zu sein scheint. 2 Die hier skizzierten oder genannten Algorithmen sind die ältesten und bekanntesten kürzeste-Weg-Algorithmen, die man in den meisten Lehrbüchern finden kann. Zu manchen der angesprochenen Probleme gibt es inzwischen andere Algorithmen, welche leicht verbesserte Laufzeiten vorweisen, aber normalerweise viel komplizierter sind. Eine Betrachtung solcher Optimierungen würde den Rahmen dieses Kapitels sprengen. 8 In sog. dags, directed acyclic graphs, also gerichtete, kreisfreie Graphen, lassen sich mit Hilfe einer topologischen Sortierung des gegebenen Graphen (welche in O(G) berechenbar ist) in Linearzeit alle kürzesten Wege von einem Knoten aus berechnen. Hat der betrachtete Graph nur positive Kantengewichte, so liefert der Algorithmus von Dijkstra (siehe 2.2.2) in Zeit O(n2) bei Verwendung einfacher Datenstrukturen (bei komplizierteren geht es z.T. noch besser, s.u.) kürzeste Wege. Kommen auch negative Kantengewichte im Graphen vor, so gibt der Algorithmus von Dijkstra nicht mehr notwendigerweise die kürzesten Wege aus (sofern diese dann überhaupt existieren). Der Algorithmus von Bellman-Ford dagegen gibt in Zeit O(n*m) korrekt die kürzesten Wege aus oder terminiert mit der Meldung, dass es im EingabeGraphen einen negativen Kreis gibt. Verbesserte Versionen des Algorithmus können in der gleichen Zeit bei Existenz von negativen Kreisen sogar den Teil des Graphen, der die negativen Kreise und davon abhängige Knoten (also Knoten mit Distanz -∞) enthält, ausgeben, sowie den Teil, der nicht von den negativen Kreisen betroffen ist. Deutlich schwieriger wird das kürzeste-Weg-Problem, wenn man an einfachen Pfaden interessiert ist – zumindest dann, wenn negative Kreise vorkommen1. Dieses ist nämlich NP-vollständig, weil die Lösung auch eine Lösung des NP-vollständigen Problems der Berechnung der längsten einfachen Pfade im Graphen G’ := (V, E, -w) (wie G, nur mit einer mit -1 multiplizierten Kostenfunktion) ist. Das alle-Paare-kürzeste-Weg-Problem lässt sich durch n-maliges Lösen des Einzelquellekürzeste-Weg-Problem lösen, was zum Teil durchaus sehenswerte Laufzeiten ergeben kann2. Der Algorithmus von Floyd, welcher einen zentralen Bestandteil in dieser Diplomarbeit einnimmt und in 2.2.3 noch ausführlich beschrieben wird, gibt nach O(n3) Zeit entweder alle kürzeste Wege aus oder die Meldung, dass ein negativer Kreis im Graphen vorhanden ist. Da wir nun einen kleinen Überblick über die Komplexitäten der Bestimmung kürzester Wege in verschiedenen Klassen von Graphen und über Algorithmen zur Lösung von kürzeste-Weg-Probleme haben, wollen wir im nächsten Abschnitt zwei Vertreter dieser Algorithmen im Detail betrachten. 1 Falls keine negativen Kreise im betrachteten Graphen vorkommen, liefern die zuvor genannten Algorithmen für das kürzeste-Weg-Problem (natürlich) immer einfache Wege. Die Betrachtung kürzester einfacher Wege als eigenes Problem macht demnach nur Sinn bei Vorhandensein negativer Kreise im Graphen, also nicht z.B. bei azyklischen Graphen oder Graphen mit nur positiven Kantengewichten. 2 Siehe hierzu vielleicht [BLUM98], S. 248 ff.. Dort wird nämlich eine interessante Transformation eines Graphen mit allgemeinen Kantengewichten (allerdings bei Fehlen von negativen Kreisen) auf positive beschrieben, welche kürzeste Wege nicht ändert und in Zeit O(nm) durchführbar ist. 9 2.2.2 Im Detail: Algorithmus von Dijkstra Ich beschreibe jetzt den Algorithmus von Dijkstra als Stellvertreter für Einzelquellekürzeste-Weg-Algorithmen, da dies wohl der bekannteste kürzeste-Weg-Algorithmus überhaupt ist und man an ihm eine Möglichkeit der komprimierten Speicherung kürzester Wege kennenlernt. Im Gegensatz zu dem Algorithmus von Bellman-Ford müssen bei dem Algorithmus von Dijkstra die Kantengewichte positiv (≥ 0) sein. Dafür hat er eine bessere Laufzeit. Bevor wir zum eigentlichen Algorithmus kommen, schauen wir uns an, wie man überhaupt Wege und Distanzen vom Startknoten s zu allen anderen Knoten des Eingabe-Graphen darstellen bzw. später mit dem Algorithmus zurückgeben kann: (folgende drei Definitionen basieren auf [KRUM00], sind aber zum Teil für unsere Zwecke etwas verändert) Ein Wurzelbaum mit Wurzel s ist ein gerichteter Graph G = (V, E), für den gilt: • s ∈V • indegG(s) = 0, d.h. der Eingangsgrad von s (Anzahl Kanten, die in s eingehen) ist 0 • indegG(v) = 1 für alle v ∈ V \{s} • G ist (schwach) zusammenhängend, d.h., wenn G’ die ungerichtete Version von G sei, die man erhält, indem man die Kantenrichtungen in G ignoriert, dann ist G’ zusammenhängend bzw. es gibt jeweils einen Weg zwischen allen möglichen Knotenpaaren von G’ Aus der Definition von Wurzelbaum folgt direkt, dass es sich dabei um einen Baum handelt, an dessen Spitze (Wurzel) sich s befindet und es genau einen Pfad von s zu jedem anderen Knoten v ∈ V gibt. Sei G = (V, E, w) ein gerichteter, gewichteter Graph. Ein kürzester Wegebaum / kürzester Pfadbaum bzgl. G mit Wurzel s ist ein Wurzelbaum T = (V’, E’) ⊆ G mit Wurzel s mit folgenden Eigenschaften: • V’ = {v ∈ V | −∞ < δ ( s, v) < +∞} , d.h. V’ enthält alle (und auch nur die) Knoten, welche von s aus erreichbar sind • Für alle v ∈ V ' \{s} ist der (eindeutige) Weg von s nach v in T ein kürzester Weg von s nach v in G Der gleich folgende Algorithmus von Dijkstra gibt einen solchen kürzesten Wegebaum nicht direkt zurück, sondern in Form einer Funktion / Array π: V → V ∪ {nil} . Diese Funktion π induziert einen sog. Vorgängergraphen Gπ, der folgendermaßen definiert ist: Gπ = (Vπ Eπ) mit: Vπ = { v ∈ V | π(v) ≠ nil} ∪ {s} und 10 Eπ = {( π(v), v) | v ∈ Vπ und v ≠ s} Man sieht, dass ein solcher Vorgängergraph von der Struktur her kürzeste Wegebäume speichern kann, aber auch Bäume, die keine kürzesten Wegebäume sind (weil er auch Kreise enthalten und unzusammenhängend sein kann). Es gilt also später zu zeigen, dass der durch die zurückgegebene Funktion induzierte Vorgängergraph ein kürzester Wegebaum ist. Die Distanzen von s zu allen anderen Knoten v δ ( s, v) werden auch in Form einer Funktion oder eines Arrays d zurückgegeben: Algorithmus: DIJKSTRA (aus [BLUM98], S. 244) Eingabe: a) gerichteter, gewichteter Graph G = (V, E, w) mit w(u, v) ≥ 0 für alle (u, v) ∈ E b) Quellknoten s ∈ V Ausgabe: d(v) = δ ( s, v) für jeden Knoten v ∈ V und ein kürzester Wegebaum Tπ Methode: d(s) := 0; π(s) := nil; for all v ∈ V \{s} do d(v) := ∞; π(v) := nil; od Q := V; while Q ≠ Ø do finde u ∈ Q mit d(u) = min({d(v) | v ∈ Q }); Q := Q \ {u}; for all v ∈ Q mit (u, v) ∈ E do if d(v) > d(u) + w(u, v) then d(v) := d(u) + w(u, v); π(v) := u; fi od od Gib {d(v) | v ∈ V } und Tπ aus. ڤ Bevor wir auf die Korrektheit des Algorithmus zu sprechen kommen, schauen wir uns ein Beispiel an (Abb. 2): 11 1 4 5 1 2 3 2 9 1 3 2 1 2 1 1 8 6 3 3 8 7 0 Abb. 2: Beispiel-Graph 2. Gerichteter, gewichteter Graph G = (V, E, w) mit V = {0, ..., 9}. Die Beschriftungen innerhalb der Knoten sind die Knotenbezeichnungen und die Beschriftungen an den Kanten e stellen die Kantengewichte w(e) dar. δ(0, 3) = 4 3 δ(0, 0) = 0 δ(0, 1) = 1 δ(0, 2) = 2 0 1 2 δ(0, 6) = 4 6 δ(0, 9) = 3 δ(0, 4) =5 4 δ(0, 7) = 7 7 8 δ(0, 8) = 6 5 δ(0, 5) = 5 9 Abb. 3: Anwendung von Dijkstra auf Beispiel-Graph 2. Dies ist der kürzeste Wegbaum (mit den kürzesten Distanzen als Ergänzung eingetragen) , der sich aus der Vorgängerfunktion π ergibt, wenn man den Algorithmus von Dijkstra auf den Beispiel-Graphen 2 (Abb. 2) anwendet mit Quellknoten s = 1. Die Korrektheit des Algorithmus von Dijkstra zeige ich auf allgemeinere Weise, als eigentlich nötig, da man auf diese Art ein paar Einblicke in das kürzeste-Weg-Problem an sich bekommt. Da es in diesem Kapitel nur um die Berechnung kürzester Pfade von dem 12 einen Knoten s aus geht, werden wir die Notation der Minimaldistanzfunktion δ etwas verkürzen, indem wir δ(v) anstatt δ(s, v) schreiben. Folgendes Lemma, welches man als Kern vieler Korrektheitsbeweise von Einzelquelle-kürzeste-Weg-Algorithmen verwenden kann (so wie ich es hier mache), sagt etwas über die Beziehung zwischen vorläufigen Distanzfunktionen d und der Minimal-Distanzfunktion δ aus1: Lemma 3: (aus [LEDA99], S. 318) Sei G = (V, E, w): a) Für die Funktion δ gilt: i. δ(v) = min({δ(u) + w(e) | e = (u, v) ∈ E }) für v ≠ s ii. δ(s) = min({0, min({δ(u) + w(e) | e = (u, s) ∈ E })}) b) Falls d eine Funktion von V nach ℜ ∪ {−∞,+∞} ist, welche nachstehende Eigenschaften erfüllt, dann gilt: d(v) = δ(v) für alle v ∈ V i. d(v) ≥ δ(v) für alle v ∈ V , ii. d(s) ≤ 0 und iii. d(v) ≤ d(u) + w(u, v) für alle e = (u, v) ∈ E Beweis: Zu a) i) Jeder Pfad von s nach v besteht aus einem Teilpfad von s zu einem Knoten u und einer Kante von u nach v. Also gilt: δ(v) = inf ({w(P) | P ist ein Pfad von s nach v}) = minu inf ({w(P’) + w(e) | P’ ist ein Pfad von s nach u und e = (u, v) ∈ E }) = min ({δ(u) + w(e) | e = (u, v) ∈ E }). Zu a) ii) Falls wegen eines negativen Kreises δ(s) = -∞ gilt, dann gibt es auch ein u mit δ(u) = -∞. Also stimmt die Gleichung in diesem Fall. Ansonsten ist ein kürzester Pfad von s nach s der triviale Pfad mit gewichteter und ungewichteter Länge von 0 oder ein Kreis P mit gewichteter Länge von 0 und ungewichteter Länge von > 0. Die Länge eines solchen Kreises beträgt nach a) i) min({δ(u) + w(e) | e = (u, s) ∈ E })}) . Die Länge eines kürzesten Pfades von s nach s δ(s) 1 Diese vorläufigen / temporären Distanzfunktionen d, die von Algorithmen sukzessive verbessert (normalerweise verkleinert) werden bis sie δ entsprechen, werden in der englisch-sprachigen Literatur als tentative bezeichnet 13 entspricht dann dem Minimum von 0 und der Länge eines solchen Kreises. Die Gleichung stimmt also auch in diesem Fall. Zu b) Hierzu nehmen wir an, dass für ein v d(v) > δ(v) gelte. Daraus folgt wegen der Ordnung in ℜ U {−∞,+∞} , dass δ(v) < +∞. Nun betrachten wir zwei mögliche Fälle: Falls δ(v) > -∞, dann sei [s = v0, v1, ..., vk = v] ein kürzester Pfad von s nach v. Es gilt δ(s) = 0 = d(s) und δ(vi) = δ(vi-1) + w(vi-1, vi) für alle 1 ≤ i ≤ k. Wegen der Annahme d(v) > δ(v) gibt es mindestens ein i, so dass d(vi) > δ(vi). Wähle nun das Minimale solche i. Nun gilt d(vi) > δ(vi) = δ(vi-1) + w(vi-1, vi) = d(vi-1) + w(vi-1, vi). Dies ist aber ein Widerspruch gegen b) iii). Ansonsten, falls also δ(v) = -∞, sei [s = v0, v1, ..., vi, ..., vj, ..., vk = v] ein Pfad von s nach v, welcher einen negativen Kreis enthält. Ein solcher Pfad existiert nach Lemma 1. Der Teilpfad von vi nach vj sei ein (einfacher) negativer Kreis. Wegen unserer Annahme d(v) > δ(v) folgt nun d(v) > -∞ und wegen Eigenschaft b) iii) muss dann auch d(vl) > -∞ für alle 0 ≤ l ≤ k gelten. Also, d(vi) = d(vi) , weil vi = vj ≤ d(vj-1) + w(vj-1, vj) ≤ d(vj-2) + w(vj-2, vj-1) + w(vj-1, vj) ≤ ... ... j −1 ≤ d(vi) + ∑l =i w(vl , vl +1 ) , und deshalb ∑ j −1 l =i w(vl , vl +1 ) ≥ 0, was ein Widerspruch dazu ist, dass der Teilpfad von vi nach vj ein negativer Kreis ist. ڤ Bevor wir nun zeigen, dass die vom Algorithmus von Dijkstra berechnete Funktion d die Eigenschaften b) i) – iii) des vorigen Lemma erfüllt und damit die Korrektheit von d zeigen, machen wir uns erstmal Gedanken über den zurückgegebenen Vorgängergraphen: Lemma 4: (basiert auf [KRUM00], S. 74) Sei G = (V, E, w) gerichteter, gewichteter Graph, s ∈ V ein Quellknoten und in G sei von s aus kein Kreis negativer Länge erreichbar. Ein Algorithmus A initialisiere eine temporäre Distanzfunktion d: V → ℜ ∪ {−∞,+∞} mit d(v) = ∞ für alle v ∈ V \{s} und d(s) = 0 und eine Vorgängerfunktion π: V → V ∪ {nil} mit d(v) = nil für alle v ∈ V .Dann führe er eine Anzahl von folgenden (atomaren) Verbesserungsschritten auf den Knoten durch: VERBESSERE(u, v) if d(v) > d(u) + w(u, v) then 14 d(v) := d(u) + w(u, v); π(v) := u; fi Dann sind folgende Eigenschaften jederzeit erfüllt: a) Der durch π induzierte Vorgängergraph Gπ = (Vπ, Eπ) ist ein Wurzelbaum mit Wurzel s b) Gπ enthält für v∈ Vπ\{s} einen Weg P = [s = πk(v), ..., π(v), v]1 von s nach v der Länge höchstens d(v). Beweis: Wir beweisen die Behauptungen a) und b) durch Induktion über die Anzahl der Verbesserungsschritte m. Induktionsanfang: Falls m = 0, so ist Vπ = {s}, Eπ = Ø, d(s) = 0 und d(v) = +∞ für v ≠ s. In diesem Fall sind Behauptung a) und b) erfüllt. Induktionsannahme: Die Behauptung gelte direkt nach dem (m-1). Schritt der while-Schleife. Induktionsschritt: m-1 → m Die beiden Aussagen gelten nach m-1 Verbesserungsschritten, und es werde ein weiterer Verbesserungsschritt durchgeführt. Falls in diesem m. Schritt die Kondition der ifAnweisung false ergibt, also d(v) ≤ d(u) + w(u, v), dann ändert sich nichts an Gπ. In diesem Fall sind also auch nach dem m. Schritt die Eigenschaften a) und b) erfüllt. Falls d(v) > d(u) + w(u, v) im m. Schritt gilt, so wird d(v) auf d(u) + w(u, v) vermindert und π(v) := u gesetzt. Dadurch wird in Gπ die Kante (u, v) hinzugefügt und entweder der Knoten v hinzugefügt oder die alte Kante (x, v) vom früheren direkten Vorgänger x von v entfernt. Zu a) Aus der Konstruktion von π und Gπ folgt direkt, dass Gπ immer genau |Vπ| - 1 Kanten besitzt. Außerdem gilt indegG(s) = 0 und indegG(v) = 1 für alle v∈Vπ \{s}. Zu zeigen ist nur noch, dass Gπ schwach zusammenhängend ist bzw. das alle Knoten von s aus erreichbar sind. War vorher π(v) = nil, also v ∉ Vπ, so wird Gπ nun eben um diesen Knoten v und um die Kante (u, v) erweitert. Nach Induktionsvoraussetzung ist u von s aus in Gπ erreichbar und damit auch v. 1 Hierbei sei π0(v) = v und πi(v) = π (πi-1(v)) für i > 0, sofern πi-1(v) ≠ nil 15 Falls vorher π(v) = x ≠ nil galt, dann wird die Kante (x, v) in Gπ durch (u, v) ersetzt. Da nach Induktionsvorausstzung Gπ vor dem Tausch ein Wurzelbaum mit Wurzel s war, genügt es zu zeigen, dass nach dem Tausch der Knoten v von s aus in Gπ erreichbar ist. Wir betrachten nun zwei denkbare Fälle: Falls u zuvor ein Nachfolger von v war, also der eindeutige Pfad von s nach u über v ging, dann würde der Kantentausch zu einem Kreis führen, weil es ja immer noch einen Pfad von v nach u gibt und jetzt noch eine Kante von u nach v. Wegen der nur |Vπ| - 1 Kanten im Graphen wäre damit ein schwacher Zusammenhang von Gπ nicht möglich. Jetzt möchte ich zeigen, dass dieser Fall nicht möglich ist: Nach Induktionsvoraussetzung von a) und b) folgt, dass zuvor der eindeutige Pfad P = P1, P2 in Gπ von s nach u die Länge d(u) hatte, wobei P1 der Teilpfad von s nach v ist und P2 der Teilpfad von v nach u. Es folgt w(P1) = d(v) und w(P2) = d(u) – d(v). Der Kantentausch findet nur statt, wenn ⇔ d(v) > d(u) + w(u, v) = = 0 > w(P1) + w(P2) + w(u, v) d(v) + w(P2) + w(u, v) w(P2) + w(u, v) w(P2) + w(u, v) ist die Länge des Kreises von v nach u nach v. Widerspruch zur Voraussetzung der Nicht-Negativität aller von s aus erreichbaren Kreise. Falls u zuvor kein Nachfolger von v war, dann ist nach dem Tausch gemäß Induktionsvoraussetzung v von s aus durch einen Pfad von s nach u nach v erreichbar (und natürlich auch alle Nachfolger von v). Zu b) Die Tatsache, dass die Pfade P von s zu allen anderen Knoten v in Gπ die Struktur P = [s = πk(v), ..., π(v), v] haben, folgt direkt aus a) sowie den Definitionen des Wurzelbaums und Vorgängergraphen. Zu zeigen ist aber noch, dass deren Längen höchstens d(v) betragen: Wenn ein Verbesserungsschritt auf die Knoten u und v durchgeführt wird, so gilt direkt danach d(v) = d(u) + w(u, v). Falls nun danach – in einem anderen Schritt – d(u) verkürzt wird, gilt d(v) > d(u) + w(u, v) (und das Array speichert zu dem Zeitpunkt den falschen Distanzwert für v1; Gπ enthält aber den richtigen – bisher kürzesten – Pfad). Der Fall d(v) < d(u) + w(u, v) kann nicht auftreten, da es sonst nicht mehr die Kante (u, v) in Gπ geben würde oder einen negativen Kreis in G. Daraus folgt für einen Pfad P = [s = πk(v), ..., π(v), v] von s zu v in Gπ: d(πi(v)) ≥ d(πi+1(v)) + w(πi+1(v), πi(v)), für i = 0, ..., k-1 1 Bemerkung: Dieser Fall kann beim Algorithmus von Dijkstra nicht auftreten, da er die Knoten in einer bestimmten, gewissermaßen optimalen Reihenfolge betrachtet. Eine solche optimale Reihenfolge der Distanzverbesserungen ist nur möglich bei positiven Kantengewichten, azyklischen Graphen oder sonstigen Spezialfällen. Siehe dazu bei Interesse [LEDA99], wo dies formal charakterisiert wird. 16 aufsummiert ⇒ d(v) = d(π0(v)) ≥ d(s) + k −1 ∑ w(π i +1 (v), π i (v)) = 0 + w(P) i =0 Also besitzt der Weg P in Gπ Länge von höchstens d(v). ڤ Folgendes Lemma zeigt, dass die vom Algorithmus von Dijkstra berechnete Distanzfunktion v die Vorraussetzungen von Lemma 3 b) erfüllt: Lemma 5: Sei G = (V, E, w) ein gerichteter, gewichteter Graph mit positiver (≥ 0) Gewichtungsfunktion w.. Nach Terminierung des Algorithmus von Dijkstra, angewendet auf diesen Graphen und einem Quellknoten s ∈ V , gilt für die zurückgegebene DistanzFunktion d: V → ℜ ∪ {−∞,+∞} : a) d(v) ≥ δ(v) für alle v ∈ V , b) d(s) ≤ 0 und c) d(v) ≤ d(u) + w(u, v) für alle e = (u, v) ∈ E Beweis: Zu a) Dies zeigen wir durch Induktion über die Anzahl m der Verbesserungsschritte (siehe eventuell Aussage des Lemmas 4): Induktionsanfang: Für m = 0, also wenn noch kein Verbesserungschritt durchgeführt wurde, ist d(s) = 0 und d(v) = +∞ für v ∈ V \{s}. Dies erfüllt sicherlich die Behauptung a) Induktionsannahme: Die Behauptung gelte direkt nach dem (m-1). Schritt der while-Schleife. Induktionsschritt: Falls beim m. Schritt die Kondition des if-Teils false ergibt, ändert sich nichts an der Distanzfunktion d und demnach ist wegen der Induktionsvoraussetzung natürlich die Behauptung immer noch erfüllt. Andernfalls wird d(v) = d(u) + w(u, v) gesetzt. Wegen der Induktionsannahme gibt es dann einen Pfad von s nach u der Länge ≤ d(u). Daraus folgt dann, dass auch ein Pfad von s nach v über u (der Länge ≤ d(v)) existiert. Dieser kann natürlich nicht kürzer als ein kürzester Pfad von s nach v sein. Damit ist die Behauptung nach dem m. Verbesserungschritt immer noch erfüllt. 17 Zu b) d(v) kann im Algorithmus von Dijkstra aufgrund des dort Verbesserungsschrittes niemals zunehmen und am Anfang gilt: d(s) = 0. verwendeten Zu c) Für diesen Beweis definiere Q0 die Menge Ø und Qm die Menge Q im Algorithmus von Dijkstra direkt nach dem m. Schritt der while-Schleife (≠ Verbesserungsschritt). Zm sei das Komplement von Qm: Zm := Vm \ Qm. Mit vm bezeichne ich im folgenden das im m. Schritt der while-Schleife gewählte u (mit minimalem d(u)) im Algorithmus von Dijstra Offenbar wird für jeden Knoten v ∈ V nach dem Streichen von v aus Q der Wert d(v) nicht mehr geändert. Hauptargument für den folgenden Induktionsbeweis ist, dass durch die Wahl des u mit minimalem d(u) aus Q gilt (und der Tatsache, dass alle Kanten positive Längen haben): d(s = v1) ≤ d(v2) ≤ ... ≤ d(vn) (*) Behauptung: Direkt nach jedem m. Schritt der while-Schleife gilt: d(v) ≤ d(u) + w(u, v), falls (für alle) e = (u, v) ∈ E und u, v ∈ Zm Beweis der Behauptung durch vollständige Induktion: Induktionsanfang: Falls m = 0, also Zm = Ø ist die Behauptung sicherlich erfüllt. Induktionsannahme: Die Behauptung gelte direkt nach dem (m-1). Schritt der while-Schleife. Induktionsschritt: Betrachte eine Kante e = (u, v) in G mit u, v ∈ Zm: Falls u und v beide auch in Zm-1 enthalten sind, folgt die Behauptung durch die Induktionsannahme. Falls u nicht in Zm-1 enthalten ist, also u = vm und v = vk mit k < m, dann folgt aus (*) d(u) ≥ d(v) und wegen positiver Kantengewichte die Behauptung. Falls v nicht in Zm-1 enthalten ist, also v = vm und u = vk mit k < m, dann gab es im k. Schritt der while-Schleife einen Verbesserungsschritt (siehe Aussage des Lemmas 4), in dem d(v) := d(u) + w(u, v) gesetzt wurde, falls zu dem Zeitpunkt d(v) > d(u) + w(u, v) galt. Also ist die Behauptung auch in diesem Fall nach Hinzufügung von vm zu Z erfüllt. Da dem Induktionsbeweis nach die Behauptung für Zn = V gilt, ist damit diese Eigenschaft des Algorithmus bewiesen. ڤ 18 Der nun folgende Satz, den man ebensogut als Korollar bezeichnen könnte, zeigt die Korrektheit des Algorithmus von Dijkstra und beendet diesen Abschnitt: Satz 1: Sei G = (V, E, w) ein gerichteter, gewichteter Graph mit positiver Gewichtungsfunktion w. Nach Terminierung des Algorithmus von Dijkstra, angewendet auf diesen Graphen und einem Quellknoten s ∈ V , gilt für die zurückgegebene Distanz-Funktion d: V → ℜ ∪ {−∞,+∞} und die Vorgängerfunktion π: V → V ∪ {nil} und die Laufzeit: a) d(v) = δ(v) für alle v ∈ V . b) Der durch π induzierte Vorgängergraph Gπ = (Vπ Eπ) ist ein kürzester Wegebaum mit Wurzel s. c) Es gibt für den Algorithmus von Dijkstra eine O(n2) Zeit Implementierung. Beweis: Zu a) Folgt direkt aus Lemma 5 und Lemma 3 b). Zu b) Folgt direkt aus a) und Lemma 4. Zu c) Bis auf das Finden von u ∈ Q mit d(u) = min({d(v) | v ∈ Q }) können wir sämtliche Arbeit den Knoten und Kanten des Graphen G zuordnen und lässt sich demnach in O(n + m) implementieren. Falls wir eine verkettete Liste für die Verwaltung der Menge Q verwenden, dann kann das minimale u aus Q jeweils in Zeit O(|Q|) gefunden werden. Die Funktionen d und π würde man mit Hilfe eines Arrays implementieren, so dass man in konstanter Zeit darauf zugreifen kann. Insgesamt ergibt das dann eine O(n2) Implementierung. ڤ Die Laufzeit von O(n2) lässt sich durch die Verwendung von komplizierteren Datenstrukturen zur Verwaltung der Menge Q noch verbessern. So kommt man mit einer Priority-Queue auf eine Laufzeit von O((n + m) log n) (siehe [BLUM98], S. 246), was für lichte Graphen effizienter als O(n2) sein kann. In [BRAN94] wird auf weitere Autoren verwiesen, welche mit Hilfe von Fibonacci-heaps bzw. AF-heaps die Zeitschranke von n log n ) für den Algorithmus von Dijkstra erreicht haben. O( m + log log n 19 2.2.3 Im Detail: Algorithmus von Floyd Der Algorithmus von Floyd löst das Alle-Paare-kürzeste-Weg-Problem für Graphen G = (V, E, w) mit V = {1, ..., n} durch dynamische Programmierung. Dazu gibt er zwei Matrizen zurück: Die Floyd-Distanz-Matrix D und die Floyd-Wege-Matrix Π (diese Matrizen werde ich im Folgenden zum Teil auch verkürzt als Distanz-Matrix bzw. WegeMatrix bezeichnen) zurück. Die erste enthält alle kürzesten Distanzen und die zweite Matrix Π speichert alle kürzesten Wege auf folgende, sehr komprimierte Art und Weise: Der Matrix-Eintrag πij speichert den direkten Vorgänger von j auf dem bzw. einem kürzesten Pfad von i nach j. Folgender Pseudocode ist – zumindest, was die Art der WegeMatrix Π betrifft – die wohl gängigste Variante: Algorithmus: FLOYD (basiert am meisten auf [BLUM98], S. 250) Eingabe: gerichteter, gewichteter Graph G = (V, E, w) mit (o.B.d.A.) V = {1, ..., n} Ausgabe: entweder eine n x n - Matrix D mit dij = δ (i, j ) für alle i, j ∈ V und eine n x n - Matrix Π mit πij = (der letzte Knoten k vor j auf einem kürzesten Pfad von i nach j) für alle i, j ∈ V oder die Meldung „Es existiert Kreis mit negativem Gewicht in G“ Methode: // Initialisiere die kürzeste-Distanz-Matrix D und die kürzeste-Weg-Matrix Π, falls // nicht schon getan for i := 1 to n do for j := 1 to n do ⎧w(i, j ), falls (i, j ) ∈ E ⎪ d ij := ⎨0, falls i = j ⎪∞, sonst ⎩ ⎧nil , falls i = j oder wij = ∞ π ij := ⎨ ⎩i, sonst od od // Eigentlicher Algorithmus for k := 1 to n (*) do for i := 1 to n do for j := 1 to n do if d ij > d ik + d kj 20 then d ij := d ik + d kj ; πij := πkj ;1 fi od od od if ( d ii < 0 für ein i∈{1, ..., n}) then print(“Es existiert ein negativer Kreis im Graphen“); stop; fi return D, Π; ڤ Bevor wir die Korrektheit des Algorithmus von Floyd zeigen, betrachten wir ein Beispiel: dij 0 1 2 3 4 5 6 7 8 9 0 0 1 2 4 5 6 4 7 6 3 1 1 0 1 3 4 5 3 6 5 2 2 2 1 0 2 3 4 2 5 4 1 3 4 3 2 0 1 2 4 7 6 3 4 5 4 3 1 0 1 5 8 6 3 5 5 4 3 2 1 0 5 8 5 2 6 4 3 2 4 5 6 0 3 6 3 7 7 6 5 7 8 9 3 0 3 6 8 6 5 4 6 7 8 6 3 0 3 πij 0 1 2 3 4 5 6 7 8 9 9 3 2 1 3 4 5 3 6 3 0 0 1 1 1 1 1 1 1 1 1 1 0 2 2 2 2 2 2 2 2 2 1 1 3 3 3 6 6 9 9 3 2 2 2 4 4 2 2 2 2 4 3 3 3 3 5 3 3 5 5 5 9 9 9 4 4 9 9 9 9 6 2 2 2 2 2 2 7 7 2 7 6 6 6 6 6 6 6 8 6 8 9 9 9 9 9 9 7 7 9 9 2 2 2 2 2 2 2 2 8 - Tab. 1: Anwendung von Floyd auf Beispiel-Graph 2 (Abb. 2). Die vom Algorithmus von Floyd zurückgegebenen Matrizen D und Π bei Eingabe von Beispiel-Graph 2 (Abb. 2). Hierbei bedeutet in der Matrix Π ´-´ nil. Die Korrektheit des obigen Algorithmus lässt sich meiner Meinung nach am einfachesten dadurch zeigen, indem man die Korrektheit einer leicht veränderten Version FLOYD’ des Algorithmus zeigt und dann begründet, warum der Korrektheitsbeweis von FLOYD’ auch auf FLOYD zutrifft. Die Version FLOYD’ unterscheided sich von FLOYD nur darin, dass sie für jeden Schritt k der äußersten while-Schleife explizit neue Matrizen Dk und Πk aus den 1 An dieser Stelle sind auch andere (sinnvolle) Zuweisungen möglich. Damit befasst sich der Kern der Diplomarbeit. Siehe Kap. 4 21 Matrizen Dk-1 und Πk-1 des vorherigen Schrittes berechnet anstatt einfach die alten Werte durch neue zu überschreiben. Algorithmus: FLOYD’ Eingabe: gerichteter, gewichteter Graph G = (V, E, w) mit (o.B.d.A.) V = {1, ..., n} Ausgabe: entweder eine n x n - Matrix Dn mit d ijn = δ (i, j ) für alle i, j ∈ V und eine n x n - Matrix Πn mit π ijn = (der letzte Knoten k vor j auf einem kürzesten Pfad von i nach j) für alle i, j ∈ V oder die Meldung „Es existiert Kreis mit negativem Gewicht in G“ Methode: // Initialisiere die kürzeste-Distanz-Matrix D und die kürzeste-Weg-Matrix Π, falls // nicht schon getan for i := 1 to n do for j := 1 to n do ⎧w(i, j ), falls (i, j ) ∈ E ⎪ 0 d ij := ⎨0, falls i = j ⎪∞, sonst ⎩ ⎧nil , falls i = j oder wij = ∞ π ij0 := ⎨ ⎩i, sonst od od // Eigentlicher Algorithmus for k := 1 to n (*) do for i := 1 to n do for j := 1 to n do if d ijk −1 > d ikk −1 + d kjk −1 then d ijk := d ikk −1 + d kjk −1 ; π ijk := π kjk −1 ;1 else d ijk := d ijk −1 ; 1 An dieser Stelle sind auch andere (sinnvolle) Zuweisungen möglich. Damit befasst sich der Kern der Diplomarbeit. Siehe Kap. 4 22 π ijk := π ijk −1 ; fi od od od if ( d iin < 0 für ein i∈{1, ..., n}) then print(“Es existiert ein negativer Kreis im Graphen“); stop; fi return Dn, Πn; ڤ Für die Korrektheit von FLOYD’ brauchen wir noch eine Definition: ⎧inf({w( P) | P ist (endlicher ) Pfad von v nach w, der als innere Knoten nur Knoten ⎪ aus {v1 ,..., v k } enthält}), falls ein solcher Pfad existiert , δ := ⎨ ⎪+ ∞, sonst ⎩ k ij Aus der Definition von δ ijk folgt direkt, dass δ ijk ≥ δ ij := δ(i, j) für k ≤ n und δ ijn = δ ij . Wenn man nun zeigen könnte, dass δ ijk = d ijk für alle k, dann hätte man damit zum großen Teil schon die Korrektheit des Algorithmus von Floyd gezeigt (zumindest beim Fehlen von negativen Kreisen). Dies wollen wir nun im folgenden Lemma machen: Lemma 6: Sei G = (V, E, w) ein gerichteter, gewichteter Graph. G enthalte keine negativen Kreise. Dann gilt nach Terminierung von FLOYD’, angewendet auf diesen Graphen, für alle k folgendes: a) δ ijk = d ijk b) π ijk enthält als Wert den letzten Knoten vor j auf einem Pfad P von i nach j in G der Länge δ ijk (also P = [i, ..., k, j]), falls j von i aus erreichbar und sonst π ijk = nil Beweis: Beweis der Behauptungen a) und b) durch vollständige Induktion über k: Induktionsanfang: Bei k = 0 sind sicherlich beide Behauptungen a) und b) erfüllt 23 Induktionsannahme: Die Behauptungen a) und b) gelten für k = l – 1 Induktionsschritt: Ein kürzester Pfad von Knoten i nach j mit inneren Knoten ∈ {1, ..., k} enthält entweder den Knoten k gar nicht als inneren Knoten oder genau einmal. Im ersten Fall gilt dann δ ijk −1 = δ ijk . Gemäß Induktionsvoraussetzung wurde dieser kürzeste Pfad schon im Schritt k-1 betrachtet. Daraus folgt d ijk −1 ≤ d ikk −1 + d kjk −1 und dementsprechend gilt nach der Zuweisung im k. Schritt d ijk −1 = d ijk und π ijk −1 = π ijk , was die beiden Behauptungen zeigt. Im zweiten Fall, also wenn δ ijk −1 > δ ijk , setzt sich dieser kürzester Pfad aus einem kürzesten (Teil-) Pfad P1 von 1 zu k und einem kürzesten (Teil-) Pfad P2 von k zu j zusammen (Lemma 1), wobei alle inneren Knoten von P1 und P2 aus {1, ..., k} sind. Also gilt δ ijk = δ ikk −1 + δ kjk −1 . Nach Induktionsvoraussetzung wurden diese Teilpfade schon im Schritt k-1 betrachtet und es folgt d ijk −1 > d ikk −1 + d kjk −1 . Also werden im k. Schritt der äußeren whileSchleife die Zuweisungen d ijk := d ikk −1 + d kjk −1 und π ijk := π kjk −1 durchgeführt, welche offensichtlich korrekt sind. Zum Induktionsbeweis ist vielleicht noch zu bemerken, dass - wie unschwer zu sehen - ein einmal in eine Matrix geschriebener Wert niemals verändert wird. ڤ Jetzt ist noch zu zeigen, dass FLOYD’ bei Existenz von negativen Kreisen in G wie gewünscht mit der Meldung „Es existiert ein negativer Kreis im Graphen“ abbricht: Lemma 7: Sei G = (V, E, w) ein gerichteter, gewichteter Graph. Falls G einen negativen Kreis enthält, dann gibt FLOYD’ die Meldung „Es existiert ein negativer Kreis im Graphen“ aus. Beweis: Betrachte einen einfachen negativen Kreis K = {v1, v2, ..., vq, ..., vr, v1}. Hierbei sei vq der Größte der inneren Knoten v2, ..., vr von K. Nach Lemma 6 und der Wahl von q sind zu irgendeinem Zeitpunkt k = k’ < q die Distanzen d vk1 ,vq und d vkq ,v1 berechnet. Zum Zeitpunkt k = vq wird dann wegen der Zuweisung d vk1v1 := d vk1−,v1q + d vkq−,1v1 spätestens d vk1v1 < 0 gelten und höchstens noch kleiner werden. Am Ende wird dann die Meldung „Es existiert ein negativer Kreis im Graphen“ ausgegeben. 24 ڤ Die letzen beiden Lemmata zusammen mit einer trivialen Performanzanalyse ergeben dann direkt folgendes Lemma über die Korrektheit von FLOYD’: Lemma 8: Sei G = (V, E, w) ein gerichteter, gewichteter Graph mit V = {1, ..., n} und einer Kostenfunktion w: E → ℜ . Falls G einen negativen Kreis enthält, dann termininiert der Algorithmus FLOYD’ mit der Meldung „Es existiert ein negativer Kreis im Graphen“. Ansonsten gibt er eine Matrix D zurück mit dij = δ(i, j) für alle i, j und eine Matrix Π mit πij = kij, für alle i, j, wobei kij der letzte Knoten vor j auf einem kürzesten Pfad von i nach j sei (also sehe ein solcher kürzester Pfad von i nach j so aus: [i, ..., kij, j]). Dies macht der Algorithmus in Θ(n3) Zeit. ڤ Jetzt ist noch zu zeigen, dass die Lemmata 6, 7 bzw. 8 auch für Algorithmus FLOYD gelten. Hierfür brauchen wir noch zur einfacheren oder eindeutigeren Formulierung eine neue Definition: Es bezeichne d ij (k) den Wert von dij bzw. D(k) die Matrix D im Algorithmus FLOYD direkt nach dem k. Schritt der äußersten for-Schleife (*). Lemma 9: Für die Algorithmen FLOYD und FLOYD’ gilt: Für alle k ≤ n gilt: Direkt nach jedem k. Durchlauf der äußersten for-Schleife (*) der Algorithmen sind die Matrizen D und Π von FLOYD gleich den Matrizen Dk und Πk von FLOYD’, also Dk = D(k). Beweis: Induktionsanfang: Bei k = 0 ist sicherlich D0 = D(0). Induktionsannahme: Die Behauptung gelte für k = l – 1 Induktionsschritt: Ich zeige nun, dass bei k = l für alle i, j bei der if-Bedingung d ij > d ik + d kj des Algorithmus FLOYD folgendes gilt: dik = d ikk −1 und dkj = d kjk −1 : Damit dik < d ikk −1 gelten kann, muss nach der Induktionsannahme zuvor im k. Schritt die ifBedingung d ik > d ik + d kk gegolten haben. Dies ist aber nicht möglich. Analoges gilt für dkj. Da offensichtlich auch in jedem Schritt der äußersten Schleife jeder Eintrag der Matrizen nur genau einmal beschrieben wird, ist damit die Behauptung gezeigt. 25 ڤ Aus den Lemmata 8 und 9 folgt dann direkt der abschließende Satz dieses Kapitels: Satz 2: Sei G = (V, E, w) ein gerichteter, gewichteter Graph mit V = {1, ..., n} und einer Kostenfunktion w: E → ℜ . Falls G einen negativen Kreis enthält, dann termininiert der Algorithmus von Floyd FLOYD mit der Meldung „Es existiert ein negativer Kreis im Graphen“. Ansonsten gibt er eine Matrix D zurück mit dij = δ(i, j) für alle i, j und eine Matrix Π mit πij = kij, für alle i, j, wobei kij der letzte Knoten vor j auf einem kürzesten Pfad von i nach j sei (also sehe ein solcher kürzester Pfad von i nach j so aus: [i, ..., kij, j]). Dies macht der Algorithmus in Θ(n3) Zeit. ڤ 26 3 Kürzeste Wege in Straßennetzen In diesem Kapitel geht es um die (automatisierte) Routenplanung in Straßennetzen. Dazu definieren wir zuerst formal ein Straßennetz zum Zwecke der Routenplanung. Danach betrachten wir ein wenig den Prozess der Modellierung eines Straßennetzes aus der Realität mit unserer Definition. Zuletzt gehen wir auf kürzeste Wege in solchen Straßennetzen ein und insbesondere auf Unterschiede und Gemeinsamkeiten zu den kürzeste-Weg-Problemen bzw. Lösungsmöglichkeiten in „normalen“ Graphen, wie wir sie im vorherigen Kapitel kennen gelernt haben. In diesem Kapitel, wie in den folgenden, habe ich nur ein wenig von [HASS00] als Literatur benutzt und sonst das Wissen aus bisherigen Informatik-Vorlesungen, die aber nichts direkt mit dieser Thematik zu tun hatten. Und natürlich stand Prof. Plümer mit Rat und Tat zur Seite. Insofern sind die Definitionen, die Prof. Plümer bisher auch nur zum kleinen Teil gesehen hat, vielleicht mit etwas Vorsicht zu genießen, weil ich nicht weiß, ob und eventuell wie diese schon mal definiert wurden. 3.1 Einleitung und Definitionen Ein Straßennetz ist für uns eine bestimmte Erweiterung eines gerichteten, gewichteten Graphen G = (V, E, w). Formal definieren wir Straßennetz wie folgt: Ein Straßennetz ist ein Tupel (V, E, S, T, w, s, t), wobei • V, E, w wie bei einem gewichteten, gerichteten Graphen mit positiver Kostenfunktion die Knotenmenge V, Kantenmenge E ⊆ (V × V ) \ {(v, v) | v ∈ V } bzw. Kostenfunktion w: E → ℜ + . Hierbei gelte wieder |V| = n, |E| = m, • S die Menge der Straßen ist, • T die Menge der Straßentypen (z.B. Autobahnen, Bundestraßen, Landstraßen, ...) ist, • s: E → S eine Funktion der Kanten auf die Straßen ist, welche also die Kanten den jeweiligen Straßen zuordnet, • t: S → T eine Funktion der Straßen auf die Straßentypen ist, die also besagt, was für einem Straßentyp die Straßen jeweils angehören. Zu dieser Definition ist zu bemerken, dass hierdurch Straßennetze möglich sind, wie sie in der realen Welt nicht vorzufinden sind. So ist es mit der Definition beispielsweise möglich, Straßen zu konstruieren, die aus vielen einzelnen Stücken bestehen, die nicht zusammenhängend sind. Durch Hinzufügen einiger zusätzlicher Bedingungen / Constraints in der Definition könnte man dies (ohne großen Aufwand) vermeiden. Da aber keine der folgenden Definitionen, Algorithmen oder sonstige Überlegungen solche zusätzlichen Eigenschaften des Straßennetz-Graphen benötigen, belassen wir es bei der 27 einfacheren. Außerdem kann es ja auch sein, dass man in einem dynamischeren Kontext ein Straßennetz modellieren möchte, in dem z.B. ein Straßenstück einer Straße wegen einer Baustelle fehlt. In den folgenden Kapiteln werden wir manchmal noch zusätzlich von dem Straßennetz fordern, dass es zusammenhängend ist oder dass es maximal einen kürzesten Weg zwischen zwei verschiedenen Knoten gibt. Erstere Forderung vereinfacht die Algorithmen und Erklärungen, und es ist nicht schwer zu sehen, dass diese die jeweiligen Probleme an sich nicht (asymptotisch) vereinfachen. Die zweite Forderung vereinfacht z.T. das Problem an sich, ist aber in der Realität wie die erste auch (fast) immer erfüllt, zumindest wenn man die Kostenfunktion unter anderem von der geometrischen Länge des Weges oder der Fahrzeit abhängig macht. 3.2 Modellierung eines realen Straßennetzes In diesem Abschnitt geht es darum, wie man ein reales Straßennetz (z.B. das Straßennetz von Nordrhein-Westfalen) in (V, E, S, T, w, s, t) umformen bzw. in einen Computer eingeben kann, um darauf Algorithmen zur Routenplanung für die Realität ablaufen zu lassen. Dies erscheint auf den ersten Blick trivial – man nehme einfach als die Knoten die Kreuzungspunkte von Straßen und als die Kanten die Straßenstücke zwischen den Kreuzungspunkten, messe Zeiten, Längen oder nehme sonstige Kostenfunktionen etc. – und ist auch nicht allzu schwierig, hat aber doch ein paar mögliche Fallstricke und verdient ein wenig Beachtung. 3.2.1 Granularität Bei der Modellierung eines Straßennetzes aus der Realität mit der Straßennetz-Definition aus dem vorherigen Abschnitt gibt es einige Freiheitsgrade. Einen von diesen würde ich als die Granularität der Modellierung bezeichnen1. Darunter verstehen wir, wieviele (für uns relevante) atomare Objekte des Straßennetzes in der Realität zu jeweils einem modellierten Objekt zusammengefasst werden bzw. welche „Größe“ unsere Knoten und Kanten im Straßennetz (V, E, S, T, w, s, t) haben. Für uns relevante (d.h. für die Wegfindung relevante) atomare Objekte des zugrundeliegenden Straßennetzes in der Realität sind einzelne Straßenspuren und Knoten auf Straßenspuren. Eine Kante im Straßennetz-Graphen kann z.B. für eine Fahrspur einer Autobahn stehen oder kann auch 1 Die Bezeichnung „Granularität“ in diesem Kontext für das, was ich meine, stammt von mir und ich konnte nicht herausfinden, ob es dafür nicht schon einen anderen Begriff gibt. Diese Bezeichnung dürfte aber relativ intuitiv sein. Hierbei ist vielleicht zu bemerken, um Missverständnissen vorzubeugen, dass bei hoher Granularität die Granulate klein sind und bei niedriger Granularität die Granulate groß sind (ähnlich wie z.B. beim Maßstab). 28 alle drei Spuren für ein bestimmtes Stück repräsentieren. In Abb. 4 ist als Beispiel eine Kreuzung einmal in hoher Granularität und in niedriger Granularität modelliert 1. Abb. 4: Kreuzung mit Modellierung in niedriger und hoher Granularität. Eine ziemlich komplexe Kreuzung (mit Ampeln, die für uns keine Rolle spielen und daher auch nicht abgebildet sind), bei der man von jeder Richtung aus kommend in jede andere Richtung fahren kann. Die Kreuzung selbst, wie sie in der realen Welt vorkommt, ist in den Farben grau und weiß eingezeichnet. Darüber ist in den Farben Himmelblau und Türkis eine Modellierung in niedriger Granularität, bestehend aus nur einem (bzw. 5) Knoten und 4 Kanten angedeutet. In den Farben rot und einem hellen orange-Ton ist eine Modellierung in hoher Granularität, bestehend aus 28 (bzw. 32) Knoten und 40 Kanten, abgebildet. Gerade bei der Verwendung einer niedrigen Granularität muss man aufpassen, dass im Modell immer noch die gleichen Wege (also nicht mehr und auch nicht weniger) möglich sind wie in der Grundlage2. Siehe dazu auch Punkt 3.2.3. 1 Zu Abb. 4 und ein paar nachfolgenden Abbildungen ist eventuell zu bemerken, dass wir keine Einbettungen von Graphen bzw. dem Straßennetz in die Ebene betrachten. Die Graphen bzw. Straßennetze sind nur zur Anschaulichkeit über dem Straßennetz aus der realen Welt gezeichnet. 2 Streng genommen ist die Modellierung in niedriger Granularität in Abb. 4 auch nicht absolut korrekt, da es darin möglich ist, in der Kreuzung einen U-Turn zu machen, was in der Realität wahrscheinlich nicht möglich ist bei normalem Verkehr und Einhaltung der Verkehrsregeln. Bei kürzeste-Weg-Berechnungen, mit denen wir uns im folgenden hauptsächlich beschäftigen werden, dürfte diese spezielle Unkorrektheit aber meistens nicht schaden, weil ein U-Turn eher selten in kürzesten Wegen enthalten ist (U-Turns sind eher a-posteriori Notlösungen bei zuvor fehlerhafter Routenplanung) 29 Für viele Anwendungen wird wahrscheinlich eine eher niedrige Granularität ausreichen, da diese meistens dazu dienen, einem Menschen zu sagen, wo er herfahren soll und Menschen – zumindest mit ein bißchen Erfahrung – mit einer Beschreibung wie „Fahr so lange geradeaus bis du auf die A3 auffahren kannst, dann Abfahrt Siebengebirge auf der A3, dann rechts auf die Straße ...“ zurechtkommen. Wenn man dagegen ein Navigationssystem für ein vom Computer gesteuertes Auto schreiben wollte, könnte man der Steuerungs-KI mit einem gespeicherten Straßennetz hoher Granularität schon helfen. Stausimulationen und computergestützte Straßennetz-Planung wären ebenfalls Anwendungen für hohe Granularität. Wenn man die Wahl hat, versucht man natürlich eine so niedrige Granularität wie möglich zu verwenden, da dies Speicherplatz einspart und auch Laufzeit bei kürzeste-Weg-Berechnungen. 3.2.2 Redundanz Unter Redundanz verstehe ich das Vorhandensein von Knoten im echten Inneren von Pfaden, die eigentlich unnötig sind, weil durch diese Knoten im Prinzip nur ein Pfad geht. In insgesamt ungerichteten Straßennetzen sind das Knoten mit einem Grad von genau zwei. In gerichteten Straßennetzen ist dies ein wenig komplizierter1. Diese Knoten bezeichnen wir als redundante Knoten. Siehe Abb. 5 für ein Beispiel. Offensichtlich hat Redundanz viel mit Granularität zu tun. So gibt es in Abb. 4 bei der Modellierung mit hoher Granularität auch den einen oder anderen redundanten Knoten nach dieser Definition. Wenn man alle redundanten Knoten der Reihe nach entfernt (und die Kanten entsprechend „umleitet“), erhält man eine Modellierung in niedrigerer Granularität 2. Trotzdem machen alle Knoten der Modellierung in hoher Granularität in dieser Kreuzung irgendwie Sinn für spezielle Anwendungen, die Straßennetze in hoher Granularität benötigen. Z.B. weil die redundanten Knoten (als einzige Knoten) Straßenspuren repräsentieren oder Punkte, bei denen man auf das Grün-Werden von Ampeln oder das Vorbeifahren von anderen Verkehrteilnehmern warten muss. Folglich definieren wir mit echter Redundanz das Vorhandensein von redundanten Knoten, die für die betrachtete Anwendung „wirklich“ überflüssig sind3. Diese echte Redundanz gilt es natürlich (per Definition) immer zu vermeiden, da man so Speicherplatz und Laufzeit für Berechnungen auf dem Straßennetz einspart. 1 In gerichteten Straßennetzen (so wie wir sie in dieser Diplomarbeit ja auch eigentlich nur betrachten) ist Knoten v genau dann redundant, wenn er genau zwei adjazente Knoten u und w hat und der Ausgangsgrad von v dem Eingangsgrad von v entspricht. 2 So ähnlich würde man wohl Algorithmen programmieren, die ein Straßennetz von einer niedrigen Granularität in eines mit einer höheren (der höchsten) Granularität transformieren. 3 Mir ist bewusst, dass dies keine eindeutige und klare Definition ist. Ich habe aber trotz längerem Nachdenken nichts besseres gefunden und denke, dass das ohne weitere Formalitäten (z.B. formale Klassifikationen von Anwendungen für Straßennetze) auch schwierig sein dürfte. 30 Abb. 5: Redundanz. Hier ist ein in den Farben grau und weiß gezeichneter Teil eines Straßennetzes zu sehen (so wie es in der realen Welt oder in Lego aussieht). Darüber ist in den Farben Himmelblau, Rot und einem hellen Orange-Ton eine Modellierung eingezeichnet. Die roten Knoten sind hierbei redundant und könnten weggelassen werden (und die zu diesen Knoten inzidenten Kanten verschmolzen werden). 3.2.3 Korrektheit der Modellierung Die Abbildung eines realen Straßennetzes auf unsere Definition sollte (natürlich) korrekt sein, d.h. in dem Modell (V, E, S, T, w, s, t) für ein reales Straßennetz dürfen keine Pfade enthalten sein, die es in der Realität nicht gibt und umgekehrt. Dies ist durch Verkehrsregeln wie Wendeverbote (diese sind oftmals eher implizit wegen starkem Verkehrsaufkommen etc.) und Abbiegeverbote oft nur durch zusätzliche Knoten (und Kanten) machbar. Gerade bei der Wahl einer niedrigen Granularität der Modellierung muss man darauf achten, dass die Korrektheit gewährleistet bleibt. Es ist klar, dass man bei der Routenplanung nicht auf die Korrektheit verzichten kann1. 1 Höchstens könnte man sie auf kürzeste Pfade einschränken,, obwohl es hier wahrscheinlich die bessere Alternative wäre, alle Kanten, die in keinem kürzesten Pfad vorkommen, einfach wegzulassen. 31 3.2.4 Kantenkosten Wie soll man die Kantenkosten wählen? Generell sind beliebige Kostenfunktionen erlaubt und je nach Anwendung auch sinnvoll: Fahrzeit, geometrische Distanz, Benzinkosten, Straßenbefahrungskosten, Zölle, ... und Kombinationen. Wenn man nach einem Weg von Punkt A nach Punkt B sucht, dann interessiert einem meistens die Zeitdauer der Reise von A nach B. Daher werden wir mit dem Kantenkosten immer – mehr oder weniger explizit – die Zeitdauer meinen 1. Die einfacher zu bestimmende geometrische Länge des Weges ist eigentlich nur sekundär als Approximation für die Zeitdauer interessant (auch wenn sie bei der zwischenmenschlichen Kommunikation wichtiger zu sein scheint). Deshalb werden wir für die Kantenkosten irgendwie die Zeitdauer des Befahrens der entsprechenden Kante nehmen. Wie soll die Zeitdauer bestimmt werden? Sinnvolle Beispiele für die Wahl der Kantenkosten sind die in der Realität gemessene durchschnittliche Fahrzeit über die Kante (vom Startknoten der Kante zum Zielknoten). Oder eine Approximation der Zeitdauer, indem man die geometrische Länge durch die vom Gesetzgeber für diese Kante erlaubte Höchstgeschwindigkeit teilt. Diese durchschnittlichen oder approximierten Kosten im Modell können natürlich wegen der fehlenden Berücksichtigung von Stau, Ampeln, Vorfahrtsregeln etc. sehr von den Kosten in der Realität abweichen, was sich durch eine Modellierung, wie wir sie verwenden (keine Ampel-Daten, keine dynamischen Daten vom Verkehr vorliegend etc.) aber auch nicht vermeiden lässt. 3.3 Kürzeste Wege Dadurch, dass ein Straßennetz N = (V, E, S, T, w, s, t) ja in erster Linie ein Graph ist, lassen sich für diese – und so machen wir das auch – die gleichen Definitionen bzgl. kürzester Wege verwenden wie bei Graphen. So bezeichnen wir z.B. im Folgenden auch in Straßennetzen N mit δ(i, j) (oder um den Bezug zum jeweiligen Straßennetz / Graph klarzumachen mit δN(i, j) ) die (kürzeste) Distanz zwischen den Knoten i und j aus N. Dementsprechend lassen sich die gleichen Algorithmen zur Berechnung kürzester Wege wie die in Kapitel zwei angesprochenen verwenden, indem man einfach S, T, s und t ignoriert. Dass in Straßennetzen keine negativen Kanten vorkommen, vereinfacht das Ganze noch. Die in Kapitel zwei angesprochenen Algorithmen sind zum Teil schon sehr effizient und optimal von den Laufzeiten oder Speicherplatzbedürfnissen. Deshalb ist es nicht unmittelbar klar, ob es überhaupt „Sinn“ macht, sich Gedanken über weitere Algorithmen 1 Den Algorithmen und Datenstrukturen, die wir später zur Berechnung kürzester Wege herleiten, ist es natürlich „egal“, was die Funktion w von (V, E, S, T, w, s, t) in der Realität bedeutet. 32 oder vor allen Dingen Heuristiken, die dann nicht immer die wirklich kürzesten Wege ausgeben, zu machen. Es gibt aber, gerade bei der Routenplanung in realen Straßennetzen, Anwendungen, für die z.B. der Algorithmus von Dijkstra von der Laufzeit her zu ineffizient ist oder die Speicherung einer Floyd-Wege-Matrix zu viel Speicherplatz benötigt. Stephan Hasselberg nennt zu diesem Punkt in seiner Dissertation (siehe [HASS00], S. 4 f.) Verkehrsfluss-Simulationen als Beispiel. In diesen Simulationen wird der Verkehrsfluss in einem Straßennetz simuliert zum Zwecke der Grundlagenforschung und insbesondere um Verkehrsnetze in einem dynamischen Kontext mit Ampeln, wirklichen Verkehrteilnehmern etc. optimieren zu können. Bei diesen Simulationen wird eine Menge von Verkehrsteilnehmern z.T. individuell mit ihrem jeweiligen Verhalten simuliert. Eine solche Testsimulation für das – noch vergleichsweise kleine – Straßennetz der Stadt Wuppertal benötigte ca. 9000 Knoten und 17000 Kanten. In jedem Schritt dieser Simulation müssen im Worst-Case 500000 kürzeste-Weg-Berechnungen durchgeführt werden, welche auf den benutzten State-of-the-Art (zu dem Zeitpunkt) Workstations mehrere Stunden Zeit benötigten. 33 4 Variante der Floyd-Wege-Matrix für Straßennetze In Abschnitt 2.2.3 haben wir den Algorithmus von Floyd kennengelernt, welcher in O(n3) Zeit das alle-Paare-kürzeste-Weg-Problem löst. Dieser gibt zwei Matrizen D und Π zurück, welche in komprimierter Form die kürzesten Distanzen δ zwischen allen Knoten bzw. die kürzesten Wege speichern. Wenn nun viele Weganfragen für ein Straßennetz zu beantworten sind (wahrscheinlich dann auch über einen längeren Zeitraum), dann muss man natürlich nicht jedes Mal die Matrizen D und Π neu berechnen, sondern kann diese immer wieder benutzen (zumindest solange sich das Sraßennetz nicht ändert). So kann man dann, nachdem man einmal die Preprocessing-Zeit von Θ(n3) für die Berechnung von D und Π investiert und dauerhaft Θ(n2) Speicherplatz für diese reserviert hält, in jeweils Θ(|P|) = O(n) Zeit einen kürzesten Pfad P ermitteln bzw. in O(1) eine kürzeste Distanz. Inhalt dieses Kapitels und ein Schwerpunkt der Diplomarbeit ist es nun, die Θ(|P|) Zeit für eine kürzeste-Weg-Berechnung mit Hilfe von Π für bestimmte Anfragen noch zu verbessern. Dazu nehmen wir in Abschnitt 4.1 noch einmal die Floyd-Wege-Matrix, wie wir sie in Abschnitt 2.2.3 kennengelernt haben, unter die Lupe und untersuchen, ob sie die einzige derartige Möglichkeit zur Speicherung kürzester Wege eines Graphen / Straßennetzes ist. Im darauf folgenden Abschnitt leiten wir eine Definition für eine bestimmte Variante der Floyd-Wege-Matrix her, welche die für unsere Zwecke optimale Struktur hat. Abschnitt 4.3 beschreibt dann einen Algorithmus, welcher eine normale Floyd-Wege-Matrix in die in Abschnitt 4.2 definierte Variante transformiert. Die Idee zu der Variante der Floyd-Wege-Matrix stammt von Prof. Plümer. Sonstige Literatur wurde nicht benutzt. 4.1 Die Floyd-Wege-Matrix genauer betrachtet Im ganzen Kapitel 4 sei (V, E, S, T, w, s, t) das zugrundeliegende Straßennetz. Schauen wir uns nochmal an, wie wir kürzeste Wege in der Floyd-Wege-Matrix – so wie sie in Kapitel 2.2.3 vorgestellt wurde – gespeichert werden (Tab. 2). Ist dies die einzige Möglichkeit, alle kürzesten Wege in einer Matrix zu speichern? Nein, denn wenn man im Algorithmus FLOYD die Anweisung „πij := πkj;” durch “πij := πik” ersetzt und im Initialisierungsteil πij := j statt πij := i, falls wij < ∞ und i ≠ j, dann wird bei einem Matrix-Eintrag πij nicht der letzte Knoten vor j auf einem kürzesten Pfad von i nach j gespeichert, sondern der erste nach i. Siehe dazu Tab. 3. 34 πij 0 1 2 3 4 5 6 7 8 9 0 - 1 0 - 2 1 1 - 3 4 5 6 2 7 6 8 9 2 - 6 - Tab. 2: Floyd-Wege-Matrix für einen Pfad von 0 nach 7. Diese Matrix ist die gleiche wie die aus Tab. 1, nur der Einfachheit halber auf die den kürzesten Pfad P = [0, 1, 2, 6, 7] betreffenden Einträge beschränkt. πij 0 1 2 3 4 5 6 7 8 9 0 - 1 1 - 2 3 4 5 2 - 6 6 7 1 2 6 8 9 - 7 - Tab. 3: Eine Variante der Floyd-Wege-Matrix für einen Pfad von 0 nach 7. Diese Matrix speichert den gleichen kürzesten Pfad P = [0, 1, 2, 6, 7] wie die in Tab.2, nur auf eine andere Weise. Man kann sich leicht überlegen, dass man im Algorithmus FLOYD die Anweisung „πij := πkj;” durch die allgemeinere “πij := (ein Knoten echt innerhalb des gerade betrachteten momentan kürzesten Pfades von i nach j über k)” ersetzen kann, welche die beiden obigen als Spezialfälle beinhaltet. Dann bedeutet ein Matrix-Eintrag πij = k, dass der in der Matrix gespeicherte kürzeste Pfad von i nach j irgendwie über den Knoten k geht. Falls der kürzeste Weg von i nach j über nur genau eine Kante geht, würde man wohl πij = i oder πij = j (oder irgendetwas eindeutiges Anderes) setzen bzw. definieren. Betrachten wir als Beispiel folgende Matrix (Tab. 4): 35 πij 0 1 2 3 4 5 6 7 8 9 0 - 1 0 - 2 1 1 - 3 4 5 6 7 2 2 6 - 6 - 8 9 - - Tab. 4: Eine weitere Variante der Floyd-Wege-Matrix für einen Pfad von 0 nach 7. Diese Matrix speichert den gleichen kürzesten Pfad P = [0, 1, 2, 6, 7] wie die in Tab.2 und 3, nur auf eine andere Weise. Die in einer solchen Matrix Π gespeicherten Wege lassen sich durch folgende Art von Baum gut veranschaulichen: Ein geordneter Binärbaum T heißt Matrix-Wegbaum für einen Pfad von u nach v bzgl. Matrix Π oder, kürzer geschrieben, (Π, u, v)-Wegbaum gdw. eine der folgenden Bedingungen zutrifft 1: • T besteht nur aus einem Blatt, das mit „u – v, u“ oder „u –v, v“ beschriftet ist und es gilt π uv = u bzw. π uv = v • T hat Wurzel w, die mit „u – v, x“ beschriftet ist, und es gilt π uv = x . Zusätzlich ist der linke Unterbaum von w ein (Π, u, x)-Wegbaum und der rechte Unterbaum von w ein (Π, x, v)-Wegbaum Siehe Abb. 6 – 8 als Beispiele für Matrix-Wegbäume. Man sieht in den Beispielen, dass die Blätter der Matrix-Wegbäume gewissermaßen redundant sind, weil ein Matix-Wegbaum ohne diese Blätter genauso aussagekräftig ist. Wir werden sie aber trotzdem noch in jedes Beispiel einzeichnen, um Verwirrungen vorzubeugen. In den allermeisten nachfolgenden Überlegungen kann man sie aber ignorieren. Wir sagen, ein Knoten x aus T (bzw. der dazu korrespondierende Matrix-Eintrag), beschriftet mit „a – b, c“, korrespondiert zu einem Knoten y aus dem zugrundeliegenden Straßennetz genau dann, wenn y = c. 1 Die Beschriftungen der Knoten sind etwas redundant, ein einfaches „x“ statt „u – v, x“ würde (zumindest im Kontext eines gesamten Baumes) genauso aussagekräftig sein. Aber diese aufwendigere Beschriftung ist m.E. leichter verständlich. 36 0-7, 6 0-6, 2 0-2, 1 0-1, 0 6-7, 6 2-6, 2 1-2, 1 Abb. 6: Matrix-Wegbaum für Matrix auf Tab. 2. Diese Abbildung stellt einen Matrix-Webbaum für den Pfad von 0 nach 7 bzgl. der Matrix aus Tab. 2 dar. 0-7, 1 0-1, 1 1-7, 2 1-2, 2 2-7, 6 2-6, 6 6-7, 7 Abb. 7: Matrix-Wegbaum für Matrix aus Tab. 3. Zu sehen ist ein Matrix-Wegbaum für den Pfad von 0 nach 7 bzgl. der Matrix aus Tab. 3. In den vorherigen drei Matrizen und Abbildungen haben wir gesehen, dass alle drei Darstellungen desselben kürzesten Weges in der Floyd-Wege-Matrix möglich sind. Vielleicht lässt sich dieser Freiheitsgrad ja für Straßennetze benutzen, um besonders relevante Informationen eines oder mehrerer (kürzester) Wege durch das Auslesen möglichst weniger Matrixeinträge zu erhalten. Dies wollen wir im nächsten Abschnitt untersuchen. 37 0-7, 2 0-2, 1 0-1, 0 2-7, 6 1-2, 1 2-6, 2 6-7, 6 Abb. 8: Matrix-Wegbaum für Matrix aus Tab. 4. Hierbei handelt es sich um den Matrix-Wegbaum für den Pfad von 0 nach 7 bzgl. der Matrix aus Tab. 4 4.2 Variante der Floyd-Wege-Matrix für Straßennetze Im letzten Abschnitt haben wir gesehen, dass es Freiheitsgrade bei der Repräsentation kürzester Wege in der Floyd-Wege-Matrix gibt. Diese möchten wir dazu benutzen, um für bestimmte Anwendungen oder Anfragen bessere Laufzeiten dadurch zu erreichen, dass wir die wichtigen Knotenpunkte der kürzesten Wege möglichst „weit oben“ in der FloydWege-Matrix speichern. Was für Knotenpunkte auf (kürzesten) Wegen in einem Straßennetz sind normalerweise besonders wichtig? Wenn man einem Menschen einen (kürzesten) Weg irgendwohin beschreibt, dann sagt man ihm etwas wie z.B.: „Fahre die Museumstraße entlang, dann biege in die Schloßallee ein, dann fährst du auf die Autobahn A3 auf, bei Abfahrt Opernplatz runter von der Autobahn, auf die Parkstraße, ...“. Den Menschen interessiert also meistens nur, wann die Straße gewechselt wird, und nicht jede einzelne Kante, weil er ja anhand der Straßenschilder die Straßen(-namen) erkennen kann. Es gibt auch viele Anfragen, für welche die Knoten auf einem kürzesten Weg, bei denen sich die Straße ändert, wichtiger sind. Es wäre nun schön, wenn sich Punkte x auf den Pfaden P von u nach v, wo von einer Straße auf eine andere gewechselt wird, möglichst weit oben im entsprechenden MatrixWegbaum von einem dazu korrespondierenden Knoten (also einem Knoten mit Markierung „u – v, x“) vertreten sind, weil man diese so schneller ausgeben kann bzw. findet. Dies wollen wir nun formal auf den Punkt bringen: Sei P = [v0, v1,..., vk-1, vk] ein Pfad in einem Straßennetz (V, E, S, T, w, s, t). Einen inneren Knoten vi auf P, also vi ∈ P, i ≠ 0, i ≠ k, nennen wir Straßenwechselpunkt oder Straßenwechselknoten bzgl. P gdw. s((vk-1, vk)) ≠ s((vk, vk+1)). Punkte auf Wegen, bei denen sich der Straßentyp ändert (z.B. bei Autobahnauffahrten / -abfahrten), sind ebenfalls wichtig: Einen inneren Knoten vi auf P, also vi ∈ P, i ≠ 0, i ≠ k, nennen wir Straßentypwechselpunkt oder Straßentypwechselknoten bzgl. P gdw. t(s((vk-1, vk))) ≠ 38 t(s((vk, vk+1))). Man sieht, dass ein Straßentypwechselknoten auch immer ein Straßenwechselknoten ist, aber nicht unbedingt umgekehrt. Siehe dazu Abb. 9 als Beispiel: Landstraße L1 0 Landstraße L2 1 Autobahn A1 2 3 Autobahn A2 4 5 Abb. 9: Ein Beispiel-Weg zur Veranschaulichung der Straßen(typ)wechselpunkte. Auf diesem Pfad P = [0, 1, 2, 3, 4, 5] sind die Knoten 1, 2 und 4 Straßenwechselpunkte bzgl. P. Der Knoten 2 ist darüber hinaus noch ein Straßentypwechselpunkt bzgl. P. Wenn man es bei allen kürzesten Wegen P schafft, dass alle Straßenwechselknoten und Straßentypwechselknoten des jeweiligen Weges (genauer gesagt, die dazu korrespondierenden Knoten) „ganz oben“ in ihren jeweiligen Matrix-Wegbäumen sind, dann kann man Anfragen1 wie z.B. „Finde alle Straßen auf dem kürzesten Weg von i nach j“, „Geht der kürzeste Weg von i nach j über eine Autobahn?“ oder „Was ist die Autobahnauffahrt / -abfahrt auf dem kürzesten Weg von i nach j?“ in jeweils Zeit O(Anzahl Straßenwechselpunkte vom kürzesten Weg P von i nach j) herausfinden, was z.B. gerade bei vielen redundanten Kanten (siehe Abschnitt 3.2.2) im Straßennetz deutlich weniger sein kann als Θ(|P|). Das „ganz oben“ gilt es nun zu formalisieren: Sei T ein Matrix-Wegbaum für einen Pfad P von u nach v bzgl. Matrix Π und v ein innerer Knoten von T (also v sei kein Blatt). Wir nennen v einen Verstoß auf Klassenebene (bzgl. T) oder Verstoß vom Typ A genau dann, wenn v nicht zu einem Straßentypwechselknoten in P korrespondiert, aber sich im linken oder rechten Unterbaum von v ein innerer Knoten befindet, der zu einem Straßentypwechselknoten korrespondiert. Wir nennen v einen Verstoß auf Instanzebene (bzgl. T) oder Verstoß vom Typ B genau dann, wenn v nicht zu einem Straßenwechselknoten in P korrespondiert, aber sich im linken oder rechten Unterbaum von v ein innerer Knoten befindet, der zu einem Straßenwechselknoten korrespondiert. Man sieht, dass Verstöße vom Typ A Spezialfälle von denen vom Typ B sind. Mit der Anzahl der Verstöße A bzw. B eines (Π, u, v)-Wegbaum T bezeichnen wir (natürlich) die Anzahl der Knoten aus T, die Verstöße vom Typ A bzw. B sind. Wir sagen ein (Π, u, v)-Wegbaum T hat (für uns) optimale Struktur genau dann, wenn die Anzahlen der Verstöße auf Klassenebene als auch die Anzahl der Verstöße auf Instanzebene gleich 0 sind. Wenn dies für einen (Π, u, v)-Wegbaum T für einen Pfad P (von u nach v) zutrifft, dann sind nämlich ganz oben im Baum die Knoten, die zu den Straßentypwechselknoten 1 Hierzu sind teilweise noch zusätzliche Verpointerungen / Referenzen und Informationen in den Datenstrukturen notwenig. Diese fallen aber zumindest asymptotisch nicht ins Gewicht. 39 von P korrespondieren und etwas weiter unten, aber immer noch so weit oben wie möglich, die Knoten, die zu den Straßenwechselknoten von P korrespondieren. Hierzu beispielhaft zwei Matrix-Wegbäume für den Pfad aus Abb. 9: 0-5, 3 0-3, 1 0-1, 0 3-5, 4 1-3, 2 1-2, 1 3-4, 3 4-5, 4 2-3, 2 Abb. 10: Ein möglicher Matrix-Wegbaum den Pfad aus Abb. 9. Dieser Matrix-Wegbaum enthält 2 Verstöße vom Typ A („0-5, 3“, „0-3, 1“) und 2 Verstöße vom Typ B („0-5, 3“, „0-3“, 1“). 0-5, 2 0-2, 1 0-1, 0 2-5, 4 1-2, 1 4-5, 4 2-4, 3 2-3, 2 3-4, 3 Abb. 11: Ein weiterer möglicher Matrix-Wegbaum den Pfad aus Abb. 9. Dieser Matrix-Wegbaum enthält keinerlei Verstöße und hat demnach optimale Struktur. Bis jetzt haben wir definiert, wann ein einzelner Matrix-Wegbaum (für uns) optimale Struktur hat. Zu definieren ist noch, wann eine ganze (Floyd-Wege-)Matrix, die ja viele eineinander verschachtelte Matrix-Wegbäume enthält, (für uns) optimale Struktur hat: Eine kürzeste Wegematrix Π hat (für uns) optimale Struktur genau dann, wenn für alle i, j 40 aus dem zugrundeliegenden Straßennetz N gilt: Der (Π, i, j)-Wegbaum hat optimale Struktur. Nachdem wir definiert haben, was wir unter einer optimalen Struktur einer Wege-Matrix verstehen, ist noch nicht klar, ob es überhaupt eine Wege-Matrix gibt, die eine solche Struktur hat. Zwar ist es leicht zu sehen, dass man einen einzelnen Pfad immer mit einem (kürzesten) Matrix-Wegbaum darstellen kann, der optimale Struktur hat, aber eine kürzeste-Wege-Matrix enthält ja sehr viele solcher Pfade, die jeweils Teilpfade voneinander sind. Inhalt des nächsten Abschnittes ist ein Algorithmus, der es schafft, eine gegebene Floyd-Wege-Matrix in eine gewissermaßen äquivalente zu transformieren, die aber optimale Struktur hat (und damit ein konstruktiver Beweis für die Bejahung obiger Frage ist). 4.3 Algorithmus zur Transformierung der Floyd-Wege-Matrix Folgender Algorithmus erwartet als Eingabe eine Floyd-Wege-Matrix Π und ein zugrundeliegendes Straßennetz bzw. sonstige Informationen über die Straßentypwechselpunkte und Straßenwechselpunkte von den in der Floyd-Wege-Matrix gespeicherten kürzesten Wege. Ausgegeben wird eine Matrix, welche immer noch alle kürzesten Wege enthält, aber optimale Strukur hat: Algorithmus: FLOYD-WEGE-MATRIX-OPTIMIERUNG Eingabe: a) Straßennetz N = (V, E, S, T, w, s, t) mit V = {1, ..., n} b) Floyd-Wege-Matrix Π für das obige Straßennetz Ausgabe: Eine Matrix Π’ , welche (wie Π) kürzeste Wege von N enthält und optimale Struktur hat Methode: for i := 1 to n do for j := 1 to n, j ≠ i do Ermittle den kürzesten Pfad P von i nach j in Π; if |P| ≤ 1 then π 'ij := π ij ; else if (es gibt einen Straßentypwechselknoten k auf P) then π 'ij := k ; else if (es gibt einen Straßenwechselknoten k auf P) then 41 π 'ij := k ; else π 'ij := irgendein Knoten k echt innerhalb von P; fi fi fi od od return Π’; ڤ Bevor wir uns Gedanken um die Korrektheit des Algorithmus machen, betrachten wir ein Beispiel (Abb. 12 und Tab. 5). Es ist nicht besonders schwer zu sehen, dass der Algorithmus, angewendet auf nur einen Pfad und alle seine Teilpfade, funktioniert, da er ziemlich direkt die Definition der Verstöße und der optimalen Struktur benutzt. Dass dies aber auch bei einer ganzen FloydWege-Matrix funktioniert, ist nicht so klar. Dazu eine neue Definition: Einen Eintrag πij = k in der Floyd-Wege-Matrix nennen wir Wurzelverstoß auf Klassenebene oder Wurzelverstoß vom Typ A genau dann, wenn im (Π, i, j)-Wegbaum die Wurzel ein Verstoß vom Typ A ist. Analog definieren wir einen Wurzelverstoß auf Instanzebene oder Wurzelverstoß vom Typ B. Es zeigt sich nun, dass diese Wurzelverstöße mit der Optimalität der Floyd-Wege-Matrix stark korreliert sind: Lemma 10: Sei (V, E, S, T, w, s, t) ein Straßennetz und Π eine Floyd-Wege-Matrix kürzester Wege dafür. Falls Π keine Wurzelverstöße vom Typ A und keine Wurzelverstöße vom Typ B enthält, dann hat Π optimale Struktur. Beweis: Zu zeigen ist, dass für alle i, j gilt: Der (Π, i, j)-Wegbaum hat keine Verstöße vom Typ A und keine Verstöße vom Typ B. Dazu schauen wir uns für beliebiges i und j (i ≠ j) den (Π, i, j)-Wegbaum an: Die Wurzel des Baumes ist mit „i – j, k“, der linke Sohn der Wurzel mit „i – k, x“ und der rechte Sohn mit „k – j, y“ beschriftet. Nach Vorraussetzung ist die Wurzel kein Verstoß vom Typ A oder B. Der linke Sohn ist Wurzel des (Π, i, k)-Wegbaumes und ist deswegen auch kein Verstoß vom Typ A oder B. Gleiches gilt für den rechten Sohn und die Söhne der Söhne und damit allen Knoten des (Π, i, j)-Wegbaumes. 42 ڤ 1 4 5 Landstraße L3 2 1 Autobahn A2 3 2 9 1 3 2 1 Autobahn A1 2 1 1 Landstraße L2 6 3 3 8 8 Landstraße L1 7 0 Abb. 12: Beispiel-Straßennetz. Dieses Straßennetz ist eine Erweiterung des Graphen aus Abb. 2. Die Beschriftungen innerhalb der Knoten sind die Knotenbezeichnungen und die Beschriftungen an den Kanten e stellen die Kantengewichte w(e) dar. Die Farben, zusammen mit der entsprechenden Beschriftung, kennzeichnen die Straßen und Straßentypen. πij 0 1 2 3 4 5 6 7 8 9 0 1 1 1 1 1 1 1 1 1 1 0 2 2 2 2 2 2 2 2 2 1 1 3 3 3 6 6 9 9 3 2 2 2 4 4 2 2 2 2 4 3 3 3 3 5 3 3 5 5 5 9 9 9 4 4 9 9 9 9 6 2 2 2 2 2 2 7 7 2 7 6 6 6 6 6 6 6 8 6 8 9 9 9 9 9 9 7 7 9 πij’ 0 1 2 3 4 5 6 7 8 9 9 2 2 2 2 2 2 2 2 8 - 0 1 1 2 2 4 2 2 9 1 1 0 2 2 2 4 2 2 9 2 2 1 1 3 3 4 6 6 9 9 3 2 2 2 4 4 2 2 9 2 4 2 2 3 3 5 2 2 9 5 5 9 9 9 4 4 2 2 9 9 6 2 2 2 2 2 4 7 7 2 7 2 2 6 2 2 4 6 8 2 8 9 9 9 9 9 4 7 7 9 9 2 2 2 2 2 4 2 2 8 - Tab. 5: Anwendung des Algorithmus FLOYD-WEGE-MATRIX-OPTIMIERUNG auf die Floyd-WegeMatrix zu Abb. 12. Die linke Matrix ist die Floyd-Wege-Matrix Π für das Straßennetz aus Abb. 12. Die rechte ist die vom Algorithmus FLOYD-WEGE-MATRIX-OPTIMIERUNG zurückgegebene Matrix Π’, welche im Gegensatz zu Π optimale Struktur hat (und immer noch die kürzesten Wege von Abb. 12 speichert). Nun schauen wir uns die Eigenschaften der zurückgegebenen Matrix Π’ an: Lemma 11: Sei (V, E, S, T, w, s, t) ein Straßennetz und Π eine Floyd-Wege-Matrix kürzester Wege dafür. Bei Eingabe Π und (V, E, S, T, w, s, t) gilt nach Terminierung für die vom 43 Algorithmus Folgendes: FLOYD-WEGE-MATRIX-OPTIMIERUNG zurückgegebene Matrix Π’ a) Π’ enthält wie Π alle kürzesten Wege für (V, E, S, T, w, s, t) b) Π’ enthält keine Wurzelverstöße vom Typ A und keine Wurzelverstöße vom Typ B Beweis: Zu a) Diese Behauptung zeigen wir durch Induktion über die Anzahl m der Zuweisungen π’ij := k für eine Matrix Π* , welche folgendermaßen definiert ist: π*ij = π’ij falls π’ij schon definiert und π*ij = πij sonst. Da nach der letzten solchen Zuweisung sicherlich Π* = Π’ gilt, zeigt dies die Behauptung. Induktionsanfang: Bei m = 0 ist Π* = Π und damit die Behauptung erfüllt Induktionsannahme: Die Behauptung gilt für m = l – 1 Induktionsschritt: Vor der m = l. Zuweisung π’ij := k (bzw. π*ij := k) galt π’ij := k’ (bzw. π*ij := k’) und der in Π* gespeicherte kürzeste Pfad P’ von i nach j (Induktionsannahme) hatte die Struktur P’ = P1’, P2’, P3’, wobei wegen k, k’ ∈ P’ entweder i) P1’ = [i, ..., k’] ein (kürzester) Pfad von i nach k’ ist, P2’ = [k’, ..., k] ein (kürzester) Pfad von k’ nach k ist und P3’ = [k, ..., j] ein (kürzester) Pfad von k nach j ist oder ii) P1’ = [i, ..., k] ein (kürzester) Pfad von i nach k ist, P2’ = [k, ..., k’] ein (kürzester) Pfad von k nach k’ ist und P3’ = [k’, ..., j] ein (kürzester) Pfad von k’ nach j ist. Wir betrachten nur Fall i), weil Fall ii) symmetrisch dazu ist: Nach der m = l. Zuweisung π’ij := k (bzw. π*ij := k) hat der in Π* gespeicherte kürzeste Pfad P von i nach j die Struktur P = P1, P2, wobei wegen der Induktionsvorraussetzung P1 = [i, ..., k] ein kürzester Pfad von i nach k ist und P2 = [k, ..., j] ein kürzester Pfad von k nach j ist. Daraus folgt, dass w(P1) ≤ w(P1’) + w(P2’) und w(P2) ≤ w(P3’) und dadurch w(P) ≤ w(P’). Zu b) Offensichtlich ist diese Behauptung erfüllt (siehe Definition und Algorithmus). ڤ 44 Aus den beiden vorherigen Lemmata folgt nun ziemlich direkt die Korrektheit vom Algorithmus FLOYD-WEGE-MATRIX-OPTIMIERUNG: Satz 3: Der Algorithmus FLOYD-WEGE-MATRIX-OPTIMIERUNG ist korrekt und in Zeit Θ(n2α) implementierbar, wobei α die durchschnittliche ungewichtete Länge eines kürzesten Pfades in dem Eingabe-Straßennetz ist. Beweis: Zur Korrektheit ist zu zeigen, dass nach einer Eingabe, welche die Eingabe-Spezifikation erfüllt, der Algorithmus mit der im Ausgabe-Teil des Algorithmus beschriebenen Ausgabe terminiert. Dies folgt direkt aus den Lemmata 10 und 11. Die Laufzeit ist Θ(n2α), weil wir für die Bestimmung des kürzesten Pfades P bzw. der Straßenwechselknoten und Straßentypwechselknoten von P jeweils Zeit Θ(|P|) brauchen – also im Durchschnitt (arithmetisches Mittel) Θ(α) bei insgesamt n2 (genau genommen bei entsprechender Implementation n2 – n) kürzesten Pfaden. ڤ Da die durchschnittliche ungewichtete Länge eines Pfades sicher kleiner n ist, ist die Laufzeit natürlich auch O(n3). Wie lang diese Länge im Einzelfall tatsächlich ist, hängt vom betrachteten Graphen / Straßennetz ab. In diesem Abschnitt haben wir also gesehen, wie wir eine Floyd-Wege-Matrix in O(n3) so transformieren können, dass sie die im vorherigen Abschnitt definierte optimale Struktur hat. Im nächsten Abschnitt wollen wir uns eine Beispiel-Anwendung anschauen, die schon etwas mit dem Kapitel 5 zu tun und auch zu den Überlegungen in jenem Kapitel geführt hat. 4.4 Beispielanwendung Die Variante der Floyd-Wege-Matrix, die wir in den vorherigen Abschnitten kennen gelernt haben, speichert alle kürzesten Wege derart, dass wir bei bestimmten kürzesteWeg-Anfragen an sie nicht immer den ganzen Weg (jeden Knoten bzw. Kante) betrachten bzw. ausgeben müssen. Wenn man z.B. nur eine Sequenz der verschiedenen Straßen eines kürzesten Weges P in ihrer Vorkommensreihenfolge erfahren möchte (so ähnlich beschreibt man einem Menschen einen Weg normalerweise), dann lässt diese Anfrage in Zeit Θ(Anzahl Straßenwechselpunkte) anstatt in Θ(|P|) beantworten. Jetzt wollen wir uns eine komplexere Anwendung anschauen, welche schon etwas mit dem nächsten Kapitel zu tun hat und auch Auslöser für die dortigen Überlegungen und dementsprechend dem zweiten Schwerpunkt dieser Diplomarbeit war. Die Idee zu dieser Anwendung kommt auch von Prof. Plümer. 45 Zur Beschreibung dieser Anwendung brauchen wir zuerst eine Definition, um Missverständisse zu vermeiden. Sei N = (V, E, S, T, w, s, t) ein Straßennetz. Wir nennen ein Straßennetz N’ = (V’, E’, S’, T’, w’, s’, t’) Straßenunternetz1 von N genau dann, wenn • V’ ⊆ V • E’ ⊆ E und für alle (u, v)∈E’ gilt: u∈V’ und v∈V’ • S’ ⊆ S • T’ ⊆ T • w’(e) = w(e) für alle e ∈ E’ • s’(e) = s(e) für alle e ∈ E’ • t’(e) = t(e) für alle e ∈ E’ Ein Straßenunternetz ist also nichts Anderes als einfach ein Teil eines Straßennetzes. Im Folgenden stellen wir noch zweierlei Anforderungen an das Straßennetz. Zum einen gebe es immer maximal einen kürzesten Weg zwischen zwei (verschiedenen) Punkten des Straßennetzes (siehe dazu eine spätere Fußnote). Zum anderen (und an sich auch o.B.d.A.) gebe es in unserem Straßennetz nur zwei Typen von Straßen: Landstraßen und Autobahnen. Also T = {Landstraße, Autobahn}. Betrachten wir jetzt ein bestimmtes Unternetz von N, das sogenannte Autobahnnetz A, welches aus den Autobahnkanten und dazu gehörenden Knoten besteht. Also A = (VA, EA, SA, {Autobahn}, wA, sA, tA) mit EA der Menge aller Kanten e, welche zu Straßen vom Typ Autobahn gehören, also t(s(e)) = Autobahn, und der Menge VA aller Knoten v, welche zu Kanten aus EA inzident sind. Die Funktionen wA, sA, tA sind wie w, s bzw. t, nur entsprechend auf die Untermenge VA von V beschränkt. Angenommen, man würde das Autobahnnetz separat vom Gesamt-Straßennetz N speichern und es dazu benutzen wollen, kürzeste Wege (bzgl. N) zwischen Knoten des Autobahnnetzes zu finden, dann gibt es Fälle, bei denen denen dies nicht klappt, weil der kürzeste Weg zwischen den Autobahnknoten nicht über das Autobahnnetz geht, sondern über Landstraßen. Hierzu ein Beispiel aus der realen Welt (Abb. 13): 1 Zur Definition des Straßenunternetzes ist zu bemerken, dass sie nicht mit der späteren Definition von Straßensubnetz zu verwechseln ist. 46 Abb. 13 Straßenkarten-Ausschnitt in der Nähe von Mayen. Hier ist ein Ausschnitt einer handelsüblichen Straßenkarte. Was hieran interessant ist, ist dass der kürzeste Weg von der A48, Abfahrt Mayen, zur A61, Abfahrt Mendig, nicht über die beiden Autobahnen geht (das ist sowohl von der geometrischen Länge als auch von der Zeit her ein großer Umweg), sondern ab der Abfahrt Mayen über die B262 und dann weiter nördlich über weitere Bundesstraßen verläuft. Wir wollen nun folgendes Problem mit Hilfe der Variante der Floyd-Wege-Matrix (effizienter als ohne) lösen: Die Erweiterung des Autobahnnetzes A in A’ um eine (von der Kardinalität her) minimale Anzahl von Kanten des Typs Landstraße aus N, so dass jeder kürzeste Pfad in N zwischen zwei Knoten des ursprünglichen Autobahnnetzes A auch in dem erweiterten Autobahnnetz A’ liegt. Dann kann man nämlich A’ (welches wahrscheinlich deutlich kleiner als N sein wird) zur Berechnung kürzester Pfade zwischen Autobahnknoten benutzen. Folgender Algorithmus löst genau dieses Problem mit der Variante der Floyd-Wege-Matrix: Algorithmus: AUTOBAHNNETZ_ERWEITERUNG Eingabe: a) Straßennetz N = (V, E, S, T, w, s, t) mit V = {1, ..., n} mit i) Es gibt maximal einen kürzesten Weg zwischen zwei verschiedenen Knoten ii) T = {Landstraße, Autobahn} b) Variante der Floyd-Wege-Matrix Π für das obige Straßennetz 47 Ausgabe: Eine von der Kardinalität her minimale Liste L von Landstraßen-Kanten, so dass, wenn man das Autobahnnetz um diese (und um die (End-)Knoten der Kanten) erweitert, jeder kürzeste Weg zwischen zwei Knoten des ursprünglichen Autobahnnetzes innerhalb des erweiterten liegt.1 Methode: L := Ø; for all (i, j)∈{(u, v)∈V× V | u ist inzident zu einer Autobahn-Kante, v ist inzident zu einer Autobahn-Kante und u ≠ v} do if ( π ij = i) // besteht der kürzeste Pfad von i nach j aus nur einer Kante? then e := diese Kante (i, j); if (e ist Landstraßen-Kante) then L := L ∪ {e}; fi else if ( π ij ist nicht inzident zu einer Autobahn-Kante) // ⇒ nur Landstraßen- // kanten auf dem Weg von // i nach j in Π (*) then L := L ∪ {alle Kanten e auf dem Pfad in Π von i nach j} fi fi od return L; ڤ Nun wollen wir die Korrektheit und Laufzeit dieses Algorithmusses zeigen: Satz 4: Der Algorithmus AUTOBAHNNETZ-ERWEITERUNG ist korrekt und in Zeit O(n2) implementierbar. 1 Falls im betrachteten Straßennetz nicht die Eigenschaft a) i) erfüllt ist, dann hat die Liste L nicht notwendigerweise minimale Kardinalität, weil die (Variante der) Foyd-Matrix die „falschen“ bzw. für diesen Algorithmus ungünstigen kürzesten Wege gespeichert haben kann. Dies sind kürzeste Wege, die über Landstraßen-Kanten gehen, obwohl es gleich schnelle nur über Autobahn-Kanten oder über weniger Landstraßen-Kanten gibt 48 Beweis: Zur Korrektheit zeige ich folgendes: (i) an Stelle (*) im Algorithmus gilt: π ij ist nicht inzident zu einer Autobahn-Kante (A) ⇒ es gibt nur Kanten vom Typ Landstraße auf dem kürzesten Weg von i nach j in Π (B). (ii) die Kanten eines jeden kürzesten Pfades P zwischen zwei Knoten i, j, die jeweils inzident zu einer Autobahnkante sind, sind in nach Beendigung des Algorithmus in L enthalten. Zu (i) Ich zeige nun, dass aus der Gültigkeit von (A) (B) folgt. Da Π optimale Struktur hat, ist, wenn möglich, ein π ij Straßentypwechselpunkt. In dem Fall wäre aber π ij inzident zu einer Autobahn-Kante. Also kann π ij kein Straßentypwechselpunkt sein. Daraus folgt, dass auf dem ganzen Pfad von i nach j nur der gleiche Straßentyp vorliegt. Da π ij eben zu keiner Autobahnkante inzident ist, kann es sich dabei nur um Landstraßen handeln. Zu (ii) Sei P ein kürzester Pfad zwischen zwei Knoten i, j, die jeweils inzident zu einer Autobahn-Kante sind. Dieser Pfad lässt sich in Teilpfade aufteilen, so dass diese jeweils mit einem zu einer Autobahn-Kante inzidenten Knoten beginnen und mit einem solchen Knoten enden und kein innerer Knoten dieser Pfade zu einer Autobahnkante inzident ist. Alle diese Teilpfade werden irgendwann im Verlauf des Algorithmus betrachtet und zu L hinzugefügt. Die Laufzeit ergibt sich aus folgenden Tatsachen: a) man kann in O(n2) Zeit herausfinden kann, welche Knoten zu Autobahn-Kanten inzident sind b) die Schleife wird O(n2) mal durchlaufen c) Die einzigen Teile innerhalb der Schleife, die nicht O(1) Zeit benötigen, lassen sich den einzelnen Kanten zuordnen. Jede dieser O(n2) Kanten wird maximal einmal zugeordnet. ڤ 49 50 5 Anwendung der Variante der Floyd-Wege-Matrix in einem hierarchischen Strassennetz Im Kapitel 4 haben wir eine Variante der Floyd-Wege-Matrix kennengelernt, welche es uns ermöglicht, bestimmte Anfragen an ein Straßennetz bzgl. kürzester Wege effizienter zu beantworten als dies mit der einfachen Floyd-Wege-Matrix möglich ist. Bei der Suche nach Anwendungen kam die Idee, diese zu verwenden, um aus einem gewissermaßen „flachen“ Straßennetz (so wie wir es bisher immer betrachtet haben) ein hierarchisches Straßennetz zu generieren, mit dessen Hilfe sich kürzeste-Weg-Anfragen in kurzer Zeit (normalerweise schneller als Dijkstra) und bei vertretbaren Speicherplatzaufwand (normalerweise weniger als Floyd) beantworten lassen. Für dieses Verfahren leiten wir zwei Algorithmen her: Der eine generiert uns aus einem gegebenen, „normalen“ Straßennetz ein hierarchisches unter Verwendung einer Heuristik, welche Verwaltungstrukturen aus der realen Welt wie Landkreise und die gegebene Hierarchie des Straßennetzes (Landstraßen, Bundestraßen, Autobahnen, etc.) ausnutzt (Abschnitt 5.2). Der andere benutzt ein solches hierarchisches Straßennetz dann zur Bestimmung exakter kürzester Wege (Abschnitt 5.1). Im Abschnitt 5.2.1 untersuchen wir Optimierungskriterien, welche solche hierarchischen Straßennetze möglichst erfüllen sollten. In Abschnitt 5.2.3 betrachten wir konkrete Worst-Case Fälle für die Heuristik und einen Best-Case-Fall, der hoffentlich bei Anwendung der Heuristik auf reale Straßennetze – im Gegensatz zu konstruierten worst-case-Beispielen – eher die Regel ist. In diesem Kapitel betrachten wir die Verwendung der Floyd-Wege-Matrix in zweierlei Hinsicht. Zum einen untersuchen wir die Verwendung des Algorithmus AUTOBAHNNETZ_ERWEITERUNG in der Heuristik zur Generierung des hierarchischen Straßennetzes (Abschnitt 5.2), welche sich als nicht unbedingt vorteilhaft, aber auch nicht unbedingt nachteilhaft erweist. Zum anderen benutzen wir sie später als Datenstruktur in dem hierarchischen Straßennetz, wo sie definitiv Sinn macht. In diesem Kapitel wurde keine spezielle Literatur benutzt. 5.1 Grundmodell In diesem Abschnitt betrachten wir ein Verfahren, wie man kürzeste Wege in einer bestimmten Art von hierarchischem Straßennetz (welches wir auch in diesem Kapitel definieren werden) berechnen kann. Dieses Verfahren garantiert optimale Lösungen kürzester-Weg-Anfragen (ist also in dieser Hinsicht keine Heuristik). Je nachdem, welche 51 Datenstrukturen man benutzt, ergeben sich unterschiedliche Laufzeiten und Speicherplatzanforderungen. Wie man so ein hierarchisches Straßennetz aus einem Straßennetz aus der realen Welt erhält, ist Thema von Abschnitt 5.2. 5.1.1 Idee Dem in diesem Kapitel betrachteten Verfahren zur Berechnung kürzester Wege liegt folgende Idee zugrunde: Angenommen, man möchte z.B. mit dem Auto vom Hauptgebäude der Universität von Bonn nach Hamburg zum Hauptgebäude der dortigen Univerität fahren. Dann sieht der kürzeste Weg dahin so aus: Man fährt zuerst über einige Straßen niederer Ordnung, dann auf eine Autobahn, danach – den größten Teil der Strecke – über diverse Autobahnen und zuletzt von der Autobahn wieder herunter auf Straßen niederer Ordnung bis zur Universität in Hamburg. Angenommen, jeder kürzester Weg hätte diese Struktur, dann könnte man das GesamtStraßennetz Deutschlands in verschiedene Regionen1 und ein Autobahn(unter)netz aufteilen und kürzeste Wege nur mit Hilfe der Region des Startknotens des Weges, der Region des Zielknotens und dem Autobahnnetz folgendermaßen berechnen: Man betrachtet alle Paare von Autobahnauffahrten in der Region des Startknotens auf der einen Seite und Autobahnabfahrten in der Region des Zielknotens auf der anderen Seite und ermittelt das Paar, welches die Gesamtlänge des konkatenierten Weges von Startknoten zur Autobahnauffahrt zur Autobahnabfahrt zum Zielknoten minimiert. Dieser Weg muss dann der kürzeste sein und kann nur durch die drei Teile des Gesamtstraßennetzes ermittelt werden. Ist es zu erwarten, dass die Annahme, dass alle kürzesten Wege in realen Straßennetzen immer die Struktur „Straßen niederer Ordnung – Autobahnauffahrt innerhalb der Region des Startknotens – Autobahnen – Autobahnabfahrt innerhalb der Region des Zielknotens – Straßen niederer Ordnung bis zum Zielknoten“ haben? Nein, bei den meisten Straßennetzen (und Autobahnunternetzen) und Einteilungen derer in Regionen wohl nicht, denn • Knoten, die innerhalb derselben Region liegen, sind oftmals nicht sehr weit voneinander entfernt, so dass der kürzeste Weg vermutlich nicht über Autobahnen geht. • Bei Knoten, die in benachbarten Regionen liegen, ist auch zu erwarten, dass der kürzeste Weg oft nicht über Autobahnen geht. 1 Der Begriff Region ist in einem sehr intuitiven Sinn gemeint. Formaler kann man sich unter dieser Regionalisierung z.B. eine Tesselation der Ebene oder (mehr auf Straßennetze bezogen) eine disjunkte, vollständige Einteilung aller Knoten (und dadurch auch der Kanten) in bzgl. den Kanten zusammenhängende, möglichst konvexe Teilmengen vorstellen. Später wird dies (natürlich) genau auf den Punkt gebracht. 52 • Abb. 13 zeigt, dass in realen Straßennetzen das Autobahnnetz nicht immer perfekt ist und bzgl. kürzester Wege in Straßennetzen nicht abgeschlossen ist. • Ferner kann es natürlich sein, dass, je nachdem wie man das gesamte Straßennetz in Regionen eingeteilt hat, die Autobahnauffahrt oder die Autobahnabfahrt eines kürzesten Weges nicht in der Region des Startknotens bzw. des Zielknotens liegt. Aber es ist zu erwarten (zumindest zu erhoffen), dass die meisten kürzesten Wege zwischen zwei „weit“ voneinander entfernten Punkten die Struktur „Straßen niederer Ordnung – Autobahnen – Straßen niederer Ordnung“ haben, denn das Autobahnnetz ist ja dazu da, Kraftfahrzeugen zu ermöglichen, schneller von einem Punkt zu einem anderen zu kommen als über normale Straßen und dementsprechend sorgfältig geplant und angelegt. Dieses auf intuitiver Ebene für reale Straßennetze skizzierte Verfahren zur Bestimmung kürzester Wege wollen wir nun im nächsten Abschnitt genauer betrachten. 5.1.2 Straßensubnetze Im vorherigen Abschnitt haben wir von „Regionen“ gesprochen und dies in einem sehr intuitiven Sinne gemeint. Eine Einteilung eines Straßennetzes in solche Regionen, so dass der vorhin skizzierte Algorithmus zur Bestimmung kürzester Wege (der in Abschnitt 5.1.3 komplett und formal beschrieben wird) immer funktioniert, wollen wir nun formal definieren. Die folgende Definition definiert das hierarchische Straßennetz, von dem wir bisher gesprochen haben. Diese Definition und der Algorithmus im nächsten Abschnitt, obwohl definiert für Straßennetze N, basieren an sich auf gewichteten, gerichteten Graphen G = (V, E, w), weil nichts von S, T, s, t im Algorithmus verwendet wird. Ich habe mich aber dafür entschieden, nicht so allgemein wie möglich zu bleiben (und hier von gerichteten, gewichteten Graphen anstatt von Straßennetzen zu sprechen), da sonst das Verständnis darunter leiden könnte (zumal es auch leicht zu sehen ist, wie man die Definition „verallgemeinern“ kann). Wir nennen ein Tupel X = ({H}, {L1, L2, ..., Lp}, f) eine Einteilung eines Straßennetzes N = (V, E, S, T, w, s, t) in (Straßen-)Subnetze genau dann, wenn 1) H = (V[H], E[H], S[H], T[H], w[H], s[H], t[H]) ein Straßenunternetz1 von N ist, 2) für alle 1 ≤ i ≤ p gilt : Li = (V[Li], E[Li], S[Li], T[Li], w[Li], s[Li], t[Li]) ist ein Straßenunternetz von N, 1 Zur Definition eines Straßenunternetzes siehe Abschnitt 4.3 53 3) f: V → { L1, L2, ..., Lp} ist eine Funktion, welche die einzelnen Knoten ihrem jeweiligem Stammsubnetz zuordnet und es gilt: v∈V[f(v)]. 4) für alle u, v ∈ V mit f(u) ≠ f(v) gilt für mindestens einen kürzesten Pfad P = [e1, e2, ..., ek-1, ek] von u nach v, falls solcher existiert, mindenstens einer der folgenden Punkte: a) Der Pfad P ist vollständig (mit allen Knoten und Kanten) in f(u) enthalten. b) Es gibt zwei Teilpfade P1 = [e1, ..., ex] und P2 = [ex+1, ..., ek] von P, wobei ex = (c, d), so dass P1 vollständig in f(u), P2 vollständig in f(v) enthalten sind und d∈V[H] gilt. Diese Teilpfade können auch leer bzw. triviale Pfade [(e, e)] sein (die Konkatenation muss aber den Pfad P ergeben) 1. c) Es gibt drei Teilpfade P1 = [e1, ..., ex], P2 = [ex+1, ..., ey] und P3 = [ey+1, ..., ek] von P, so dass P1 vollständig in f(u), P2 vollständig in H und P3 vollständig in f(v) enthalten sind. Diese Teilpfade können auch leer bzw. triviale Pfade [(e, e)] sein (die Konkatenation muss aber den Pfad P ergeben). 5) für alle u, v ∈ V mit mit f(u) = f(v) gilt: Der kürzeste Pfad von u nach v, falls ein solcher existiert, ist vollständig in f(u) enthalten. Hierbei bezeichnen wir H als das High-Level (Straßen-)Subnetz und L1, ..., Lp als LowLevel (Straßen-)Subnetze und meinen mit (Straßen-)Subnetz entweder ein High- oder Low-Level-Subnetz. Zu dieser Definition ist Folgendes zu bemerken: • Es ist ganz bewusst nicht verlangt, dass das High-Level-Subnetz einer solchen Subnetz-Einteilung X eines Straßennetzes N aus Autobahnen bestehen muss. Zumindest für das Erste kann man sich aber zum leichteren Verständnis unter dem High-Level-Subnetz das Autobahnunternetz des betrachteten Straßennetzes vorstellen (oder z.B. auch das durch den Algorithmus AUTOBAHNNETZ_ERWEITERUNG erweiterte Autobahnunternetz). • Im Prinzip sind die Mengen V[Li] ∩ V[H] die Zugänge vom Low-Level-Subnetz Li zum High-Level-Subnetz H bzw. die Autobahnauffahrten und –abfahrten. Dies sollte spätestens beim nächsten Algorithmus klar werden. • Für die englischen Begriffe High-Level- und Low-Level-Subnetz habe ich mich entschieden, weil sie prägnanter sind als die deutschen Alternativen, die mir eingefallen sind, und relativ allgemein sind. • In der Definition soll (natürlich) V[▪], E[▪], etc. nicht eine implementierte Funktion oder Array oder sonstiges Derartiges darstellen, sondern dient nur der Bezeichnung. 1 Dies ist nicht 100% formal definiert, aber für die Intuition förderlich, denke (und hoffe) ich. 54 • Die Definition X = ({H}, {L1, L2, ..., Lp}, f) habe ich so definiert, weil es meiner Meinung sich leicht in höhere Hierarchie-Dimensionen1 auf zentrale Art und Weise2 verallgemeinern lässt: So könnte man z.B. für Dimension d die Einteilung wie nachstehend definieren: X := {H d }, {H 1d −1 ,..., H edd−−11 },..., {H 11 ,..., H e11 }, {L1 ,..., L p }, f ) • Dies ist eine rein theoretische Definition, in der nichts darüber ausgesagt wird, wie die Subnetze in einem Rechner gespeichert werden. Solange es möglich ist, werden wir uns darüber auch keine Gedanken machen, da die später hergeleiteten Algorithmen in dieser Hinsicht auch relativ flexibel sind. Erst in Abschnitt 5.3.1 werden wir uns konkrete Datenstrukturen anschauen (und z.T. auch schon ein wenig in den Laufzeitanalysen der folgenden Algorithmen). Es dürfte hierbei auch klar sein, dass man die Zugänge zwischen den Subnetzen in praktischen Implementationen wohl irgendwie explizit machen würde und nicht so elegant theoretisch implizit wie hier. 5.1.3 Algorithmus zur Ermittlung kürzester Wege Im letzten Abschnitt haben wir definiert, was eine Einteilung eines Straßennetzes in Subnetze ist. Nun schauen wir uns den schon in 5.1.1 skizzierten Algorithmus für die Bestimmung kürzester Wege innerhalb eines hierarchischen Straßennetzes an, aber diesmal komplett und formal auf der Grundlage der Definition des vorherigen Abschnitts: Algorithmus: HIERARCHISCHE_WEGBERECHNUNG Eingabe: a) Bzgl. einer Einteilung X = ({H}, {L1, L2, ..., Lp}, f) des Straßennetzes N = (V, E, S, T, w, s, t) mit V = {1, ..., n}in Subnetze Folgendes: i) Startknoten i∈V und Zielknoten j∈V des zu berechnenden Pfades ii) das High-Level-Subnetz H = (V[H], E[H], S[H], T[H], w[H], s[H], t[H]) von X iii) das Low-Level Subnetz L(1) = (V[L(1)] , E[L(1)], S[L(1)], T[L(1)], w[L(1)], s[L(1)], t[L(1)]) von X, s.d. f(i) = L(1) (Stammsubnetz von i) iv) das Low-Level Subnetz L(2) = (V[L(2)], E[L(2)], S[L(2)], T[L(2)], w[L(2)], s[L(2)], t[L(2)]) von X, s.d. f(j) = L(2) (Stammsubnetz von j) v) der zu i korrespondierende Knoten in L(1), der zu j korrespondierende Knoten in L(2) und der zu j korrespondierende Knoten in L(1), falls dieser 1 Ich bin mir nicht sicher, ob man bei solchen Hierarchien von Dimensionen spricht, aber es dürfte auf intuitive Weise gut verständlich sein. 2 Ein rekursiver Ansatz wäre vielleicht noch schöner, weil ja ein Straßensubnetz auch selber wieder ein Straßennetz ist, dass ja wieder in Straßensubnetze eingeteilt sein könnte... 55 existiert, seien bekannt (bzw. diese werden anstatt i und j aus N übergeben). Ausgabe: entweder den kürzesten Pfad in N zwischen dem Startknoten i und dem Zielknoten j und dessen Länge oder, falls kein solcher existiert, „[], ∞“ Methode: if L(1) = L(2) then berechne δ(i, j) und ggf. den kürzesten Weg P von i nach j innerhalb L(1) ; return P, δ(i, j) bzw. Ø, ∞; fi dmin := ∞; for all a∈ (V[L(1)] ∩ V[H]) // (V[L(1)] ∩ V[H]) ist die Menge der Auffahrten / // Ausgänge von L(1) zu H (zumindest im Prinzip) do for all e∈ (V[L(2)] ∩ V[H]) // (V[L(2)] ∩ V[H]) ist die Menge der Abfahrten / // Eingänge von in L(2) von H kommend do berechne durch das Subnetz L(1): d(1) := δ L(1) (i, a); // = δ N (i, a) berechne durch das Subnetz H: dH := δ H (a, e); // = δ N (a, e) berechne durch das Subnetz L(1): d(2) := δ L( 2 ) (e, j); // = δ N (e, j) if d(1) + dH + d(2) < dmin then dmin := d(1) + dH + d(2); amin := a; emin := e; fi od od if j∈L(1) then if δ L(1) (i, j) ≤ dmin then ermittle durch das Subnetz L(1) den kürzesten Pfad P von i nach j; return P, δ L(1) (i, j); fi fi if dmin < ∞ 56 then ermittle durch das Subnetz L(1) den kürzesten Pfad P(1) von i nach a; ermittle durch das Subnetz H den kürzesten Pfad PH von a nach e; ermittle durch das Subnetz L(2) den kürzesten Pfad P(2) von a nach j; konkateniere P(1), PH und P(2) zu Pmin // wobei (natürlich) das jeweils zweite Vor// kommen von a und e gestrichen wird return Pmin, dmin; else return [], ∞; fi ڤ Nun wollen wir die Korrektheit und Laufzeit dieses Algorithmusses zeigen: Satz 5: Der Algorithmus HIERARCHISCHE_WEGBERECHNUNG ist korrekt und lässt sich so implementieren, dass eine Laufzeit von O(xyz + p) erreicht wird, wobei • x = |V[L(1)] ∩ V[H]| sei, • y = |V[L(2)] ∩ V[H]| sei, • z = z(1) + zH + z(2) und z(1) die Dauer (obere Schranke) der Berechnung einer kürzesten Distanz δ L(1) , zH die Dauer (obere Schranke) der Berechnung einer kürzesten Distanz δ H , z(2) die Dauer (obere Schranke) der Berechnung einer kürzesten Distanz δ L( 2 ) sei, • ρ = ρ (1) + ρ H + ρ (2) und ρ (1) die Dauer (obere Schranke) eines kürzesten Weges in L(1), ρ H die Dauer (obere Schranke) der Berechnung eines kürzesten Weges in H, ρ (2) die Dauer (obere Schranke) der Berechnung kürzesten Weges in L(2) sei. Beweis: Zur Korrektheit überlegen wir uns Folgendes: Falls es keinen kürzesten Weg zwischen i und j in N gibt, dann wird vom Algorithmus „[], ∞“ ausgegeben, weil L(1), L(1) und H Straßenunternetze von N sind und demnach keine neuen Wege darin über die Zugänge enthalten sein können. Falls j von i aus erreichbar ist, dann gilt für einen kürzesten Weg Punkt (4) oder (5). Falls L(1) = L(2) bzw. f(i) = f(j), dann folgt die Korrektheit direkt aus Punkt (5) der Definition der Einteilung eines Straßennetzes in Subnetze. Ansonsten gilt nach der Definition für einen kürzesten Weg Punkt (4) a), b) oder c): 57 Falls a) gilt, dann wird dieser kürzeste Pfad im Rumpf der if-Anweisung „if j∈L(1)“ gefunden und zurückgegeben. Falls b) gilt, dann wird dieser kürzeste Pfad in den beiden verschachtelten for-Schleifen bei a = e = d gefunden und später zurückgegeben. Im Fall c) wird dieser Pfad in den beiden verschachtelten for-Schleifen bei einem bestimmten Paar (a, e) gefunden und später zurückgegeben. Die Laufzeit kommt folgendermaßen zustande: Für den Fall L(1) = L(2) gilt die Schranke wegen des additiven p sicherlich. Der if-Teil „if j∈L(1)“ ist auch in dem additiven p enthalten, weil man mit einer kürzesten Weg auch die kürzeste Distanz hat. Ansonsten: Wenn man den Datenstrukturen der Low-Level-Subnetze z.B. jeweils eine verkettete Liste mit den Zugängen zum High-Level-Subnetz (die Mengen V[L(1)] ∩ V[H]) hinzufügt (und entsprechende Verpointerungen), dann kann man die ganzen Paare (a, e) in Zeit O(xy) generieren. Für das Innere der beiden while-Schleifen braucht man offensichtlich jeweils O(z) Zeit. Für die Rückgabe muss dann der ganze Pfad (und nicht nur die Länge) berechnet bzw. zurück gegeben werden. Diese Zeit ist in dem ρ enthalten. ڤ Wir wissen nun, wie wir mit einer gegebenen Subnetz-Einteilung eines Straßennetzes kürzeste Wege berechnen können. Aber wir wissen noch nicht, wie wir eine solche Subnetz-Einteilung aus einem Straßennetz gewinnen können und welche Datenstrukturen sich bei einer Implementierung für die Speicherung der Subnetze eignen, um gute Resultate zu erhalten. Ersteres wollen wir im nächsten Abschnitt behandeln. 5.2 Herleitung der Straßensubnetze In diesem Abschnitt wollen wir uns überlegen, wie wir „sinnvolle“ Subnetz-Einteilungen zu einem gegebenen Straßennetz erhalten. Einfach irgendeine gültige Subnetz-Einteilung zu erhalten ist nicht schwer, bringt aber nicht unbedingt viel. So kann man z.B. ein Straßennetz N = (V, E, S, T, w, s, t) folgendermaßen in Subnetze „einteilen“: X := ({H}, {L}, f), wobei H = L = (V, E, S, T, w, s, t) und f(v) = L für alle v∈V. Diese Einteilung ist wohl ziemlich nutzlos. Daher überlegen wir uns zuerst Kriterien, die es bei einer SubnetzEinteilung möglichst zu optimieren gilt, bevor wir auf einen konkreten Algorithmus, der das dann mehr oder weniger gut macht, zu sprechen kommen. 58 5.2.1 Optimierungskriterien Nun betrachten wir ein paar mögliche Optimierungskriterien bzw. Funktionen, die es bei der Erstellung der Subnetze zu minimieren gilt. Bei diesen Optimierungsfunktionen geht es immer um die Größe des für die Datenstrukturen der Subnetzaufteilung benötigten Speicherplatzes oder um die (eher theoretische) Größe der Subnetze selbst, da diese an sich schon wichtig sind und auch bei vielen Datenstrukturen mehr oder weniger direkt die Laufzeitschranken für den Algorithmus HIERARCHISCHE_WEGBERECHNUNG determinieren. Jede dieser Optimierungskriterien definiert ein eigenes Optimierungsproblem, welches als Eingabe ein Straßennetz N = (V, E, S, T, w, s, t) erwartet und dann eine Einteilung dessen in Straßensubnetze X = ({H}, {L1, L2, ..., Lp}, f) in (hoffentlich) polynomieller Zeit liefert und X dann das entsprechende Optimierungskriterium unter allen möglichen Subnetzeinteilungen X’ von N minimiert. Die meisten dieser Optimierungsprobleme bzw. Kriteriumsfunktionen haben kaum praktischen Nutzen und sind nur da, um die Verständlichkeit dieses Themas durch ein weiteres Beispiel zu fördern. Außerdem lässt sich an ihnen gut der Anstieg der theoretischen Schwierigkeit der optimalen Lösung der jeweiligen Probleme erkennen. Wir fangen mit den einfachen (und meistens praktisch wertlosen) Optimierungskriterien an und arbeiten uns zu den komplexeren durch. Zur Beschreibung der Optimierungskriterien benutze ich eine intuitive, formal vielleicht nicht ganz saubere Notation: „Min! x“ bezeichne, dass es x zu minimieren gilt. Mit D(x) meinen wir den Speicherplatzbedarf zur Speicherung von x mit den betrachteten Datenstrukturen. Mit |x| meinen wir die (eher theoretische) Kardinalität, die sehr wohl von D(x) asymptotisch abweichen kann. Im einzelnen bezeichnen wir mit |N| := |G| = |V| + |E| 1 für Straßennetze N = (V, E, S, T, w, s, t) (also auch Subnetze). Dadurch, dass sich (m.E.) eigentlich immer Datenstrukturen für x mit Speicherplatzverbrauch O(|x|) finden lassen (die dann vielleicht in anderer Hinsicht nicht unbedingt die effizientesten sind), ist D(x) immer eine Verallgemeinerung von |x| (oder eher O(|x|) ). Zu der Auswahl der Kriteriumsfunktionen und Bewertungen / Erläuterungen ist zu bemerken, dass Straßennetze in der Regel sehr lichte (erweiterte) Graphen sind und demnach die Anzahl der Kanten m asymptotisch in linearem Zusammenhang mit der Anzahl der Knoten n stehen. Also gilt m = O(n) und demnach brauchen eben z.B. Datenstrukturen, die solche Graphen bzw. Straßennetze mit den Kantenmengen als verkettete Listen speichern, asymptotisch um Faktor n weniger Speicherplatz als Datenstrukturen, welche die Kanten als Adjazenzmatrix speichern oder insbesondere 1 S, T, w, s, t lassen sich m.E. immer durch entsprechende Verpointerungen ohne zusätzlichen asymptotischen Speicherplatzverbrauch in die Datenstrukturen zur Speicherung von N = (V, E, S, T, w, s, t) einbauen, weil sie jeweils in linearem Zusammenhang mit |E| stehen. 59 Datenstrukturen, welche nebenbei noch eine (Variante der) Floyd-Wege-Matrix benutzen (das werden wir später tun). Kriteriumsfunktion (1): Min! |H| ( normalerweise = Min! D(H) ) Bei diesem Kriterium geht es darum, die Größe des High-Level-Subnetzes zu minimieren. Dieses Problem ist leicht optimal lösbar, indem man das gegebene Straßennetz N z.B. folgendermaßen in Subnetze „aufteilt“: X := ({H}, {L}, f), wobei H = (Ø, Ø, 0) und L = (V, E, w) und f(v) = L für alle v∈V. Hat diese Aufteilung von N in Subnetze praktischen Nutzen? Wohl kaum. p Kriteriumsfunktion (2): Min! ∑| L i =1 i | Mit Kriterium möchte man die Gesamtgröße der Subnetze zu minimieren. Dieses Problem ist auf die gleiche Weise optimal lösbar wie die Kriteriumsfunktion (1), weil jeder Knoten zumindest einmal in einem Low-Level-Subnetz vorkommen muss und hierbei jeder Knoten in genau einem Subnetz vorkommt. Wie bei der Kriteriumsfunktion (1) fällt mir hier keine sinnvolle Anwendung ein, weil die optimale Lösung nicht mehr viel mit einem hierarchischen Straßennetz zu tun hat. p Kriteriumsfunktion (3): Min! ∑ D( L ) i =1 i Diese Kriteriumsfunktion sieht zwar sehr ähnlich aus wie (2), eine optimale Lösung hierfür kann aber je nach verwendeten Datenstrukturen von einer optimalen Lösung für (2) abweichen. Dies kommt daher, dass aus der Mathematik z.B. folgendes bekannt ist: (a + b)2 > a2 + b2 für a, b∈ Ν , aber (a + b)1 = a1 + b1. Deshalb werden hier normalerweise bei optimalen Lösungen bei Verwendung von Datenstrukturen, deren Komplexität quadratisch in n ist, die Gesamtknoten ungefähr gleich auf die einzelnen Subnetze verteilt sein. Dies muss aber bei Kriteriumsfunktion (2) überhaupt nicht so sein, sondern es zählt nur die Gesamtzahl der Knoten (wenn die Kantenzahl linear in n ist, was wir bei Straßennetzen, wie gesagt, normalerweise annehmen können). Bei Datenstrukturen, die in n lineare Komplexität haben, ist die optimale Lösung hierfür gleich der von Funktion (2) und hat kaum praktischen Nutzen. Mir ist kein polynomieller Algorithmus bekannt, der für Datenstrukturen mit in n quadratischer Komplexität bzgl. dieser Kriteriumsfunktion optimale Lösungen generiert. Ohne die Beachtung von H macht diese Kriteriumsfunktion aber auch nicht viel Sinn. 60 p Kriteriumsfunktion (4): Min! ∑| L i =1 i | + |H| Dieses Kriterium lässt sich auf die gleiche Weise optimal lösen wie die Funktionen (1) und (2) und leider auch mit dem gleichen, geringen praktischen Nutzen. p Kriteriumsfunktion (5): Min! ∑ D( L ) + D(H) i =1 i Wie bei Funktion (3) ist mir hierzu auch kein polynomieller Algorithmus zur Bestimmung optimaler Lösungen bei Datenstrukturen mit in n quadratischer Komplexität bekannt. Eine Einteilung eines Straßensubnetzes, die dies minimiert, hat aber praktische Relevanz, weil sie minimalen Speicherplatz benötigt. Kriteriumsfunktion (6): Min! max{| Li | + | L j | + | H |} i , j ,i ≠ j Für dieses Problem ist mir kein polynomieller Lösungsalgorithmus bekannt. Ein solcher hätte aber praktischen Nutzen, weil man zu einer Wegberechnung mit dem Algorithmus HIERARCHISCHE_ WEGBERECHNUNG zwei Low-Level-Subnetze benötigt und ein HighLevel-Subnetz und man durch dieses Kriterium den benötigten Speicherplatz für eine Wegberechnung minimieren kann (eine Anwendung hierfür wäre z.B. auch ein auf verschiedenen Prozessoren verteilt arbeitender Wegfindungs-Algorithmus). Außerdem ist dieses Kriterium auch ein wenig mit Funktion (8) (positiv) korreliert. Kriteriumsfunktion (7): Min! max{D( Li ) + D( L j ) + D( H )} i , j ,i ≠ j Wie bei (6) ist mir für diese Kriteriumsfunktion auch keine polynomieller Lösungsalgorithmus bekannt und die praktische Relevanz ist hier sogar noch ein wenig höher, weil diese Funktion allgemeiner ist. p Kriteriumsfunktion (8): Min! ∑ | V (L ) ∩ V (H ) | i =1 i p Diese Kriteriumsfunktion minimiert die durchschnittliche Anzahl von Übergängen der Low-Level-Subnetze ins High-Level-Subnetz. Der Nutzen liegt darin, dass die Anzahlen der Übergänge direkt die Laufzeit des Algorithmus HIERARCHISCHE_ WEGBERECHNUNG determinieren. Nebenbei wird eine Lösung, die diese Funktion minimiert auch eher insgesamt „kleine“ Subnetze haben und somit in dieser Beziehung auch in die „Nähe“ der Minima der Funktionen (6) bzw. (7) kommen. 61 Fazit: Meiner Meinung nach sind die Funktionen (5) – (8) aus den schon beschriebenen Gründen am sinnvollsten. Am „liebsten“ wäre mir ein (polynomieller) optimaler Algorithmus bzgl. (8), (7) und (5), wobei die Kriterien in dieser Reihenfolge (lexikographisch) herangezogen werden. Aber selbst ein solcher Algorithmus muss nicht immer wirklich gute Resultate bzgl. Speicherplatzverbrauch und Laufzeiten bringen, da es vermutlich für unsere Anwendung inhärent schlechte Straßennetze bzw. Graphen gibt. Diese dürften aber in der Praxis (Straßennetze aus der Realität) kaum auftreten. 5.2.2 Heuristik: Landkreise und Autobahnunternetz als Grundlage für die Subnetze In diesem Abschnitt wollen wir einen Algorithmus – genauer gesagt eine Heuristik – herleiten, welcher, gegeben ein Straßennetz, immer eine (gültige) Einteilung dessen in Subnetze generiert, die aber nicht unbedingt die im vorherigen Abschnitt aufgezählten Optimierungskriterien erfüllt. Dem Algorithmus zur Einteilung eines Straßennetzes in Subnetze liegt die Idee des Grundmodells aus Abschnitt 5.1.1 zugrunde, von der wir uns ab 5.1.2 vorerst ein wenig distanziert haben, indem wir die Low-Level-Subnetze und das High-Level-Subnetz ohne direkten Bezug zu Straßennetzen und die vorhandenen Strukturen definiert haben. Die Idee ist, die in einem Straßennetz schon vorkommende Hierarchie der Straßentypen und die vom Staat, Land oder sonstiger Instanz bestimmten Verwaltungsbezirke wie z.B. Landkreise als Grundlage für die Einteilung des Straßennetzes in Straßensubnetze zu benutzen. Da diese vorläufige Einteilung wahrscheinlich gegen einige Punkte der Definition der Einteilung eines Straßennetzes in Subnetze verstößt, muss diese noch nachbearbeitet werden. Bevor wir auf diese Nachbearbeitung eingehen, betrachten wir diese vorläufige Einteilung: Sei N = (V, E, S, T, w, s, t) das zugrunde liegende Straßennetz. Der Einfachheit halber gebe es nur folgende zwei Straßentypen: „Autobahnen“ und den Rest, den wir als „Landstraßen“ bezeichnen, also T = {Autobahn, Landstraße} 1. Sei P = {P1, P2, ..., P|P|} die Menge der Landkreise (oder sonstige Verwaltungsbezirke) der N zugrunde liegenden 1 Das ganze Verfahren lässt sich natürlich auch in mehr Dimensionen hierarchisch organisieren, was auch (vermutlich) sehr sinnvoll sein kann. Darauf werde ich im Ausblick noch einmal kurz eingehen. Eine Hierarchie in 5 Ebenen könnte bzgl. der Straßen z.B. so aussehen: eine Ebene / Level mit 30’er Zonen, ein Level Straßen in Städten, ein Level Landstraßen, ein Level Bundesstraßen, ein Level Autobahnen. Diese Diplomarbeit betrachtet aber nur die einfachere Variante mit zwei Ebenen (die auch schon genug Schwierigkeiten beinhaltet). 62 „Karte“1. Dazu gebe es eine Funktion g: V → P, welche jeden Knoten zu (genau) einem Landkreis zuordnet. P ist also eine Partitionierung der Knotenmenge (man kann demnach hierfür eine beliebige Partitionierung von V nehmen). Aus N, P und g möchten wir nun eine Einteilung von N in Subnetze X = ({H}, {L1, L2, ..., Lp}, f) mit p = |P| erhalten. Ein vorläufiges High-Level-Subnetz H erhalten wir dadurch, dass wir H als das Autobahnunternetz von N definieren (siehe Kapitel 4.4). Alternativ dazu kann man auch das durch den Algorithmus AUTOBAHNNETZ_ERWEITERUNG erweiterte Autobahnnetz nehmen, ist aber nicht unbedingt erforderlich und auch nicht unbedingt vorteilhaft (dazu später mehr). Die Low-Level-Subnetze Li, 1 ≤ i ≤ p definieren wir vorläufig folgendermaßen: Li := (V[Li], E[Li], S[Li], T[Li], w[Li], s[Li], t[Li]), wobei V[Li] := {v∈V | g(v) = Pi}, E[Li] := Ø, S[Li] := die Nullfunktion 0, T[Li] := {Autobahn, Landstraße}, und w[Li], s[Li] und t[Li] jeweils als die Nullfunktion 0. g definieren wir (natürlich) gleich f. Was fehlt jetzt noch? Diese Einteilung von N in Subnetze erfüllt offensichtlich die Punkte (1) bis (3) der Definition zur Einteilung eines Straßennetzes in Subnetze. Die Punkte (4) und (5) dagegen sind im Allgemeinen nicht erfüllt, da z.B. keines der Low-Level-Subnetze irgendwelche Kanten enthält. Nun gehen wir folgendermaßen im Algorithmus vor: Wir betrachten alle kürzesten Pfade und sorgen lokal bei jedem einzelnen Pfad dafür, dass dieser die Punkte (4) und (5) der Definition erfüllt. Dazu betrachten wir nun eine Klassifikation von möglichen Fällen für (kürzeste) Pfade und überlegen uns bei jedem einzelnen Fall, wie man diesen im Falle eines Verstoßes gegen Punkt (4) oder (5) der Definition erfüllen kann: Hierzu betrachten wir einen kürzesten Pfad P = [v0, e1, v1, e2, ..., vk-1, ek, vk] von v0 zu vk. Bzgl. dieses Pfades bezeichne x den maximalen Index, so dass vx ∈ V[f(v0)] und y den minimalen Index, so dass vy ∈ V[f(vk)]. Ferner bezeichne a∈{x, ..., y} den minimalen Index, so dass va ∈ V[H], falls es so ein a gibt und sonst a := -1 und b∈{x, ..., y} den maximalen Index, so dass vb ∈ V[H], falls es so ein b gibt und sonst b := -1. 1 Karte ist hier intuitiv zu verstehen. An kaum einer Stelle der Diplomarbeit gehen wir auf Einbettungen von Graphen in die Ebene ein (siehe z.B. A*-Algorithmus), Topologie oder sonstiges. Zum Teil könnte deren Beachtung zwar vielleicht sinnvoll sein, würde aber den Rahmen dieser Diplomarbeit sprengen. 63 Fall 1: x = k (dies ist äquivalent zu vk ∈ V[f(v0)]) vk liegt also im Stamm-Subnetz von v0. Siehe Abb. 14. Fall 1 a: Für alle i∈{0, ..., k} gilt: vi ∈ V[f(v0)] und für alle i∈{1, ..., k} gilt: ei ∈ E[f(v0)] Der ganze kürzeste Pfad von v0 nach vk liegt also im Stammsubnetz von v0. Dieser Fall erfüllt Punkt (5) oder Punkt (4) a) der Definition. Fall 1 b: ¬ Fall 1 a) Dieser Fall verstößt gegen Punkt (5) oder Punkt (4) a). Dieser Verstoß lässt sich aber leicht dadurch beheben, dass man alle Knoten und Kanten von P zum Subnetz f(v0) hinzufügt. f(v0) v0 f(v0) v0 vk vk Abb. 14: Beispiel Fall 1 a, Fall 1 b. Links sieht man eine Skizze für den Fall 1 a) und rechts eine für den Fall 1 b) 64 Fall 2: x < k und a = x und b = y Es gibt also ein Stück Autobahn (und wenn auch nur ein einzelner Knoten oder ein oft unterbrochenes Stück) zwischen dem Stammsubnetz von v0 und dem Stammsubnetz von vk. Damit dieser Fall nicht gegen Punkt (4) c) verstößt, muss Folgendes gelten (die Unterfall-Kombinationen sind hier zu viele um aufzuzählen): a) Für alle 0 ≤ i ≤ a: ei ∈ E[f(v0)] (und dadurch natürlich auch vi ∈ V[f(v0)]), sprich, das erste Stück bis zum Index a muss vollständig zu f(v0) gehören. b) Für alle a < i ≤ b: ei ∈ E[H], also das Stück zwischen a und b muss vollständig zu H gehören. c) Für alle b < i ≤ k: ei ∈ E[f(v0)], also das letzte Wegstück (ab Index b) muss vollständig zum Stammsubnetz von vk gehören. Falls obige Bedingungen nicht erfüllt sind, kann man sie durch einfaches Hinzufügen von den entsprechenden Knoten und Kanten zu den jeweiligen Subnetzen erfüllen. Siehe Abb. 15. f(v0) v0 vx = va f(vk) vy = vb vk f(v0) v0 vx = va f(vk) vy = vb vk Abb. 15: Beispiel Fall 2. Oben sieht man den Fall 2, wo die Punkte a), b) und c) verletzt sind. Unten sieht man den die Korrektur, bei der a), b) und c) nach dem Hinzufügen von Knoten und Kanten zu den entsprechenden Subnetzen erfüllt sind. Die blauen und roten Kanten zusammen stellen den kürzesten Weg von v0 nach vk dar. Die blauen Kanten bezeichnen hierbei Landstraßen-Kanten und die roten Autobahn-Kanten. Die farbigen Flächen skizzieren jeweils die Stammsubnetze vom Startknoten und Zielknoten des aktuellen Pfades. 65 Fall 3: x < k und a > x und b = y Dieser Fall ist wie Fall 2, nur beginnt das (eventuell nur aus einem Knoten bestehende oder lückenhafte) Autobahnstück außerhalb des Stammsubnetzes von v0. Damit dieser Fall nicht gegen Punkt (4) c) verstößt, erweitern wir das Stammsubnetz von v0 um die Knoten und Kanten von vx bis vk und beseitigen Lücken innerhalb der drei Wegteile. Formal ausgedrückt, sorgen wir wieder dafür, dass die drei Punkte a), b) und c) von Fall 2 erfüllt sind, nur ist dieses Mal a > x: (Siehe Abb. 16.) a) Für alle 0 ≤ i ≤ a: ei ∈ E[f(v0)] (und dadurch natürlich auch vi ∈ V[f(v0)]), sprich, das erste Stück bis zum Index a muss vollständig zu f(v0) gehören. b) Für alle a < i ≤ b: ei ∈ E[H], also das Stück zwischen a und b muss vollständig zu H gehören. c) Für alle b < i ≤ k: ei ∈ E[f(v0)], also das letzte Wegstück (ab Index b) muss vollständig zum Stammsubnetz von vk gehören. Dass wir das Stammsubnetz von v0 erweitern ist Willkür. Wir könnten ebenso das Autobahnstück verlängern, so dass dessen erster Knoten vx ist. Aber wahrscheinlich ist es meistens besser – so wie wir es getan haben – das Stammsubnetz zu erweitern, weil sonst das High-Level-Subnetz zu groß werden würde (und das High-Level-Subnetz ist später an fast jeder kürzesten-Weg-Berechnung beteiligt). 66 f(v0) v0 vx f(vk) va vy = vb vk f(v0) v0 f(vk) va = vx vy = vb vk Abb. 16: Beispiel Fall 3. Oben sieht man den Fall 3, wo die Punkte a), b) und c) verletzt sind. Unten sieht man den die Korrektur, bei der a), b) und c) nach dem Hinzufügen von Knoten und Kanten zu den entsprechenden Subnetzen erfüllt sind. Die blauen und roten Kanten zusammen stellen den kürzesten Weg von v0 nach vk dar. Die blauen Kanten bezeichnen hierbei Landstraßen-Kanten und die roten Autobahn-Kanten. Die farbigen Flächen skizzieren jeweils die Stammsubnetze vom Startknoten und Zielknoten des aktuellen Pfades. Fall 4: x < k und a = x und b < y Dieser Fall ist wie Fall 2, nur endet das (eventuell nur aus einem Knoten bestehende oder lückenhafte) Autobahnstück echt vor dem Stammsubnetzes von vk. Damit dieser Fall nicht gegen Punkt (4) c) verstößt, erweitern wir das Stammsubnetz von vk um die Knoten und Kanten von vb bis vy und beseitigen eventuelle Lücken in den Wegteilen. Formal ausgedrückt, sorgen wir wieder dafür, dass die drei Punkte a), b) und c) von Fall 2 erfüllt sind, nur ist dieses Mal b < y. Siehe Abb. 17. Wie in Fall 3 ist diese Art der Erweiterung der Subnetze nicht die einzig mögliche, um diesen Pfad die Punkte (4) und (5) erfüllen zu lassen, aber die intuitiv naheliegenste und vermutlich meistens auch günstigste Variante. 67 f(v0) f(vk) v0 vb vx = va vy vk f(v0) f(vk) v0 vy = vb vx = va vk Abb. 17: Beispiel Fall 4. Oben sieht man den Fall 4, wo die Punkte a), b) und c) verletzt sind. Unten sieht man den die Korrektur, bei der a), b) und c) nach dem Hinzufügen von Knoten und Kanten zu den entsprechenden Subnetzen erfüllt sind. Die blauen und roten Kanten zusammen stellen den kürzesten Weg von v0 nach vk dar. Die blauen Kanten bezeichnen hierbei Landstraßen-Kanten und die roten Autobahn-Kanten. Die farbigen Flächen skizzieren jeweils die Stammsubnetze vom Startknoten und Zielknoten des aktuellen Pfades. Fall 5: x < k und a > x und b < y Dieser Fall enthält im Prinzip Fall 3 und 4, denn das Autobahnstück beginnt zu spät und endet zu früh. Damit dieser Fall nicht gegen Punkt (4) c) verstößt, erweitern wir beide Stammsubnetze und beseitigen sonstige Lücken. Formal ausgedrückt, sorgen wir wieder dafür, dass die drei Punkte a), b) und c) von Fall 2 erfüllt sind, nur ist dieses Mal a > x und b < y. Siehe Abb. 18. Wie in den anderen Fällen ist dies nicht die einzige Korrekturmöglichkeit. 68 f(v0) f(vk) v0 vx vb va vy vk f(v0) v0 f(vk) va = vx vb = vy vk Abb. 18: Beispiel Fall 5. Oben sieht man den Fall 5, wo die Punkte a), b) und c) verletzt sind. Unten sieht man den die Korrektur, bei der a), b) und c) nach dem Hinzufügen von Knoten und Kanten zu den entsprechenden Subnetzen erfüllt sind. Die blauen und roten Kanten zusammen stellen den kürzesten Weg von v0 nach vk dar. Die blauen Kanten bezeichnen hierbei Landstraßen-Kanten und die roten Autobahn-Kanten. Die farbigen Flächen skizzieren jeweils die Stammsubnetze vom Startknoten und Zielknoten des aktuellen Pfades. Fall 6: x < k und a = -1 In diesem Fall (siehe Abb. 19) gibt es kein Autobahnstück zwischen dem Stammsubnetz von v0 und dem Stammsubnetz von vk. Wir könnten nun z.B. das Wegstück zwischen vx und vy zum High-Level-Subnetz hinzufügen, aber meistens ist es wahrscheinlich sinnvoller, dieses Stück zum Stammsubnetz von v0 hinzu zu fügen (oder alternativ zum Stammsubnetz von vk) und nur den Knoten vy zum High-Level-Subnetz hinzuzufügen, um das High-Level-Subnetz, welches bei fast jeder kürzesten-Weg-Berechnung benötigt wird, 69 nicht zu groß werden zu lassen. Also formal ausgedrückt, sorgen wir dafür, dass Folgendes erfüllt ist: a) Für alle 0 ≤ i ≤ y: ei ∈ E[f(v0)] (und dadurch natürlich auch vi ∈ V[f(v0)]), sprich, das erste Stück bis zum Index a muss vollständig zu f(v0) gehören. b) Der Punkt vy gehört zu V[H]. c) Für alle y < i ≤ k: ei ∈ E[f(v0)], also das letzte Wegstück (ab Index b) muss vollständig zum Stammstubnetz von vk gehören. f(v0) v0 vx f(vk) vk vy f(v0) v0 f(vk) vy = vx vk Abb. 19: Beispiel Fall 6. Oben sieht man den Fall 2, wo die Punkte a), b) und c) verletzt sind. Unten sieht man den die Korrektur, bei der a), b) und c) nach dem Hinzufügen von Knoten und Kanten zu den entsprechenden Subnetzen erfüllt sind. Die blauen und roten Kanten zusammen stellen den kürzesten Weg von v0 nach vk dar. Die blauen Kanten bezeichnen hierbei Landstraßen-Kanten und die roten Autobahn-Kanten. Die farbigen Flächen skizzieren jeweils die Stammsubnetze vom Startknoten und Zielknoten des aktuellen Pfades. Wir können also die gegen die Punkte (4) und (5) der Definition einer Einteilung eines Straßennetzes in Subnetze verstoßenden Fälle 1 b), 2 zum Teil, 3, 4, 5 und 6 lokal bei einem betrachteten Pfad P korrigieren, indem wir das Stamm-(Low-Level-)Subnetz des Startknotens von P, das Stamm-(Low-Level-)Subnetz des Zielknotens von P und das High-Level-Subnetz um Knoten und Kanten von P erweitern. Hat diese lokale Korrektur Einfluss auf andere (nicht gegen die Punkte (4) und (5) verstoßende) Pfade P’ in N bzw X? Höchstens positiven, d.h. es kann passieren, dass dadurch ein Pfad P’, der vorher gegen 70 (4) und (5) verstoßen hat, nun nicht mehr gegen diese Punkte der Definition verstößt (dies dürfte z.B. „häufig“ bei Pfaden P’, die P als echten Teilpfad enthalten, auftreten). Vorher nicht gegen (4) und (5) verstoßende Pfade bleiben „sauber“. Durch die bisherigen Überlegungen haben wir den Algorithmus vollständig hergeleitet und auch schon zum großen Teil bewiesen. Nachfolgend der zusammen fassende Pseudocode des Algorithmus: Algorithmus: SUBNETZ_EINTEILUNGS_GENERIERUNG Eingabe: a) ein Straßennetz N = (V, E, S, T, w, s, t) mit V = {1, ..., n} b) eine Menge von Landkreisen (Landkreis-Objekte) von N {P1, P2, ..., P|P|} c) ein Unterstraßennetz A von N (das Autobahnunternetz von N), eventuell vorbearbeitet mit dem Algorithmus AUTOBAHNNETZ-ERWEITERUNG d) eine Funktion g: V → {P1, ...., P|P|} Ausgabe: eine Einteilung von N in Straßensubnetze X = ({H}, {L1, L2, ..., L|P|}, f) Methode: // Initialisiere die vorläufigen Low-Level-Subnetze, High-Level-Subnetz und f p := |P|; for i := 1 to p do V[Li] := {v∈V | g(v) = Pi}; E[Li] := Ø; S[Li] := Ø; T[Li] := {Autobahn, Landstraße}; w[Li] := 0; s[Li] := 0; t[Li] := 0; od H := A; f := g; // Eigentlicher Algorithmus for i := 1 to n do for j := 1 to n, j ≠ i do betrachte den kürzesten Pfad P von i nach j in N; k := der Fall von P gemäß obiger Klassifizierung bzgl. N und Xcur := ({H}, {L1, L2, ..., Lp}, f); Behandele Fall k wie oben angegeben bzgl. P, N, Xcur, also füge H, f(i) und f(j) die beim Fall k angegebenen Knoten, Kanten, Straßen aus P hinzu und erweitere w[Li], s[Li] und t[Li] entsprechend; od 71 od X := ({H}, {L1, L2, ..., Lp}, f); return X; ڤ Die wichtigesten Punkte der Korrektheit des Algorithmus haben wir uns schon überlegt, fassen diese aber nochmal prägnant in folgendem Satz mit einer Laufzeitanalyse zusammen: Lemma 11: Der Algorithmus SUBNETZ-EINTEILUNGS-GENERIERUNG ist korrekt und lässt sich so implementieren, dass eine Laufzeit von O(n3) erreicht wird. Beweis: Bei der Korrektheit ist zu zeigen, dass am Ende für das zurückgegebene Tupel X die Punkte (1) – (5) der Definition einer Einteilung eines Straßennetzes in Subnetze erfüllt sind. Die Punkte (1) – (3) sind offensichtlich erfüllt. Die Punkte (4) und (5) sind aus folgenden zwei Gründen erfüllt: Zum einen betrachten wir jeden kürzesten Pfad innerhalb N (genau) einmal und falls ein solcher Pfad zum Fall 3, 4, 5 oder 6 gehört und damit gegen Punkt (4) oder (5) verstößt, erweitern wir die Subnetze immer derart, dass dieser Verstoß danach nicht mehr vorliegt (dies kann man in jedem einzelnen Fall der vollständigen Fallunterscheidung auf triviale Art und Weise überprüfen). Zum anderen kann es (natürlich) nicht passieren, dass eine Erweiterung eines Subnetzes bei der Bearbeitung eines kürzesten Pfades P für einen anderen kürzesten Pfad P’, der vor der Erweiterung die Punkte (4) und (5) erfüllt hat, danach die Punkte (4) und (5) nicht mehr erfüllt. Auf eine Laufzeit von O(n3) kommen wir z.B. durch folgende (hier nur skizzierte) Implementierung: Die Kantenmenge E von N wird repräsentiert durch Adjazenzlisten und N ist ansonsten bzgl. V, E, S, T, s, t hinreichend verpointert. Es gibt zu jedem Knoten und zu jeder Kante in N ein Array von Pointern auf die Subnetze (also hat das Array eine Größe von p+1) derart, dass v.SubnetzRef[SubnetzNr] = null ⇔ v kommt nicht im Subnetz mit der Nummer SubnetzNr vor und v.SubnetzRef[SubnetzNr] = x ≠ null ⇔ x ist Referenz auf den Knoten (besser gesagt, die Knotenkopie von) v im Subnetz mit der Nummer SubnetzNr. Dies ermöglicht uns, jeweils in O(1) zu überprüfen, ob eine bestimmte Kante oder Knoten schon in einem Subnetz vorkommt oder eine Referenz auf diesen Knoten / Kante zu bekommen. Die Zeit für die Initialisierung dieser Verpointerungen fällt asymptotisch nicht ins Gewicht. 72 Vor Ablauf des eigentlichen Algorithmus wird eine Floyd-Wege-Matrix berechnet (die hinreichend mit N verpointert wird), welche dann die kürzesten Wege speichert. Dies ist in Zeit O(n3) möglich. Durch die Floyd-Wege-Matrix bekommt man dann jeden kürzesten Pfad P (alle Knoten und Kanten) im Inneren der beiden while-Schleifen in Zeit O(|P|) 1. Durch die Verpointerungen können wir innerhalb von O(|P|) Zeit herausfinden, zu welchem Fall der aktuelle Pfad gehört und in nochmal O(|P|) Zeit die Subnetze entsprechend erweitern (durch die Verpointerungen sollte das Hinzufügen einer Kante oder eines Knotens in einem Subnetz jeweils nur O(1) Zeit in Anspruch nehmen)2. Also brauchen wir für die Überprüfungen aller Pfade insgesamt O(n2 α) = O(n3) Zeit, wobei α die durchschnittliche ungewichtete Länge eines kürzesten Pfades in N ist. Insgesamt dominiert hier also das O(n3) der Anwendung des Algorithmus von Floyd. ڤ Was wir noch nicht angesprochen haben ist, ob bzw. was uns eine Vorbearbeitung des Autobahnunternetzes mit dem Algorithmus AUTOBAHNNETZ_ERWEITERUNG bringt. Eine erste Beobachtung dazu ist, dass es trotz Vorbearbeitung des Autobahnunternetzes passieren kann, dass das Autobahnnetz noch weiter im Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG erweitert werden muss, weil dieses nur gegenüber den ursprünglichen Knoten des Autobahnunternetzes bzgl. kürzester Wege abgeschlossen ist 3. Siehe dazu Abb. 20: Man sieht, dass bei Verwendung von dem Algorithmus AUTOBAHNNETZ_ERWEITERUNG vor Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG das resultierende High-LevelSubnetz immer größer oder gleich groß ist, als wenn man das Autobahnsubnetz zuvor nicht mit dem Algorithmus AUTOBAHNNETZ_ERWEITERUNG vorbearbeitet. Andersherum ist es aber denkbar, dass die Low-Level-Subnetze durch ein etwas größeres High-LevelSubnetz deutlich kleiner sind. Im Abschnitt „Güte der Heuristik“ sind zwei (konstruierte) Beispiele zu sehen, wo sich einmal die Verwendung von AUTOBAHNNETZ_ERWEITERUNG lohnt und das andere mal ungünstig ist. 1 Da wir hier jeden Knoten und jede Kante auf Zugehörigkeiten zu den Subnetzen überprüfen müssen und eventuell zu Subnetzen hinzufügen müssen, bringt uns hier die Variante der Floyd-Matrix nichts. Abgesehen davon, braucht 3 die Berechnung der Floyd-Matrix ohnehin schon O(n ) Zeit. 2 Natürlich lässt sich das auch alles auf einmal in O(|P|) machen. 3 Streng genommen könnte man in solchen Fällen natürlich auch die Low-Level-Subnetze erweitern. Dies dürfte meistens aber eher ungünstig sein, vermute ich, weil diese dadurch sehr vergrößert werden können. Siehe Abb. ? 73 f(v0) M f(vk) v0 vx = va Q vk vy = vb O P R Abb. 20: Beispiel für Fall 2 nach AUTOBAHNNETZ_ERWEITERUNG. Die Autobahnstücke von M bis Q und M bis R sind keine ursprünglichen, sondern sind im Verlauf des Algorithmus AUTOBAHNNETZ_ERWEITERUNG entstanden, weil sie kürzeste Pfade zwischen M und Q bzw. R sind. Weil O und P daher auch keine ursprünglichen Autobahnknoten sind, wird der kürzeste Weg zwischen O und P nicht im Verlauf des Algorithmus AUTOBAHNNETZ_ERWEITERUNG zum Autobahnnetz hinzugefügt. Dieser muss dann noch im Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG zum High-Level-Subnetz hinzugefügt werden. 5.2.3 Güte der Heuristik In diesem Abschnitt wollen wir ein paar Beispiele betrachten, um zu sehen, wie schlecht die Heuristik SUBNETZ_EINTEILUNGS_GENERIERUNG sein kann. Die den Beispielen zugrunde liegenden Straßennetze, die Partitionierung in Landkreise und die Auswahl des Autobahnnetzes sind alle konstruiert. Daher werden wir nach Betrachtung der Beispiele jeweils diskutieren, inwieweit solche Fälle bei realen Straßennetzen zu erwarten sind 1. Es ist zu bemerken, dass dieser Abschnitt bei weitem keinen Anspruch auf Vollständigkeit erhebt. Wir betrachten nicht die einzelnen Kriteriumsfunktionen aus Abschnitt 5.2.1 und wie genau der Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG diese erfüllt, sondern es geht nur darum durch konstruierte Beispiele einzelne Schwächen des Algorithmus aufzuzeigen und zu diskutieren, ob diese bei realen Straßennetzen in größerem Maße zu erwarten sind. Hierzu ist im Zusammenhang mit Abschnitt 5.2.1 auch zu bemerken, dass wir hier den Speicherplatzbedarf mit Hilfe der theoretischen Kardinalität bemessen und keine konkreten Datenstrukturen für D(▪) betrachten. Betrachten wir nun folgendes erste Beispiel, welches im Ggs. zu den darauf folgenden noch sehr ausführlich und formal beschrieben ist (siehe die Abb. zum Verständnis): Beispiel (1) Sei N(z) := (V, E, S, T, w, s, t) das zugrunde liegende Straßennetz mit 1 Leider fehlte die Zeit, um die Heuristik anhand von realen Straßennetzen empirisch zu untersuchen. Dies ist sehr schade, da bei Heuristiken solche Untersuchungen mit am wichtigsten ist. 74 • V = {v1, v2, ..., vz, v’1, v’2, ..., v’z} • E = {(vi, vi+1), (vi+1, vi) | 1 ≤ i ≤ z} ∪ {(v’i, v’i+1), (v’i+1, v’i) | 1 ≤ i ≤ z} ∪ {(vz, v’1)} ∪ {( v’1, vz)} • S = {A1}, • T = {Autobahn}, • w(e) = 1, s(e) = A1 für alle e∈E, • t(A1) = Autobahn Betrachte nun folgende Auswahl der Partitionen / Landkreise und des vorläufigen HighLevel-Subnetzes / Autobahnnetz A: • P = {P1, ..., Pz} und g(vi) = Pi und g(v’i) = Pi, • A = N(z) Wenn man auf diese Eingabe den Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG laufen lässt, ergibt sich eine Einteilung in Subnetze X = ({H}, {L1, L2, ..., Lz}, f) mit • f = g, • H = N(z), • Li = (V[Li], E[Li], S[Li], T[Li], w[Li], s[Li], t[Li]) für 1 ≤ i ≤ z mit • V[Li] = {vi, vi+1, ..., vz, v’1, v’2, ..., v’i}, • E[Li] = {(vj, vj+1), (vj+1, vj) | i ≤ j ≤ z} ∪ {(v’j, v’j+1), (v’j+1, v’j) | 1 ≤ j ≤ i} ∪ {(vz, v’1)} ∪ {( v’1, vz)} • S[Li], T[Li], w[Li], s[Li], t[Li]) analog zu den Funktionen in N (mit teilweise eingeschränktem Definitionbereich). Also gilt für alle i: |V[Li]| = z + 1 = n / 2 + 1 = O(n) und |E[Li]| = z = n / 2 = O(n). Insgesamt ergibt sich wegen der Anzahl der Subnetze ein Speicherplatzbedarf von O(n2) Ein einfaches (unhierarchisches) Speichern des Graphen geht in O(n). Oder wenn wir dem dem Algorithmus anfangs n Paritionen, die jeweils einen Knoten enthalten, übergeben hätten, wäre dies auch in O(n) möglich gewesen. Siehe Abb. 21: v1 v2 vz-1 vz v’1 v’2 v’z-1 v’z Abb. 21: Abbildung für Beispiel 1. Hierzu ist zu bemerken, dass die einzelnen Farben die anfängliche Partitionierung sichtbar machen. Am Ende gehört jeder Knoten und jede Kante zu jedem Highund Low-Level-Subnetz, was absolut unakzeptabel ist. Hierzu gibt es folgendes zu bemerken und zu interpretieren: 75 Der Grund dafür, dass die Low-Level-Subnetze so groß werden, liegt in Punkt (5) der Definition einer Einteilung eines Straßennetzes in Subnetze und diesbezüglich unsinnigen Wahl der Partitionen in diesem Beispiel. Dieser Punkt (5) impliziert nämlich eine gewisse Art von Konvexität bzgl. kürzester Wege innerhalb der Subnetze, die in diesem Beispiel nicht gegeben ist. Es fällt aber bei diesem Beispiel auf, dass gerade hier, wegen des großen High-LevelSubnetzes diese Konvexität eigentlich nicht notwendig ist, da man die Wege innerhalb des Low-Level-Subnetzes auch über das High-Level-Subnetz berechnen kann. Dementsprechend könnte man die Definition etwas abschwächen und den Algorithmus zur Bestimmung kürzester Wege ein wenig verändern. Ich habe mich aber dagegen entschieden, weil ich finde, dass zumindest jeweils ein kürzester Weg zwischen Knoten, die das gleiche Stammsubnetz haben, in diesem Stammsubnetz enthalten sein soll. Hauptgrund für das schlechte Abschneiden des Algorithmus in diesem Beispiel ist er aber nicht unbedingt selber1, sondern eben die schlechte Eingabe der Partitionen. Bei Landkreisen in realen Netzen z.B., an denen sich auch der Straßenbau ein wenig orientiert, ist so etwas (hoffentlich) nicht zu erwarten. Die folgenden Beispiele werden wir – im Interesse des Lesers – auf viel informellere und intuitivere Weise betrachten und diese hauptsächlich anhand der Abbildungen erklären. M.E. ist diese Darstellung aber immer noch eindeutig. Beispiel (2) Siehe Abb. 22 zu diesem Beispiel, welche das wieder von der Variable z abhängige Straßennetz m.E. eindeutig definiert: v’1 v’2 1 1 1 1 v1 30 v’z-1 1 1 v2 30 30 v’z 1 1 vz-1 30 vz 1 In gewisser Hinsicht natürlich schon, denn im Idealfall würde er ja gar nicht so eine Eingabe erwarten und einfach nur durch die Eingabe eines Straßennetzes eine z.B. bzgl. der in Abschnitt 5.2.1 angegebenen Kriteriumsfunktionen optimale Einteilung eines Straßennetzes in Subnetze herleiten. 76 Abb. 22: Abbildung für Beispiel 2. Hierzu ist zu bemerken, dass die einzelnen Farben die anfängliche Partitionierung sichtbar machen. Die Zahlen an den Kanten entsprechen den Kanten-gewichten. Rote Kanten gehören dem vorläufigen High-Level-Subnetz / Autobahnunternetz an und schwarze nicht (sind vom Typ Landstraße). Wenn man auf dieser Eingabe den Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG laufen lässt, ergibt sich wieder, dass jeder Knoten und jede Kante bis auf die AutobahnKanten zu jedem Low-Level-Subnetz gehört. Das Autobahnsubnetz bleibt so erhalten. Dieses äußerst schlechte Ergebnis kommt wieder durch eine sehr schlechte Eingabe zustande. Die Einteilung in Partitionen ist hier nicht unbedingt so ungünstig, aber dieses Mal ist die Wahl des Autobahnunternetzes / High-Level-Subnetzes sehr schlecht, denn der kürzeste Weg von vi nach vi+1 ist über die Landstraßen 10 mal kürzer als über Autobahn. Wenn man statt dieser Autobahnkanten die Kanten zwischen den gestrichelten v – Knoten nehmen würde (die in der Abbildung dazu parallelen Kanten), dann wäre die Einteilung wohl bzgl. fast jeder halbwegs sinnvollen Kriteriumsfunktion viel besser. Hier erweist sich hier die Vorbearbeitung des hier so ungünstigen Autobahnstücks mit dem Algorithmus AUTOBAHNNETZ_ERWEITERUNG als nützlich. Durch diese wird das High-Level-Subnetz zwar größer (es enthält dann alle Kanten und Knoten) – aber nicht asymptotisch1 – aber die Low-Level-Subnetze bleiben so wie die vorläufige Partitionierung und damit verringert sich der gesamte Platzbedarf asymptotisch um Faktor n. Beispiel (3) Dieses Beispiel soll zeigen, dass die Verwendung der Vorbearbeitung des Autobahnunternetzes sich auch in asymptotischer Größenordnung als negativ erweisen kann: (siehe Abb. 23) v1 v2 v3 vl vl+1 vl+2 vl+3 vz-2 vz-1 vz 1 Man kann sich überlegen, dass das Autobahnnetz bei realen Straßennetzen wohl immer in linearem Zusammenhang zum Gesamtstraßennetz steht. Es ist dann zwar vielleicht 100 mal kleiner, aber wenn man das Gesamtstraßennetz um Faktor x vergrößert (Anzahl Knoten und Kanten) , dann wird wohl das Autobahnnetz auch in etwa um x größer werden. 77 Abb. 23: Abbildung für Beispiel 3. In diesem Beispiel gibt es eine schwarz eingezeichnete Partition und eine gelb eingezeichnete Partition zu Beginn des Algorithmus. Es gibt genau drei rot eingezeichnete Autobahn-Kanten. Die restlichen z-4 Kanten sind alle vom Typ Landstraße. Der Algorithmus AUTOBAHNNETZ_ERWEITERUNG generiert für diese Einteilung unnötigerweise ein High-Level-Subnetz, welches alle Knoten und Kanten dieses BeispielGraphen enthält und demnach Komplexität von Θ(n) hat. Unnötigerweise deshalb, weil die beiden Low-Level-Subnetze innerhalb sich selbst ja die kürzesten Wege ohne die Erweiterung berechnen können und für die (kürzesten) Wege in das andere Subnetz ja das eine, die beiden Low-Level-Subnetze verbindende Autobahnstück, ausreicht. Ohne die Vorbearbeitung würde der Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG ein High-Level-Subnetz der Größe O(1) zurückgeben. Warum ist gerade hier die Erweiterung des Autobahnnetzes um einen kürzesten Weg zwischen zu Autobahn-Kanten inzidenten Knoten nachteilhaft? Der Grund dafür liegt darin, dass hier nur zwei Low-Level-Subnetze da sind. Wäre „links“ von dem schwarzen Subnetz noch ein derartig „1-dimensional“angelegtes Subnetz oder „rechts“ von dem gelben, dann würde das Autobahnsubnetz sowieso vom Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG so ähnlich erweitert werden. Trotzdem sind natürlich auch in der Realität solche Fälle möglich, wenn auch wohl kaum in dieser Größenordnung. Beispiel (4) Mit diesem Beispiel möchte ich zeigen, dass es auch bzgl. hierarchischer Datenstrukturen oder zumindest bzgl. der von uns definierten Einteilung eines Straßennetzes in Subnetze auch inhärent schlecht geeignete Eingabe-Graphen bzw. Straßennetze gibt: (siehe Abb. 24) In diesem Beispiel besteht jeder kürzester Weg aus nur jeweils einer und – jedesmal einer verschiedenen – Kante. Hier macht m.E. ein hierarchisches Verfahren ähnlich wie unseres keinen Sinn. Treten so ein Beispiel in der Praxis bei realen Straßennetzen auf? Nein. In realen Straßennetzen würde schon allein aus Kostengründen wohl kaum für jedes Paar von Knoten eine eigene Straße gebaut werden. Wir können eigentlich fast immer davon ausgehen, dass in realen Straßennetzen die Anzahl der Kanten in linearer Relation mit der Anzahl der Knoten steht. 78 v1 vz v2 vz-1 v3 ... Abb. 24: Abbildung für Beispiel 4. In diesem Beispiel ist ein vollständiger Graph für die Knotenmenge {v1, ..., vz} zu sehen. Jede Kante habe Gewicht von 1. Ich habe keine Partitionen und Autobahnen eingezeichnet, weil sie hier definitiv keinen Sinn machen (siehe Text). 79 Beispiel (5) Dieses letzte Beispiel soll zeigen, dass dieses hierarchische Verfahren auch wirklich sinnvoll sein kann und stellt einen Best-Case dar. Das Straßennetz N(z) ist in der folgenden Abb. (25) angegeben (c konstant): v 1c −1 v 1c v 22 v c2−1 v 12 v11 v12 vcz v1z v1z −1 v 2z −1 v cz−1 vcz −1 v 2z vc2 vcz−−11 Abb. 25: Abbildung für Beispiel 5. In diesem Beispiel-Straßennetz N(z) gebe es z, hier farblich gekennzeichnete, Partitionen. Es gibt genau z rot eingezeichnete Autobahn-Kanten und zc blau eingezeichnete Landstraßenkanten. Insgesamt gibt es zc Knoten, wobei c konstant sei. Nach Ausführung des Algorithmus SUBNETZ_EINTEILUNGS_GENERIERUNG besteht das High-Level-Subnetz H aus den ursprünglichen z Autobahnkanten und die z Low-LevelSubnetze bestehen gemäß der Partitionierung aus jeweils c Knoten und c Kanten. Also hat das High-Level-Subnetz Komplexität von O(z) und die z Low-Level-Subnetze jeweils O(1). Ich nehme an, dass dieser Fall in etwa dem Normalfall für reale Straßennetze (entsprechend große) entspricht. Warum ich dies denke und was hieran so gut ist, diskutieren wir in Abschnitt 5.3.2, wo wir zu dem Hauptergebnis dieser Diplomarbeit kommen. Fazit Die ersten vier Beispiele haben gezeigt, dass der Algorithmus SUBNETZ_EINTEILUNGS _GENERIERUNG bei eventueller Vorarbeit von dem Algorithmus AUTOBAHNNETZ _ERWEITERUNG den benötigten Speicherplatz auf das quadratische vergrößern kann. In den Diskussionen zu den einzelnen Beispielen sind wir zu dem Ergebnis gekommen, dass solche Fälle bei Eingabe von realen Straßennetzen mit Verwaltungsstrukturen wie z.B. Landkreise und dem eventuell vorbearbeiteten Autobahnunternetz zumindest eher selten 80 vorkommen. Es gibt gute Argumente dafür, dass der Best-Case normalerweise bei realen Straßennetzen ungefähr eintritt, welche wir in Abschnitt 5.3.2 betrachten. 5.3 Verwendung der Variante der Floyd-Wege-Matrix In Abschnitt 5.1 haben wir ein Verfahren kennengelernt, welches, gegeben eine Einteilung eines Straßennetzes in Subnetze, kürzeste Wege berechnen kann. Im nachfolgenden Abschnitt haben wir dann einen Algorithmus hergeleitet, der uns aus einem Straßennetz, einer Menge von Verwaltungsbezirken des Straßennetzes – z.B. Landkreise – (bzw. beliebige Straßenunternetze) und einem Autobahnnetz (bzw. beliebiges Straßenunternetz) eine solche Unterteilung des Straßennetzes in Subnetze liefert. Bisher haben wir kaum die Datenstrukturen zur Speicherung der Straßensubnetze betrachtet. Das und insbesondere die Verwendung der Variante der Floyd-Wege-Matrix aus Kapitel 4 als Datenstruktur ist Thema dieses Abschnitts. 5.3.1 Datenstrukturen Was für Datenstrukturen kann man für die Speicherung der Straßensubnetze einer Einteilung eines Straßennetzes N = (V, E, S, T, w, s, t) in Straßensubnetze X = ({H}, {L1, L2, ..., Lp}, f) verwenden? Für unsere Zwecke im Prinzip alle, die es dem Algorithmus HIERARCHISCHE_WEGBERECHNUNG ermöglichen zu laufen. Konkret muss jedes Subnetz (sowohl High- als auch Low-Level) folgende Informationen zurückgeben bzw. berechnen können: • (alle) kürzeste Wege zwischen Knoten des jeweiligen Subnetzes • (alle) kürzesten Distanzen δ(i, j) zwischen Knoten i, j aus dem jeweiligen Subnetz Dazu müssen das High-Level-Subnetz und die Low-Level-Subnetze noch folgende Informationen zurückgeben bzw. herleiten können: • welche Knoten in den jeweiligen Low-Level-Subnetzen bzw. in dem High-LevelSubnetz Zugänge in das High-Level-Subnetz bzw. die jeweiligen Low-LevelSubnetze sind, also die Mengen V[Li] ∩ V[H], 1 ≤ i ≤ p. • Die Zugänge müssen noch entsprechend verpointert bzw. entsprechende sonstige Referenzen vorhanden sein (hier gibt es verschiedene Möglichkeiten), da in der Praxis wohl ein Knoten u∈V[Li] ∩ V[H] durch zwei verschiedene Knoten v und w repräsentiert wird mit v aus Li und w aus H und man in der Lage sein muss, aus v w ermitteln zu können. Dies ist z.B. durch eine Referenz von Knoten v im LowLevel-Subnetz Li zum Knoten w im High-Level-Subnetz H möglich. 81 Im einfachsten Fall kann man die Subnetze wie gerichtete Graphen mit den Kanten als Adjazenzlisten in den Knoten speichern1 und dazu noch Datenstrukturen für die Zugänge von den Low-Level-Subnetzen zum High-Level-Subnetz und umgekehrt (ähnlich wie für die Variante der Floyd-Wege-Matrix, s.u.) verwenden. Zur Ermittlung der kürzesten Wege innerhalb der Subnetze kann man dann z.B. den Algorithmus von Dijkstra verwenden. Wir wollen nun als Datenstruktur für die Speicherung der Subnetze die Variante der Floyd-Wege-Matrix aus Kapitel 4 benutzen. Um die folgenden Ausführungen unmissverständlich auf den Punkt zu bringen, werden wir jetzt zum Teil auf eine niedrigere Abstraktionsebene – von der bisher zum größten Teil konzeptuellen Ebene nun in die Nähe der Implementationsebene – übergehen. Folgende Implementierung der Datenstrukturen werden wir im Rest dieses Kapitels 5 betrachten: Wie zuvor sei N = (V, E, S, T, w, s, t) das zugrunde liegende Straßennetz und X = ({H}, {L1, L2, ..., Lp}, f) eine Einteilung von N in Straßensubnetze. Jedes Subnetz (sowohl high- als auch low-level) Li = (V[Li], E[Li], S[Li], T[Li], w[Li], s[Li], t[Li]) sei folgendermaßen in einer Implementation repräsentiert: • wie ein „normaler“ gewichteter, gerichteter Graph Gi := (V[Li], E[Li], w[Li]) mit der Kantenmenge E[Li] als Adjazenzlisten der Knoten • die Mengen bzw. Funktionen S[Li], T[Li], s[Li], t[Li] seien in der Implementierung des obigen Graphen Gi irgendwie „eingebaut“ bzw. verpointert, so dass man in O(1) Zeit bestimmen kann, welcher Straße eine Kante angehört und was für einen Straßentyp eine bestimmte Straße hat. • der Variante der Floyd-Wege-Matrix Π’, die alle kürzesten Wege innerhalb von Li speichert und diese speichere zusätzlich in jedem Matrix-Eintrag πij neben dem k (siehe Kapitel 4) noch eine Referenz auf die Kante auf dem kürzesten Weg von i nach j direkt vor k und die Kante direkt nach k, falls diese exisitieren und nil sonst • einer Floyd-Distanz-Matrix D, welche alle kürzesten Distanzen δ(i, j) für i, j∈ Li speichert Zusätzlich enthalte jedes Low-Level-Subnetz Li = (V[Li], E[Li], S[Li], T[Li], w[Li], s[Li], t[Li]) eine Datenstruktur, welches ihr ermöglicht, alle |V[Li] ∩ V[H]| Zugänge zum HighLevel-Subnetz H in O(|V[Li] ∩ V[H]|) Zeit zu bestimmen und in jeweils O(1) Zeit auch eine Referenz zum entsprechenden Knoten im High-Level-Subnetz zu erhalten. 1 Hierbei und müssen natürlich noch die gegenüber (normalen) gerichteten Graphen zusätzlichen Informationen von Straßennetzen eingebaut werden, also S, T, s, t. Dies dürfte aber in den meisten Fällen eher trivial sein. 82 Analog enthalte das High-Level-Subnetz eine Datenstruktur, welche die Zugänge zu den Low-Level-Subnetzen speichert, so dass diese jeweils in O(1) inkl. Referenz auf den jeweils entsprechenden Low-Level-Knoten ermittelt werden können. Zudem sei für jeden Knoten v∈V klar (in O(1) Zeit), zu welchem Stamm-Subnetz es gehört (also die Funktion f sei irgendwie in das Gesamt-Straßennetz eingebaut). Welche Möglichkeiten gibt es dafür? Ein Array wäre eine Möglichkeit und Hashing eine andere. Im ersten Fall benötigt man Θ(n) Speicherplatz und bei der zweiten Möglichkeit weniger (abhängig von dem verwendeten Verfahren), aber die Zugriffszeiten sind nur noch im Erwartungswert über eine Menge von Zugriffen konstant. Im weiteren Verlauf betrachten wir der Einfachheit halber nur die Array-Variante, weil diese auch schon sehenswerte Resultate bringt. Außerdem muss im Subnetz herausfindbar sein, ob bei einer kürzeste-Weg-Anfrage der Zielknoten j im Stammsubnetz f(i) des Startknotens i enthalten ist. Dies geht einfach Überprüfung aller Knoten in f(i). Zumindest erhofften best-case stellt dies keinen asymptotischen Overhead gegenüber komplizierteren Möglichkeiten wie z.B. Hashing dar. Man sieht, dass diese Beispiel-Implementation (wobei es hier auch noch einige Freiheitsgrade gibt) einiges an Redundanz aufweist. Asymptotisch lässt sich der Speicherplatz-Bedarf allerdings nicht reduzieren, da die Variante der Floyd-Wege-Matrix oder die Floyd-Distanz-Matrix mit Θ(|V[Li]|) den Speicherplatzbedarf für ein Subnetz Li dominiert. Daher werden wir ab jetzt diese Datenstrukturen betrachten statt eine bis ins (nicht-asymptotische) Detail optimierte Implementation. Nachdem wir nun wissen, wie wir die Variante der Floyd-Wege-Matrix in unsere Datenstrukturen eingebaut haben, wollen wir im nächsten Abschnitt betrachten, was uns dies bringt. 5.3.2 Performanz und Komplexität Im vorherigen Abschnitt haben wir uns auf einer schon der Programmierung nahen Ebene Datenstrukturen für die Subnetze einer Einteilung eines Straßennetzes N in Subnetze X bei Verwendung der Variante der Floyd-Wege-Matrix überlegt. Nun wollen wir untersuchen, was für eine Laufzeit sich damit für den Algorithmus HIERARCHISCHE_WEGBERECHNUNG ergibt und wie der Gesamt-Speicherplatzbedarf der Subnetze von X ausfällt. Nach Satz 5 benötigt der Algorithmus HIERARCHISCHE_WEGBERECHNUNG O(xyz + ρ) Zeit für die Berechnung eines kürzesten Weges, wobei • x = |V[L(1)] ∩ V[H]| ist, • y = |V[L(2)] ∩ V[H]| ist, 83 • z = z(1) + zH + z(2) und z(1) die Dauer (obere Schranke) der Berechnung einer kürzesten Distanz δ L(1) , zH die Dauer (obere Schranke) der Berechnung einer kürzesten Distanz δ H , z(2) die Dauer (obere Schranke) der Berechnung einer kürzesten Distanz δ L( 2 ) ist, • ρ = ρ (1) + ρ H + ρ (2) und ρ (1) die Dauer (obere Schranke) eines kürzesten Weges in L(1), ρ H die Dauer (obere Schranke) der Berechnung eines kürzesten Weges in H, ρ (2) die Dauer (obere Schranke) der Berechnung kürzesten Weges in L(2) ist. Bei Verwendung der Floyd-Distanz-Matrix ist z = O(1). Da die (Variante der) FloydWege-Matrix lineare Zeit |P| für die Ausgabe eines Pfades P benötigt, beträgt die Zeitdauer ρ für die Berechnung des kompletten Weges P bei der (Variante der) FloydWege-Matrix Θ(P). Da wir noch herausfinden müssen, ob der Zielknoten im Stammsubnetz des Startknotens liegt, ergibt dies eine Gesamtlaufzeit von O(xy + |P| + |V[f(i)]|). Für Anfragen, die nicht den ganzen kürzesten Pfad erfragen, sondern nur einen Teil dieser Information, lässt sich ρ etwas verallgemeinern. Und zwar bezeichnen wir mit ρ die Zeitdauer der Bearbeitung einer Anfrage, die (nur) den kürzesten Weg P von i nach j betrifft. Bei Verwendung der normalen Floyd-Wege-Matrix, wie in Kapitel 2 definiert, beträgt die Laufzeit ρ für die meisten1 Anfragen, die den kürzesten Weg P betreffen, Θ(P), auch wenn sie durch weniger Informationen als durch den ganzen Weg P berechenbar sind. Dies liegt daran, dass es mehr oder weniger „zufällig“ ist, wie P in der Floyd-Wege-Matrix abgespeichert ist (bzw. es in der Matrix sehr viele Freiheitsgrade gibt) und man beim Auslesen der Wurzel eines Matrix-Wegbaumes nicht viel über die beiden Unterbäume erschließen kann. Bei Verwendung der Variante der Floyd-Wege-Matrix dagegen gibt es schon Anfragen, die zu dieser Matrix passen und Laufzeiten ρ von o(|P|) haben. Wenn man beispielsweise die Straßennamen aller Straßen des kürzesten Weges P von i nach j herausfinden möchte, dann geht das in Laufzeit ρ = Θ(Anzahl Straßenwechselpunkte von P) was gerade bei einer hohen Granularität der Modellierung (siehe Kapitel 3) meistens deutlich kleiner sein wird als das Durchlaufen des gesamten Matrix-Wegbaumes von P. Wie gering ρ gegenüber |P| wird, hängt von der Art der Anfrage und dem Weg P ab und sind im WorstCase immer gleich, weil P z.B. nur aus einer Kante bestehen kann. Wie groß kann xy sein? Im Worst-Case n2. In der Praxis, wenn man diese Algorithmen und Datenstrukturen für wirkliche Straßennetze verwendet, ist dies wohl sehr 1 Es gibt natürlich noch Sonderfälle von Anfragen, die auch eine normale Floyd-Matrix in Zeit o(|P|) beantworten kann, welche aber meistens vermutlich weniger praktische Relevanz haben. Ein Beispiel dafür ist die Anfrage „Enthält der Pfad P (bzw. der kürzeste Pfad von i nach j) genau eine Kante?“. Diese Anfrage ist z.B. immer in O(1) beantwortet, was bei Anfrage-Antwort „nein“ kleiner als |P| ist. 84 unwahrscheinlich, da man erwarten kann, dass das zugrunde liegende Straßennetz halbwegs sinnvoll in Landkreise oder sonstige Partitionen eingeteilt ist und das Autobahnunternetz auch sinnvoll angelegt und nicht viel zu groß ist. Ich vermute, dass xy bei praktischen Straßennetzen deutlich kleiner als V[L(1)] und V[L(2)] ist, weil der Anteil der Kanten (bzw. Knoten), die zum High-Level-Subnetz gehören, sehr gering ist. Wenn dem nicht so ist, lohnt sich ein solches hierarchisches Netz weder zur Bestimmung kürzester Wege noch als Speicherstruktur. Für den Speicherplatz der Datenstrukturen gilt das, was wir in Abschnitt 5.2.4 bzgl. der Güte der Heuristik festgestellt haben – allerdings im Quadrat –, denn die Variante der Floyd-Wege-Matrix braucht (genau wie die normale Floyd-Wege-Matrix) für ein (Unter-) Straßennetz mit n Knoten Θ(n2) Speicherplatz. Außerdem brauchen wir noch Θ(n) Speicherplatz für die Implementierung der Funktion f mittels eines Arrays, welche hier aber von der Größenordnung her nicht ins Gewicht fällt. Dies alles ergibt dann bei einer Einteilung eines Straßennetzes N = (V, E, S, T, w, s, t) in Subnetze X = ({H}, {L1, L2, ..., Lp}, f) folgenden Speicherplatzbedarf bei Verwendung der in Abschnitt 5.3.1 beschriebenen Datenstrukturen: 2 Θ(|V[H]| ) + p ∑ Θ(|V[Li]|2) i =1 Im Worst-Case sind |V[H]| und V[Li]| jeweils Θ(n). Dies ergibt dann folgende Gesamtgröße der Datenstrukturen: Θ((p + 1) n2). p kann, wie wir gesehen haben, auch im Bereich von Θ(n) liegen. Dies würde einen Worst-Case-Speicherplatzbedarf von Θ(n3) ergeben. Würde in praktischen Anwendungen ein Speicherplatzbedarf von Θ(n3) auftreten? Wohl kaum. Viel eher ist folgendes Szenario möglich, dass leider, wie die meisten solcher Überlegungen in der Diplomarbeit, nicht an praktischen Beispielen überprüft wurde, sondern nur auf Vermutungen, ein wenig logischem Denken und Spekulationen beruht. Ab jetzt gehen wir davon aus, dass, wie bei realen Straßennetzen, die Anzahl der Kanten m in linearen Zusammenhang mit der Anzahl der Knoten des betrachteten Straßennetzes stehen, also m = O(n): Sei N1 das Straßennetz von Deutschland und es sei in Subnetze X1 eingeteilt. Das Straßennetz N2 von Frankreich sei ebenfalls in Subnetze X2 eingeteilt. Die Low-LevelSubnetze von X1 hätten im Durchschnitt (einfach das arithmetische Mittel) die Größe d1 und die Low-Level-Subnetze von X2 die Durchschnittsgröße d2. Angenommen, man würde nun ein konsolidiertes Straßennetz N3 erstellen, welches aus den beiden Straßennetzen N1 und N2 besteht (und Kanten, die von N1 nach N2 gehen und umgekehrt) und man würde es in Subnetze X3 einteilen. Für X3 kann man höchstwahrscheinlich mehr oder weniger die alten Low-Level-Subnetze von X1 und X2 nehmen und für das High-Level-Subnetz H3 mehr oder weniger eine Vereinigung der beiden High-Level-Subnetze H1 von X1 und H2 von X2. Damit ist die Durchschnittgröße d3 85 der Low-Level-Subnetze von H3 in etwa gleich d1 und d2. Dies ist eigentlich auch ganz logisch, denn Straßennetze können ja (in der Praxis) nicht beliebig „dicht“ (hiermit ist nicht die Dichtheit von Graphen gemeint) werden, sondern wachsen in erster Linie in der Größe der bedeckten Fläche. Das heisst, ab einer bestimmten Größe von Straßennetzen bleibt die Durchschnittgröße der Low-Level-Subnetze einer Subnetzeinteilung in etwa konstant. Also können wir folgern, dass demnach auch der Speicherplatzbedarf für ein einzelnes Subnetz O(1) beträgt. Der Gesamtspeicherbedarf für die Low-Level-Subnetze beträgt dann Θ(p), wenn p die Anzahl der Low-Level-Subnetze ist. Somit haben wir trotz Verwendung an sich quadratischer Datenstrukturen (Variante der Floyd-Wege-Matrix) mit allen ihren Vorteilen einen nur linearen Speicherplatzverbrauch! Allerdings gilt dies nicht für das Autobahnsubnetz, denn dieses wächst wohl zwar von der theoretischen Kardinalität der Knoten und Kanten genauso linear wie die Anzahl der LowLevel-Subnetze, ist aber nicht so aufgeteilt, dass wir bei quadratischen Datenstrukturen auf einen nur linearen Speicherplatzverbrauch kommen1. Wie beim worst-case fällt auch hier der für die Implementierung von f benötigte Speicherplatz beim Gesamtspeicherplatz nicht ins Gewicht. Insgesamt würde dies dann einem Gesamt-Speicherplatzverbrauch von Θ(|V[H]|2 + p) praktische Straßennetze = Θ(|V[H]|2) entsprechen. Dieses H dürfte (hoffentlich ist das Autobahnnetz gut angelegt) auch nicht viel größer als das ursprüngliche Autobahnunternetz sein. Streng genommen ist natürlich das Autobahnunternetz sicherlich auch linear in Anzahl der Gesamtknoten, so dass Θ(|V[H]|2) = Θ(n2) asymptotisch gilt. Es dürfte aber für praktische Anwendungen einen Unterschied machen, ob wir z.B. bei einem (hypothetischen) Straßennetz mit 100000 Knoten insgesamt und 10000 Knoten des Autobahnunternetzes insgesamt 1000002 Speicherplatz brauchen oder 100002. Wenn die Low-Level-Subnetze Größe von O(1) haben, dann sind natürlich auch xy und |V[f(i)]| in der Größenordnung von O(1), wodurch sich für die Beantwortung einer Anfrage, welche nur einen kürzesten Weg P betrifft, sich folgende Laufzeit ergibt: Θ (ρ) ( = O(|P|) ) 1 Hier würde es sich natürlich anbieten, eine weitere Hierarchie-Ebene im Autobahn- / High-Level-Subnetz einzuführen, sobald der Speicherplatzbedarf des High-Level-Subnetzes größer wird als der eines einzelnen Low-LevelSubnetzes. 86 5.3.3 Mögliche praktische Anwendungen Im vorherigen Abschnitt haben wir für den worst-case und dem erhofften Fall analysiert, wie schnell wir mit Hilfe einer Einteilung eines Straßennetzes in Subnetze, generiert durch den Algorithmus SUBNETZ-EINTEILUNGS-GENERIERUNG, und dem Algorithmus HIERARCHISCHE_WEGBERECHNUNG kürzeste-Weg-Anfragen beantworten können und wieviel Speicherplatz für die Einteilung des Straßennetzes in Subnetze erforderlich ist. Nun wollen wir uns überlegen, für was für eine Art von praktischer Anwendung dieses Verfahren geeignet ist. Dabei betrachten wir nur den erhofften Fall einer wirklich guten Einteilung des zugrundeliegenden Straßennetzes in Subnetze 1. Dazu wollen wir unser hierarchisches Verfahren mit den Algorithmen von Floyd und Dijkstra vergleichen, siehe Tab. 6: Dijkstra Laufzeit O(m + n log n ) log log n Speicherplatz Θ(n + m) = Θ(n) Floyd Unser (best-case) Θ (|P|) Θ(ρ) Θ(n2) Θ(|V[H]|2) Tab. 6: Gegenüberstellung asymptotischer Eigenschaften von kürzeste-Weg-Algorithmen. Für diese Laufzeiten nehmen wir m = O(n) an, wie es auch für Straßennetze aus der Realität zu erwarten ist. Außerdem betrachten wir bei unserem hierarchischen Verfahren nur den für reale Straßennetze erhofften und vermuteten Fall, der am Ende des vorherigen Kapitels angesprochen wurde. Unser hierarchisches Verfahren ist also sehr viel schneller als Dijkstra und in etwa gleich schnell wie Floyd. An Speicherplatz verbraucht unser Verfahren asymptotisch doch noch viel mehr als Dijkstra, weil die Komplexität des High-Level-Subnetzes sicherlich linear abhängig von n ist. Aber der Speicherplatzverbrauch dürfte normalerweise auch noch deutlich unter dem von Floyd liegen, trotz der Tatsache, dass vermutlich viele Knoten und Kanten in mehr als einem Subnetz sind. Gebrauchen kann man also dieses Verfahren für Anwendungen, für die sehr effizient optimale kürzeste Wege berechnet werden müssen. Wenn die Wege nicht unbedingt wirklich kürzeste Wege sein müssen, dann sind wahrscheinlich andere heuristische Verfahren, die nur in den meisten Fällen wirklich kürzeste Wege zurückgeben, wegen ihrem wahrscheinlich geringerem Speicherplatzverbrauch vorzuziehen. 1 Für den worst-case wird es vermutlich kaum praktische Anwendungen geben, die mit anderen Verfahren konkurieren können. Es bleibt natürlich die Frage, ob der erwartete Fall bei Straßennetzen aus der realen Welt wirklich dieser Best-Case ist. 87 Ein großer, bisher noch nicht angesprochener Vorteil dieses Verfahrens gegenüber von dem von Floyd und Dijkstra ist, dass es offensichtlich sehr leicht parallelisierbar ist. Denn man könnte jedes der p Low-Level-Subnetze einem Rechner zuordnen, ein weiterer Rechner für das High-Level-Subnetz und dann vielleicht noch ein Anfrage-Server, an dem man die kürzeste-Weg-Anfragen stellt und der diese dann an die entsprechenden zwei beteiligten Low-Level-Server und dem einen High-Level-Server weiterleitet und am Ende den konkatenierten kürzesten Weg zurückgibt. So muss auch jeder Rechner nur genug (Haupt-)Speicher für das jeweilige Subnetz haben, das ihm zugeordnet ist. Die Rechner für die Low-Level-Subnetze brauchen dann nur O(1) Speicherplatz, der Rechner für das High-Level-Subnetz Θ(|V[H]|2) und der Anfrage-Server O(n) für die Speicherung des Arrays für die Funktion f. 88 6 Implementation In diesem Kapitel möchte ich kurz auf die in der Bearbeitungszeit der Diplomarbeit entstandene Java-Implementation der Algorithmen SUBNETZ_EINTEILUNGS_ GENERIERUNG und HIERARCHISCHE_WEGBERECHNUNG sowie einer graphischen Oberfläche – dem sog. Straßennetz-Editor – zum Erstellen, Editieren und Aufrufen der beiden Algorithmen eingehen. 6.1 Bedienung und Funktionalität des Straßennetz-Editors In diesem Abschnitt möchte ich kurz auf die Bedienung eingehen. Diese ist m.E. ziemlich intuitiv und sollte nach 10 Minuten „rumspielen“ durchschaut sein. Folgender Screenshot zeigt die Oberfläche des Straßennetz-Editors (siehe Abb. 26): Abb. 26: Screenshot des Straßennetz-Editors. Das Hauptfenster der Applikation ist in drei Teile aufgeteilt. Rechts befindet sich die Ansicht des Straßennetzes. Links oben die Straßenliste des aktuellen Straßennetzes und links unten die Partitionenliste (Liste der Verwaltungsbezirke). Mit der linken Maustaste 89 kann man im Straßennetz und in den beiden Listen einzelne Objekte selektieren. Mit dem Mausrad kann man stufenlos in dem Straßennetz zoomen. Zum Straßennetzes und dem Editor ist folgendes zu bemerken: • Die Knoten sind alle durchnumeriert (die Nummerierung kann aber geändert werden). • Aus Zeitgründen gibt es leider nur eine Metrik und das sind Pixel bei Zoom von 100% • Eine Kante hat (neben Start- und Endknoten etc.) drei Attribute: Die geometrische Länge (bzgl. der einen Metrik), Höchstgeschwindigkeit und Fahrzeit. Relevant für kürzeste Wegberechnung ist nur die Fahrzeit. Beim Editieren der Kanten kann man sie aber aus den anderen beiden Daten berechnen lassen. Wenn man eine Kante erstellt, dann wird sie automatisch auf ihre geometrische Länge bzgl. der Metrik gesetzt. Die Länge kann man natürlich nachträglich ändern. • Es gibt drei Darstellungen für Kanten: Einen einfachen Pfeil, eine einfache Linie oder einen Doppelpfeil. Erstere stellt eine gerichtete Kante dar. Die einfache Linie stellt wie der Doppelpfeil zwei Zwillingskanten (hin und zurück vom Startknoten) dar. Im Gegensatz zum Doppelpfeil sind bei der einfachen Linie die 3 Attribute bei den beiden Zwillingskanten gleich. • Es gibt drei verschiedene Ansichtsmodi: Die normale Ansicht, die SubnetzAnsicht und die Einzelschrittmodus-Ansicht. Die zweite stellt bei einer schon berechneten Einteilung des Straßennetzes in Subnetze das aktuell selektierte (in der Partitionenliste) in einer anderen Farbe dar. Die letzte ist die Ansicht des Einzelschrittmodus des Algorithmus SUBNETZ_EINTEILUNGS_ GENERIERUNG. • Man kann neue Knoten zeichnen, indem man über das „Knoten, Kanten“-Menü den entsprechenden Editiermodus auswählt und dann einfach auf die Karte / Straßennetz klickt. Zum zeichnen von Kanten markiert man den Start- und Zielknoten. • Mit Partitionen sind die Verwaltungsbezirke, wie z.B. Landkreise, gemeint Nun gehen wir auf die Menüs ein: File: Straßennetz speichern, laden, neues erschaffen. Hierbei ist zu bemerken, dass eine eventuell berechnete Einteilung des Straßennetzes in Subnetze nicht gespeichert oder geladen werden kann. View: Hier kann man den Zoom einstellen (geht auch mit Mausrad), die Art der Kantenbeschriftung auswählen und zwischen den Ansichtsmodi umschalten. Edit: Dieses Menü dient zum Löschen und Editieren selektierter Objekte. 90 Knoten, Kanten: Dieses Menü ermöglicht einem, in Editiermodi zum Einfügen von Kanten und Knoten zu wechseln. Strassen: In diesem Menü kann man neue Straßen erstellen, Kanten zu Straßen zuordnen und die Straßen automatisch für die Darstellung im Editor färben. Diese Färbung färbt Autobahn-Kanten standardmäßig in RotTönen und Landstraßen-Kanten in Blau-Tönen. Der Färbungsalgorithmus sorgt dafür, dass zueinander inzidente Kanten verschieden gefärbt sind, soweit das möglich ist. Partitionen: Wie das Straßenmenü, nur die Färbung färbt die Partitionen einfach der Reihe nach. Berechne: In diesem Menü kann man die Algorithmen SUBNETZ_EINTEILUNGS_ GENERIERUNG und HIERARCHISCHE_WEGBERECHNUNG und den Algorithmus von Dijkstra (zum Testen) auf das aktuelle Straßennetz anwenden. Für die Generierung der Subnetze gibt es auch einen Einzelschrittmodus. Hierbei ist zu bemerken, dass der Editor auf Konsistenz zwischen dem aktuellen Straßennetz und den Subnetzen achtet. Wenn man beispielsweise eine Einteilung in Subnetze generiert hat und danach etwas am Straßennetz ändert, muss man die Subnetze erst neu erstellen lassen, bevor man den Algorithmus HIERARCHISCHE_WEGBERECHNUNG aufrufen kann (der entsprechende Menü-Button ist dann nicht anklickbar) 6.2 Algorithmen In diesem Abschnittt möchte ich kurz etwas zu der Implementierung der Algorithmen SUBNETZ_EINTEILUNGS_GENERIERUNG und HIERARCHISCHE_ WEGBERECHNUNG sagen. Der Code ist mit dem JAVA JDK 1.4.0 b92 programmiert worden unter der Entwicklungsumgebung von JBuilder Enterprise. Es werden Klassen aus dem JDK 1.4 verwendet (die MouseWheel Klassen), so dass das Projekt nicht unter niedriger Version kompiliert bzw. interpretiert werden kann. Zumindest der Code der Algorithmen SUBNETZ_EINTEILUNGS_GENERIERUNG und HIERARCHISCHE_ WEGBERECHNUNG und der grundlegenden Straßennetz-Klassen ist ausführlich dokumentiert. In Abschnitt 5.3 haben wir als Datenstruktur für die Speicherung der Subnetze die Variante der Floyd-Wege-Matrix benutzt. Diese habe ich aber am Ende aus Zeitmangel nicht implementieren können, obwohl die Infrastruktur durch Interfaces im Prinzip schon 91 da ist. (die Algorithmen sprechen die Subnetze über Interfaces an, so dass fast beliebige Datenstrukturen sich dahinter verbergen können). Stattdessen habe ich das Straßennetz und die Subnetze relativ normal gespeichert mit teilweise intensiven Verpointerungen, welche aber asymptotisch nicht ins Gewicht fallen. Kürzeste Wege ermitteln das Straßennetz und die Subnetze mit dem Algorithmus von Dijkstra. 92 7 Zusammenfassung Ziel dieser Arbeit ist die Entwicklung eines hierarchischen Verfahrens zur Bestimmung kürzester Wege in einem gegebenen Straßennetz aus der realen Welt, z.B. dem Straßennetz Deutschlands. Dieses soll vor allen Dingen sehr schnell kürzeste-WegAnfragen exakt beantworten können und der Speicherplatzverbrauch sollte unter dem von Algorithmus Floyd bleiben. Dazu haben wir in Kapitel 3 zuerst das theoretische Gebilde Straßennetz definiert als einen Graphen mit ein paar zusätzlichen Funktionen, welche die Kanten zu Straßen und Straßen zu Straßentypen, wie z.B. Autobahnen, Landstraßen, etc. zuordnen. Die Modellierung eines Straßennetzes aus der realen Welt mit Hilfe dieser Definition ist größtenteils straight-forward, beinhaltet aber doch ein paar Punkte, die es zu bedenken gibt und die in Kapitel 2 angesprochen werden. Da ein solches Straßennetz in erster Linie ein Graph ist, lassen sich die gleichen Algorithmen und (fast die gleichen) Datenstrukturen zur Speicherung dieser verwenden. Nichtsdestotrotz besteht bei bestimmten Anwendungen Bedarf an sehr effizienten Algorithmen zur kürzeste-Weg-Berechnung in Straßennetzen, welche die klassische Graphentheorie nicht abdeckt. In Kapitel 4 haben wir uns eingehend mit dem Algorithmus von Floyd beschäftigt. Dieser gibt bekannterweise bei Eingabe eines Graphen zwei Matrizen zurück – die FloydDistanz-Matrix und die Floyd-Wege-Matrix –, welche alle kürzesten Distanzen respektive alle kürzesten Wege des Graphen speichern. Diese beiden Matrizen kann man – nachdem man sie ersteinmal berechnet hat – zur Beantwortung kürzester-Weg-Anfragen benutzen. So erhält man bei Θ(n2) Speicherplatzverbrauch (n sei hierzu die Anzahl der Knoten des Straßennetzes) in O(1) Zeit eine kürzeste Distanz und in Θ(|P|) Zeit einen kürzesten Weg P. In der Floyd-Distanz-Matrix gibt es Freiheitsgrade der Repräsentation der in dieser Matrix gespeicherten kürzesten Wege. Prof. Plümer hatte die Idee, diese zu benutzen, um in der Matrix die Struktur von Wegen in Straßennetzen zu inkorporieren. Dadurch lassen sich dann bestimmte Anfragen bzgl. kürzester Wege bei gleichem Speicherplatzverbrauch (zumindest asymptotisch, eventuelle Verpointerungen können den tatsächlich anfallenden Speicherplatzverbrauch ein wenig erhöhen) effizienter als in Θ(|P|) Zeit beantworten, weil diese Anfragen nicht den ganzen Weg zurückgegeben haben wollen, sondern z.B. nur die Straßen des Weges. Wir haben einen Algorithmus entwickelt, der eine Floyd-Wege-Matrix innerhalb Θ(n2α) = O(n3) Zeit in die gewünschte Variante transformiert, wobei α die durchschnittliche, ungewichtete Länge eines kürzesten Pfades in dem betrachteten Straßennetz ist. Als Beispiel, welches auch die ursprüngliche Motivation zur Forschung im Bereich hierarchischer Datenstrukturen war, haben wir die Variante der Floyd-WegeMatrix dazu benutzt, um das sog. Autobahnunternetz eines geg. Straßennetzes um Wege zu ergänzen, die eigentlich schon darin enthalten sein sollten, aber nicht sind – kürzeste Verbindungen zwischen Knoten des Autobahnunternetzes, die aber nicht im Autobahnunternetz enthalten sind. Dies geht mit der Variante der Floyd-Wege-Matrix, 93 wenn sie schon berechnet ist, in O(n2) Zeit im Ggs. zu den normalerweise benötigten O(n3) (zumindest für die naheliegende Implementation). In Kapitel 5 wenden wir uns den hierarchischen Strukturen zur Speicherung von Straßennetzen zu. Hierzu definieren wir eine sog. Einteilung eines geg. Straßennetzes in Subnetze. Diese Einteilung enthält eine Menge von sog. Low-Level-Subnetzen und ein High-Level-Subnetz. Jedes dieser Subnetze ist im Prinzip ein eigenes Straßennetz – ein Teil des ursprünglichen –, welches bestimmte Eigenschaften erfüllt. Jeder Knoten des zugrundeliegenden Straßennetzes gehört zu einem bestimmten Low-Level-Subnetz, seinem Stamm-Subnetz. In Abschnitt 5.1.3 leiten wir einen Algorithmus her, der geg. zwei Knoten des zugrundeliegenden Straßennetzes, die zwei Stammsubnetze der Knoten und das High-Level-Subnetz der betrachteten Einteilung des Straßennetzes in Subnetze einen kürzesten Weg im zugrundeliegenden Straßennetz zwischen den Knoten bestimmt. Bei einer (gültigen) Einteilung des Straßennetzes in Subnetze gibt dieser Algorithmus immer eine exakte Lösung aus. Dieser Algorithmus ist relativ unabhängig von den benutzten Datenstrukturen zur Speicherung der Subnetze definiert. Die Laufzeit ist demnach von den bentzten Datenstrukturen abhängig und wie das Straßennetz in Subnetze eingeteilt wurde. Im Abschnitt 5.2 überlegen wir uns, wie wir aus einem Straßennetz die bisher nur theoretisch definierte Einteilung in Subnetze erhalten können und welche zusätzlichen Eigenschaften diese erfüllen sollten, um den Speicherplatzverbrauch zur Speicherung dieser und die Laufzeit für den Algorithmus aus Abschnitt 5.1.3 niedrig zu halten. Dies überlegen wir uns auch ohne Betrachtung der konkreten Datenstrukturen zur Speicherung der (theoretisch definierten) Subnetze. In Abschnitt 5.2.3 leiten wir eine Heuristik her, die aus einem gegebenen Straßennetz, einer Partitionierung dessen in Verwaltungsbezirke (z.B. Landkreise) und dem Autobahn(unter)netz des Straßennetzes eine Einteilung in Subnetze generiert. Die Heuristik besteht darin, dass die zuvor überlegten wünschenswerten Eigenschaften / Optimierungskriterien, nicht unbedingt erfüllt sind. Die Laufzeit beträgt Θ(n3) und der Speicherplatzbedarf entspricht dem von dem zugrundeliegenden Straßennetz und der kompletten generierten Einteilung in Subnetze. Es zeigt sich, dass die Heuristik zumindest in konstruierten Beispielen sehr große (quadratisch in Anzahl der Kanten und Knoten des eingegebenen Straßennetzes), ungünstige Einteilungen des Straßennetzes in Subnetze generieren kann. In der Praxis ist dies wahrscheinlich nicht zu erwarten. Da vermuten wir eher den Best-Case. In dieser Diplomarbeit gibt es aber diesbezüglich keine empirischen Untersuchungen. Im Abschnitt 5.3 betrachten wir erstmals eine konkrete Datenstruktur zur Speicherung der Subnetze – die Variante der Floyd-Wege-Matrix. Diese braucht zwar normalerweise in Anzahl der Knoten quadratischen Speicherplatz, aber es ist zu erhoffen, dass die Heuristik das zugrundeliegende Straßennetz gut in einzelne kleinere Teile – die Subnetze – zerlegt hat und demnach der Speicherplatzbedarf deutlich niedriger als normalerweise ist (sowohl insgesamt wegen dem Quadrat als auch der Tatsache, dass man für eine kürzeste-WegBerechnung nur drei Subnetze von möglicherweise sehr vielen braucht und die Berechnung verteilt bearbeiten kann). In Abschnitt 5.3.2 argumentieren wir (wobei ich den letztendlichen theoretischen oder empirischen Beweis schuldig bleibe), dass wir ein reales 94 Straßennetz (mit Hilfe des einen Algorithmus) dann so in Subnetze zerlegen können, dass diese Einteilung in Subnetze insgesamt Θ(|V[H]|2) Speicherplatz benötigt, wobei V[H] in etwa der Größe des Autobahnunternetzes entspricht, und mit ihr in Θ(ρ) = O(|P|) Zeit kürzeste-Wege-Anfragen beantworten können. Hierbei gilt Θ(ρ) = o(|P|) für spezielle Arten von Anfragen bzgl. kürzester Wege, für welche die Variante der Floyd-WegeMatrix gut geeignet ist. In Kapitel 6 wird kurz auf eine im Verlauf der Bearbeitungszeit entstandene Implementation der in dieser Diplomarbeit entwickelten Algorithmen eingegangen. Diese besteht aus einem Editor zum erstellen, editieren, speichern und laden von Straßennetzen und der Möglichkeit zum Aufruf der Implementationen zur hierarchischen Ermittlung kürzester Wege. Als Datenstruktur wurde hier aus Zeitmangel noch nicht die (Variante der) Floyd-Wege-Matrix benutzt. Nun möchte ich einen kleinen Ausblick über weiterführenden Forschungsbedarf in dieser Thematik geben. Zum einen sind unbedingt Untersuchungen der Performanz und des Speicherplatzverbrauchs des in dieser Diplomarbeit hergeleiteten Verfahrens für aus der Realität stammende Straßennetze erforderlich, da dieses Verfahren eben eine Heuristik bzgl. dieser beiden Dimensionen ist. Dazu ist höchstwahrscheinlich auch eine Implementation zu programmieren bzw. die während dieser Diplomarbeit entstandene entsprechend zu erweitern, die als Datenstrukturen – wie in dieser Diplomarbeit theoretisch auch behandelt, aber praktisch aufgrund von Zeitmangel nicht umgesetzt – die Variante der Floyd-Wege-Matrix benutzt. Wenn sich dieses Verfahren durch Tests für die Praxis als tauglich erweist, wäre eine eingehendere Untersuchung der worst-, average- und best-cases sowie Wahrscheinlichkeitsverteilungen bzgl. realer Straßennetze wünschenswert. Dazu müssten wahrscheinlich reale Straßennetze in theoretischer Hinsicht genau untersucht und klassifiziert werden, um solche theoretischen Resultate gewinnen zu können. Umso besser wäre natürlich ein Algorithmus, der die hierarchischen Datenstrukturen bzgl. bestimmter, sinnvoller Kriterien immer optimal generiert, falls dies möglich ist. Es ist aber nicht auszuschließen, dass manche dieser (Optimierungs-)Probleme NP-hart sind. Weiterhin besteht eventuell Forschungsbedarf, dieses Verfahren für mehr als nur zwei hierarchische Ebenen weiterzuentwickeln, falls durch Tests nachgewiesen wurde, dass es so schon zumindest einigermaßen tauglich für die Praxis ist. Zuletzt könnte die Suche nach weiteren Anwendungen für die Variante der Floyd-WegeMatrix gewinnbringend sein. Vielleicht gibt es ein (möglichst praktisch relevantes) Problem, welches durch die Anwendung der Matrix inklusive der O(n3) Zeit für die Berechnung der Matrix schneller lösbar ist als ohne. 95 Literatur [BLUM98] Blum, N. (1998): Theoretische Informatik : Eine anwendungsorientierte Einführung. R. Oldenbourg Verlag München Wien 1998. 1. Auflage {BRAN94] Brandstädt, A. (1994): Graphen und Algorithmen. B.G. Teubner Stuttgart 1994 [HASS00] Hasselberg, S. (2000): Some results on heuristical algorithms for shortest path problems in large road networks, Inaugural-Dissertation zur Erlangung des Doktorgrades der Mathematisch-Naturwissenschaftlichen Fakultät der Universität zu Köln, Köln 2000 [KRUM00] Krumke, S.O., Noltemeier, H. et al. Graphentheoretische Konzepte und Algorithmen. Vorlesung Universität Würzburg. Würzburg 2000. URL: http://www-info1.informatik.uniwuerzburg.de/vorlesungen/SS99/graphentheorie/vorlesung.pdf [LEDA99] Mehlhorn, K., Näher, S. (1999): LEDA :A platform for combinatorial and geometric computing. Cambridge University Press, Cambridge 1999 96 Erklärung Hiermit erkläre ich, dass ich die vorliegende Arbeit selbstständig verfasst und keine anderen als die angegebenen Hilfsmittel benutzt habe. Die Arbeit wurde bisher keiner anderen Prüfungsbehörde vorgelegt und auch nicht veröffentlicht. Bad Honnef, den 10.07.2002 ______________________________________ ( Lasse Asbach) 97