Der Dijkstra Algorithmus Laufzeitoptimierung für den Einsatz in Navigationssystemen Seminararbeit am Ignaz Kögler Gymnasium Landsberg am Lech Rahmenthema des Wissenschaftspropädeutischen Seminars: Vermessung und Navigation Leitfach: Mathematik von Tobias Jülg Kursleiter: Matthias Müller, OStR Laufzeitoptimierung des Dijkstra Algorithmus Inhaltsverzeichnis 1 Einleitung ....................................................................................... 3 2 Der Dijkstra Algorithmus ............................................................... 4 2.1 Einführung in die Graphentheorie ....................................................................... 4 2.2 Überlegungen zur Problemstellung ..................................................................... 5 2.2.1 Der Dijkstra-Algorithmus und dessen Funktionsweise ........................................ 7 3 Wieso sind Änderungen notwendig? ........................................... 9 3.1 Laufzeitbeweis am Quellcode ............................................................................. 9 3.2 Laufzeitversuch mittels eines Java Programms ................................................ 10 4 Laufzeitoptimierung des Dijkstra Algorithmus .......................... 12 4.1 Bidirektionale Suche ......................................................................................... 13 4.2 A* Algorithmus .................................................................................................. 14 4.2.1 Funktionsweise des A* Algorithmus ................................................................. 15 4.3 Contraction Hierarchy ....................................................................................... 16 5 Anwendung in Navigationssystemen ......................................... 18 6 Zusammenfassung ...................................................................... 20 7 Anhänge ....................................................................................... 21 7.1 Bedienung des selbsterstellten Programmes .................................................... 21 7.2 Laufzeitbeweis am Quellcode ........................................................................... 25 8 Abbildungsverzeichnis ................................................................ 26 9 Literaturverzeichnis ..................................................................... 27 10 Erklärung des Verfassers ............................................................ 29 Laufzeitoptimierung des Dijkstra Algorithmus 3 Einleitung 1 EINLEITUNG Routenplanung ist mittlerweile ein großer Bestandteil unserer heutigen Gesellschaft. Vor allem im industriellen Sektor sind elektronische Navigationssysteme kaum noch wegzudenken, da man mit ihnen in sehr kurzer Zeit den optimalsten, also in den meisten Fällen den effizientesten Pfad von A nach B berechnen kann. Gerade in unserer heutigen Zeit der Globalisierung ist der Warentransport sehr wichtig. Innerhalb Europas wird hauptsächlich mit dem Lastkraftwagen oder der Bahn transportiert. Weltweit fließen die Warenströme dagegen eher über den See- oder Luftweg. Hier spielt oft der monetäre Faktor eine große Rolle. Denn durch unnötig lange Strecken entstehen mehr Kosten für Treibstoff, Personal und die Abnutzung des Transportmittels. Deshalb ist in der Regel die schnellste Route auch die beste. Andererseits ist aber auch das Benutzen von großen, ausgebauten Wegen, wie zum Beispiel einer Autobahn, von Vorteil, da dort auch bei längeren Wegen, durch die höhere Geschwindigkeit, Zeit eingespart werden kann. Bei größeren Transporten mit dem Flugzeug kann eine falsche Route dramatische Kostenauswirkungen haben. Diese Faktoren muss eine Navigationssoftware berücksichtigen. Zusätzlich spielt die benötigte Laufzeit des Algorithmus auch eine wichtige Rolle. Bei laufenden Transportprozessen, wie zum Beispiel im LKW-Verkehr, ist es oft durch Staus erforderlich, schnell eine passende alternative Route zu finden. Somit liegt es nahe, Navigationsgeräte und dessen Funktionsweise näher zu analysieren. Dabei stößt man zunächst einmal auf grundlegende aber sehr interessante Fragen: Wie kann ein Routenplaner überhaupt eine Strecke berechnen? Und wie ist es möglich, dass die Algorithmen bei einem großen Straßennetz, mit mehreren Millionen Kreuzungen und entsprechend vielen Weg-Möglichkeiten, das Ergebnis in wenigen Sekunden finden? Für die Beantwortung dieser Fragen wird in der Seminararbeit zunächst ein passender Algorithmus gesucht, welcher dann an die Anforderungen der Straßennavigation angepasst wird. Die dabei verwendeten Methoden zur Laufzeitoptimierung werden in ähnlicher Weise auch bei professioneller Navigationssoftware verwendet, siehe (OpenStreetMap Wiki 2015, Abs. 4). Laufzeitoptimierung des Dijkstra Algorithmus 4 Der Dijkstra Algorithmus 2 DER DIJKSTRA ALGORITHMUS 2.1 EINFÜHRUNG IN DIE GRAPHENTHEORIE Für die Verwendung von Kartendaten in Navigationsprogrammen, ist es sinnvoll die Navigationsdaten in ein Format, beziehungsweise Modell zu bringen, mit dem ein Computer arbeiten kann. Wichtig dabei ist, dass alle Ortsbeziehungen enthalten sind. Ein mathematischer Graph erfüllt genau diese Anforderungen, deshalb wird dieses Modell in den meisten Fällen für die Beschreibung von Karten benutzt. Dem folgenden Inhalt liegen (Gallenbacher 2007, S. 3–7) und (Wikipedia 2015c, Abs. Betrachteter Gegenstand) zugrunde. Ein Graph ist eine Art Netzwerk, das aus Knoten besteht, welche die vernetzten Objekte darstellen und Kanten, welche die Verbindungen der Knoten bilden. Wenn eine Kante zwischen zwei Knoten besteht, sind diese verbunden und es ist ein direkter Weg vorhanden. Man muss dabei allerdings zwischen gerichteten und ungerichteten Graphen unterscheiden. Bei ungerichteten steht eine Kante immer für eine beidseitige Verbindung. Bei gerichteten Graphen enthält die Kante auch noch die Eigenschaft der Richtung, somit kann jede Kante nur in eine Richtung genutzt werden. Bei beidseitigen Verbindungen werden deshalb immer zwei Kanten benötigt. Das Kantengewicht ist eine Zahl, welche die Wegkosten beschreibt, die notwendig sind, um über die jeweilige Kante zu reisen. Jede Kante hat ein Gewicht. Bei gerichteten Graphen kann es vorkommen, dass es zwischen zwei Knoten abhängig von der Richtung zwei unterschiedliche Gewichte gibt. Falls nicht direkt darauf hingewiesen wird, werden in dieser Seminararbeit ungerichtete Graphen benutzt. Für die verwendeten Algorithmen macht das keinen Unterschied, jedoch ist ein ungericheter Graph leichter zu generieren. Damit ein Straßennetz in einem Graphen modellieren werden kann, werden Kreuzungen als Knoten und Straßen als Kanten des Graphen beschrieben, siehe Bild 2-1. Die Länge einer Straße stellt in der Regel das Kantengewicht dar. Die Kantenkosten können aber auch Fahrzeit, Verkehr oder Mautgebühren sein, vergleiche (Velden 2014b, Abs. 1). Würde man gerichtete Graphen verwenden, könnte man jeweils noch zwischen Einbahn- und normalen Straßen unterscheiden. Laufzeitoptimierung des Dijkstra Algorithmus 5 Der Dijkstra Algorithmus Bild 2-1: Vom Straßennetz zum mathematischen Graphen. Die ursprünglichen Abbildungen stammen aus (Gallenbacher 2007, S. 2 und 7, Abb. 1.1 und 1.4) Echte Verkehrsnetzte können mithilfe des Graphen-Modells beschrieben werden. Ein Graph kann wiederum durch die Verwendung einer Adjazenzmatrix, einer Matrix in der alle Knotenbeziehungen gespeichert sind, implementiert werden. Somit ist eine digitale Kartenspeicherung möglich. Im nächsten Schritt wird ein „shortes path“ Algorithmus gesucht, welcher an einem Graphen arbeitet. Dabei ist es erforderlich, dass die folgenden Bedingungen erfüllt werden: Der Algorithmus sollte in annehmbarer Zeit den kürzesten Pfad zwischen Start- und Zielknoten liefern, falls es überhaupt einen gibt. Falls nicht, sollte er terminieren und eine entsprechende Fehlermeldung ausgeben. 2.2 ÜBERLEGUNGEN ZUR PROBLEMSTELLUNG Eine Möglichkeit, den kürzesten Pfad vom Start- zum Zielknoten zu finden, wäre einfach alle Wege zu testen. Diese Methode wird „Brute Force“, zu deutsch „Rohe Laufzeitoptimierung des Dijkstra Algorithmus 6 Der Dijkstra Algorithmus Gewalt“, genannt, da sie keinerlei Intelligenz benutzt und auch bereits gefundene Informationen nicht mit einbezieht. Es werden stupide alle möglichen Wege hintereinander verglichen, worin auch das Problem dieser Methode besteht: Bei einem großen Graphen gibt es sehr viele Kanten, welche eine hohe Anzahl an möglichen Wegen verursachen. Bild 2-2: Anzahl der Kanten, beziehungsweise die aller Wege von A aus bei vollständigen Graphen mit 3, 4 und 5 Knoten. Bei der Anzahl der Wege sind auch Pfade enthalten, welche nicht zum Zielknoten führen. Diese Daten können mit der Wege-Formel aus (Gallenbacher 2007, S. 30) berechnet werden. Besteht ein Graph aus 3 Knoten und jeweils alle Knoten sind untereinander verbunden, dann gibt es genau 2 mögliche Wege von A nach B. Bei einem Graphen mit gleicher Konstellation, aber mit 4 Knoten, sind es bereits 5 Wege. Bei einem mit 5 Knoten sind es 9 Wege. Ein Brute Force Algorithmus betrachtet allerdings nicht nur alle Wege die bei dem Zielknoten enden, sondern alle die überhaupt möglich sind. Somit kommt eine sehr schnell wachsende Anzahl an zu vergleichenden Pfaden zustande, siehe Bild 2-2. Im schlimmsten Fall, dem „Worst Case“, ist der Zielgraph ein vollständiger Graph, was bedeutet, dass alle Knoten untereinander verbunden sind (Gallenbacher 2007, S. 29). Für diesen Fall gibt es eine Formel, mit der die ungefähre Anzahl der Rechenschritte eines „Brute Force“ Algorithmus berechnet werden kann (Gallenbacher 2007, S. 30). Hierbei steht 𝑛c für die Anzahl der Rechenschritte und Laufzeitoptimierung des Dijkstra Algorithmus 7 Der Dijkstra Algorithmus 𝑛 für die Anzahl der im Graph enthaltenden Knoten. ( 2.1 ) 𝑛c ≈ (𝑛 − 1)! ∗ (𝑛 − 1) ( 2.2 ) Bei 10 beziehungsweise 20 Knoten erhält man durch diese Formel bereits folgende Werte (Gallenbacher 2007, S. 30). 𝑛c (10) = 3265920 ( 2.3 ) 18 𝑛c (20) ≈ 2,3 ∗ 10 Aus diesem Beispiel ist erkenntlich, dass der Berechnungsaufwand mit der Anzahl der Knoten sehr schnell extrem hoch wird. Diese Methode kann für einen großen Graphen mit mehreren Millionen Knoten in annehmbarer Zeit kein Ergebnis liefern, weil bei jedem Ausführen alle möglichen Verbindungen untersucht werden müssen. Die Laufzeit, abhängig von der Anzahl der möglichen Wege, ist dann unannehmbar hoch. Wesentlich besser wäre es, wenn die Laufzeit von der Anzahl der Knoten abhängig wäre und nicht exponentiell wachsen würde. Für das klassische kürzeste Wege Problem existiert bereits ein solcher Algorithmus, der Dijkstra-Algorithmus. Dieser Algorithmus wird im Folgenden genauer beschrieben. 2.2.1 DER DIJKSTRA-ALGORITHMUS UND DESSEN FUNKTIONSWEISE In dem folgenden Grundlagen-Kapitel werden (Velden 2014b) und (Wikipedia 2015b, Abs. Informelle Darstellung) des Öfteren als Quellen verwendet. Für die Implementierung des Algorithmus an einem normalen Graphen, benötigt die Knotenklasse drei weitere Attribute: 1. Eine Distanz zum Startknoten, welche immer wieder aktualisiert wird, falls ein noch kürzerer Weg existiert. 2. Eine Art Marker, welcher Knoten zu denen bereits der kürzeste Weg vom Startknoten gefunden wurde, markiert. Laufzeitoptimierung des Dijkstra Algorithmus 8 Der Dijkstra Algorithmus 3. Und ein Speicherfeld, in das, falls ein neuer Knoten entdeckt wird, oder es einen kürzeren Weg zu diesem gibt, sein Vorgänger hineingeschrieben wird. Dadurch kann am Ende die Kette der Knoten, die den kürzesten Weg bilden, vom Zielknoten rekursiv zurückverfolgt werden. Die einzigen Informationen die der Algorithmus benötigt, um an einem gegeben Graphen zu arbeiten sind der Startknoten 𝑠 und der Zielknoten 𝑡. ( 2.4 ) ( 2.5 ) Diese werden am Anfang dem Algorithmus übergeben. Zur Initialisierung vor dem Start gehören außerdem folgende Arbeitsschritte: Die Markierungen von allen Knoten entfernen. Die Distanzen aller Knoten auf Unendlich setzten, da man diese noch nicht kennt. Die Vorgänger aller Knoten entfernen. Der Startknoten wird von Anfang an markiert und seine eigene Distanz wird auf 0 gesetzt, da dies die Distanz zu ihm selbst ist. Der folgende Teil ist Kern des Algorithmus und wird solange wiederholt, bis alle Knoten markiert sind. Zunächst werden alle unmarkierten Nachbarn des aktuellen Knotens 𝑥 ( 2.6 ) untersucht und deren Distanzen aktualisiert. Diese ergeben sich aus der Summe der Länge des kürzesten Weges vom aktuellen Knoten zum Startknoten 𝑔(𝑥) und des Kantengewichtes zum neuen Knoten 𝑑(𝑥, 𝑥neu ): 𝑔(𝑥neu ) = 𝑑(𝑥, 𝑥neu ) + 𝑔(𝑥). ( 2.7 ) Dabei steht xneu für einen Nachbarn von x, g(xneu ) für die Distanz des neuen Knotens zu s, d(x, xneu ) für eine Funktion, welche die Wegkosten zwischen 2 Knoten ausgibt und g(x) für die Distanz des Knotens x zu s. Diese Aktion wird aber nur dann ausgeführt, wenn der alte Abstand größer als der neue ist, was bei einem unbesuchten Knoten immer der Fall ist, da seine Distanz auf Unendlich steht. Anschließend wird der nächste zu markierende Knoten aus der Laufzeitoptimierung des Dijkstra Algorithmus 9 Wieso sind Änderungen notwendig? Warteschlange ausgewählt. Die Warteschlange beinhaltet alle nicht markierten Knoten, also alle zu denen es noch einen kürzeren Weg geben kann und alle die noch nicht entdeckt wurden. Der Knoten mit der geringsten Distanz wird ausgewählt und markiert, er ist jetzt der aktive Knoten von dem aus weiter gesucht wird. Falls der Zielknoten markiert wurde, aber es immer noch unmarkierte Knoten gibt, wird der obige Block solange wiederholt bis dies nicht mehr der Fall ist. 3 WIESO SIND ÄNDERUNGEN NOTWENDIG? Durch die Themenstellung ergibt sich die folgende Frage: Sind Änderungen am Dijkstra Algorithmus überhaupt notwendig oder kann dieser unverändert in den zu untersuchenden Applikationen, den Navigationssystemen, eingesetzt werden? Die Beantwortung dieser Frage erfolgt mit zwei separaten Methoden. Die erste Untersuchung besteht aus einem Beweis, bei dem der Laufzeit-Zuwachs, bei steigender Knotenanzahl 𝑛, über den Quellcode ermittelt wird. Dabei kommt eine gängige Methode der Quellcodebetrachtung entsprechend (Brichzin et al. 2010, S. 148–149) zur Anwendung. Die zweite Methode ist ein Laufzeitversuch, bei dem das Ergebnis der Ersten experimentell überprüft wird. Bei der Durchführung dieses Versuches wird 𝑛 linear gesteigert und dabei die Laufzeit des Algorithmus gemessen. 3.1 LAUFZEITBEWEIS AM QUELLCODE Im Folgenden wird ein Laufzeitbeweis am Quellcode durchgeführt, welcher die Laufzeit-Knoten-Abhängigkeit veranschaulichen soll, vergleiche dazu Bild 7-5 im Anhang. Bei der Laufzeitbetrachtung erhält man für den Zeitzuwachs, abhängig von der Knotenanzahl 𝑛, die folgende Funktion. Hierbei ist 𝑡𝑖 die Laufzeit des jeweiligen Codeabschnittes und 𝑡𝑔𝑒𝑠 die Gesamtlaufzeit des Programmes: 𝑡𝑔𝑒𝑠 (n) = 𝑛² ∗ (𝑡5 + 𝑡7 ) + 𝑛 ∗ (𝑡2 + 𝑡4 + 𝑡6 + 𝑡9 ) + 𝑡1 + 𝑡3 + 𝑡8 . ( 3.1 ) Laufzeitoptimierung des Dijkstra Algorithmus 10 Wieso sind Änderungen notwendig? Diese „Worstcase“-Schleifenbetrachtungsfunktion kann dabei entsprechend der folgenden Vorgehensweise ermittelt werden: Man muss jeweils den Faktor, den ein Codefragment maximal wiederholt wird, mit dessen Laufzeit multiplizieren. Da es zwei Codeabschnitte, mit den Laufzeiten 𝑡5 und 𝑡7 gibt, welche im vorliegenden Beispielcode von zwei ineinander liegenden Schleifen durchlaufen werden, liegt hier eine quadratische Laufzeitzunahme vor. Diese Zunahme ist abhängig von 𝑛, weil 𝑛 die maximale Anzahl der Durchläufe beider Schleifen ist und somit der Code maximal 𝑛²-mal ausgeführt wird. Bei der Grenzwertbetrachtung von Funktionen ist allgemein bekannt, dass der Term mit der höchsten Potenz den größten Einfluss auf das Ergebnis hat. Somit haben 𝑡5 und 𝑡7 das größte Gewicht bei der Laufzeitzunahme. 3.2 LAUFZEITVERSUCH MITTELS EINES JAVA PROGRAMMS Zur experimentellen Überprüfung des Beweises von 3.1 wird der Dijkstra Algorithmus hinsichtlich seines Laufzeitwachstums untersucht. Dabei kommt die galileische Methode zum Einsatz. Eine Durchführungsbeschreibung für Experimente, bei der eine Hypothese aufgestellt werden muss, welche dann mittels des Versuchs überprüft und angepasst wird, siehe (Hermann-Rottmair et al. 2009, S. 55). Die Hypothese dieses Experiments ist, dass die Laufzeit des Algorithmus von der Knotenanzahl des Graphen abhängt. ( 3.2 ) Für den Versuch wurde ein Programm erstellt, welches die Knotenanzahl 𝑛 möglichst linear von 100 Knoten bis auf 10000 bei jedem Iterationsschritt erhöht. Die Funktion für die Knotenanzahl des aktuellen Programmdurchlaufs ist: 𝑛 = 100 ∗ 𝑖 + 100; 𝑖 ∈ {0, 1, 2, … , 99}. ( 3.3 ) Der Messwert jedes Schrittes wird dann mit der jeweiligen Knotenanzahl in einem 𝑛 − Δt-Diagramm eingetragen, siehe Bild 3-1. Laufzeitoptimierung des Dijkstra Algorithmus 11 Wieso sind Änderungen notwendig? Laufzeit des Dijkstra 600 y = (0,0516x2 + 0,1334x)/10000 R² = 0,9948 Laufzeit in ms 500 400 300 Laufzeit des Dijkstra 200 Poly. ( Laufzeit des Dijkstra) 100 100 676 1296 1849 2500 3025 3600 4225 4900 5476 6084 6561 7225 7744 8464 9025 9604 0 Anzahl der Knoten Bild 3-1: Ergebnis des Laufzeitexperiments Da sich bei der Änderung nur eines Parameters, nämlich der Knotenanzahl 𝑛, die Laufzeit verändert, ist die Hypothese ( 3.2 ) bestätigt. Die Trendlinie, welche den Verlauf der Messwerte mit einem Bestimmtheitsmaß von 99,48 % beschreibt, zeigt den „optimalen“ Verlauf. Aufgrund der großen Anzahl an Messwerten, kann man mit hoher Wahrscheinlichkeit annehmen, dass sich die Wachstumszunahme auch bei noch höheren Knotenanzahlen nicht mehr ändert. Die prognostizierte Funktion für die Laufzeit Δ𝑡 in Sekunden, lautet dabei wie folgt: Δ𝑡 = 10−7 ∗ (0,0516 𝑛2 + 0,1334 𝑛). ( 3.4 ) Anhand dieser Gleichung sieht man, dass die Laufzeit quadratisch wächst. Um einen Eindruck zu bekommen, ob der Dijkstra bei größeren Straßennetzen unverändert eingesetzt werden kann, wird die Knotenanzahl eines üblichen Graphen probehalber in die Funktion eingesetzt. Die exakte Anzahl an Kreuzungen lässt sich nur mit einer gewissen Unschärfe bestimmen, da sie sehr stark von der jeweiligen Definition abhängt. Laut (Ruopp 2012, S. 37)1 gibt es im deutschen Straßennetz circa 1 Für diese Quelle wurde Herr Anton Donner angeschrieben, ein Navigationsexperte welcher im deutschen Zentrum für Luft und Raumfahrt am Institut für Kommunikation und Navigation arbeitet. Er zeigte mir einige nützliche Informationen, welche mir im Laufe der Seminararbeit hilfreich waren. Dafür möchte ich ihm herzlich danken. Laufzeitoptimierung des Dijkstra Algorithmus 12 Laufzeitoptimierung des Dijkstra Algorithmus 15 ∗ 106 Kreuzungen. Im Folgenden soll jede dieser Kreuzungen einem Knoten entsprechen. Für 𝑛 = 15 ∗ 106 erhält man den Wert Δ𝑡 = 1.2 ∗ 106 s, also circa 14 Tage! Wollte man mit Hilfe eines Routenplaners, welcher den normalen Dijkstra Algorithmus benutzt, durch ganz Deutschland fahren, müsste man etwa 2 Wochen auf das Ergebnis warten. Dieser Wert übersteigt jegliche tolerierbare Dauer einer Routenberechnung. Schon bei Kurzstrecken müsste mehrere Minuten auf das Ergebnis gewartet werden. Außerdem wären Routenänderungen, zum Beispiel auf Grund eines Staus, wenn eine schnelle alternative Route benötigt wird, rein zeitlich nicht möglich. Aus diesem Grund sind „shortest path“-Berechnungen, die in wenigen Sekunden ein Ergebnis liefern, wie es bei den heutigen Navigationssystemen der Fall ist, nicht ohne weitere Zeitoptimierungen des Dijkstra Algorithmus bei größeren Straßennetzen möglich. Zusammenfassend lässt sich sagen, dass der Dijkstra Algorithmus durchaus für das Routing-Problem in der Navigation zum Einsatz kommen kann. Vorteile sind eine relativ einfache Umsetzung, da lediglich das Straßennetz als Graph modelliert werden muss. Außerdem lässt sich der Algorithmus sehr gut implementieren. Allerdings benötigt er aufgrund des bereits bewiesenen quadratischen Laufzeitwachstums sehr viel Zeit bei großen Graphen. Deshalb ist er in seiner normalen Form für Navigationssysteme, welche ein Ergebnis in wenigen Sekunden benötigen, eher ungeeignet. Um den Dijkstra Algorithmus aber trotzdem einsetzten und damit alle Vorteile nutzen zu können, sind einige Optimierungen, speziell im Hinblick auf die Laufzeit, notwendig. Im folgenden Kapitel sollen diese Optimierungsmöglichkeiten benannt und genauer darauf eingegangen werden. 4 LAUFZEITOPTIMIERUNG DES DIJKSTRA ALGORITHMUS Um Optimierungsmöglichkeiten zu finden, muss man den Algorithmus hinsichtlich seiner Schwachstellen untersuchen. Er berechnet zum Beispiel oft Werte, welche im weiteren Verlauf nicht mehr benötigt werden. Laufzeitoptimierung des Dijkstra Algorithmus 13 Laufzeitoptimierung des Dijkstra Algorithmus Bild 4-1: Suchraum des Dijkstra Algorithmus, im Bild links, verglichen mit einer bidirektionalem Variante. Der Graph ist dabei das deutsche Straßennetz. Der Startknoten ist Rot und der Zielknoten grün markiert. Kanten welche während dieses Vorgangs untersucht wurden, sind fett gedruckt. Diese Abbildung stammt aus (Schütz 2004, S. 15 Fig. 5). Eine Stelle an der dies passiert fällt sofort auf, wenn man sich nach einer Berechnung einmal alle, während des Suchvorgangs durchlaufenen Kanten anschaut. Diese bilden in der Regel eine Art Kreis um den Startknoten, siehe Bild 4-1. Zur Minimierung dieses Suchaufwandes in die falsche Richtung, wäre es sinnvoll ein Kriterium einzuführen, welches die Suche bevorzugt in die Richtung des Zielknotens laufen lässt. Die einfachste und am wenigsten aufwendigste Lösung für dieses Problem ist die bidirektionale Suche, welche im folgenden Kapitel erklärt wird. 4.1 BIDIREKTIONALE SUCHE Bei dieser Methode wird der Dijkstra nicht nur von Startknoten 𝑠 aus gestartet, sondern auch von Zielknoten 𝑡. So erhält man zwei parallel laufende Suchprogramme, welche sich in der Mitte treffen, falls es einen Weg von 𝑠 nach 𝑡 Laufzeitoptimierung des Dijkstra Algorithmus 14 Laufzeitoptimierung des Dijkstra Algorithmus gibt. Da der Weg nun halbiert ist, dringt der Algorithmus auch weniger weit in falsche Richtungen vor, siehe Bild 4-1. Dadurch sinkt der Suchaufwand und die Laufzeit wird geringer. Bei der Umsetzung dieser Variante gibt es jedoch einiges zu beachten. Jeder Knoten benötigt zwei Markerattribute, für jeden Dijkstra ein eigenes. Außerdem braucht man eine zusätzliche Codesequenz, welche beim Zusammentreffen zweier Suchpfade, also immer dann wenn ein Knoten zweimal markiert wurde, die minimale Summe des kürzesten Weges von Start zu Ziel aktualisiert (Schütz 2004, S. 15). Dies ist notwendig, da der erste Knoten, welcher von beiden Richtungen gekennzeichnet wurde, nicht unbedingt Teil des kürzesten Weges ist (Schütz 2004, S. 14). Es muss jedoch beachtet werden, dass bei einem gerichteten Graphen alle Kantenrichtungen für den Algorithmus, welcher bei t startet, umgedreht werden müssen. Da wir hier aber immer von ungerichten Graphen ausgehen, ist das an dieser Stelle zu vernachlässigen. Die Implementation kann zwar durch die zwangsweise erforderliche genaue Parallelität der Algorithmen erschwert werden, trotzdem wird diese Methode sehr häufig benutzt, da der originale Graph unverändert eingesetzt werden kann. Somit kann diese Methode gut in Verbindung mit weiteren Optimierungsmöglichkeiten verwendet werden. So wird der Algorithmus beispielsweise in wissenschaftlichen Arbeiten, wie bei (Ruopp 2012, S. 7), als Grundlage für weitere Forschungen benutzt. Gerade wegen des hohen Multithread-Aufwandes macht die Verwendung eines bidirektionalen Dijkstra aber erst mit einem großen Graphen Sinn. Bei der eigenen Implementation ist erst ab circa 2000 Knoten ein Laufzeitunterschied erkennbar, zu sehen in Bild 5-1 im Kapitel 5. 4.2 A* ALGORITHMUS Diese Verbesserungsmethode setzt, ähnlich wie der bidirektionale Dijkstra, an dem großen Suchbereich des Dijkstra Algorithmus an. Sie versucht durch heuristische Schätzungen speziell in Richtung des Ziels zu suchen. Der A* Algorithmus, gesprochen „A Stern“, ist im Prinzip ein Dijkstra-Algorithmus mit ein paar kleinen Erweiterungen und Verbesserungen. Zunächst sucht er priorisiert in Laufzeitoptimierung des Dijkstra Algorithmus 15 Laufzeitoptimierung des Dijkstra Algorithmus die Richtung in der sich das Ziel befindet, um somit unnötigen Suchaufwand zu vermeiden. Diese Art von Optimierungen wird auch „goal-directed search“ (Wagner und Willhalm 2007, S. 5) oder informiertes Suchverhalten genannt (Velden 2014a, Abs. 1). Um diese Methode durchzuführen, benutzt der Algorithmus für die Erkennung der Distanz zwischen dem untersuchten und dem Zielknoten, eine heuristische Schätzungsfunktion. 4.2.1 FUNKTIONSWEISE DES A* ALGORITHMUS Diesem Kapitel werden die folgenden zwei Quellen zugrunde gelegt: (Wikipedia 2015a) und (Velden 2014a). Der Algorithmus benötigt, außer den Listen welche vom Graphen bei der Implementierung bereits benötigt werden, noch zwei zusätzliche: Eine „Open List“ und eine „Closed List“. In der Open List werden alle neugefunden Knoten, zu denen noch keine Information über dem Abstand bekannt ist, hineingeschrieben. Um anschließend den kürzesten Weg zurückverfolgen zu können wird, ähnlich wie beim Dijkstra, bei jedem neuen Knoten der jeweilige Vorgänger gesetzt. Die Knotenklasse benötigt ein neues Attribut, welches den geschätzten Abstand 𝑓(𝑥) zum Ziel speichert, wobei 𝑥 für den jeweiligen Knoten steht, siehe ( 2.6 ). 𝑓(𝑥) wird einmal beim Eintragen in die Open List berechnet und jedes Mal, wenn ein noch kürzerer Weg zu 𝑥 gefunden wurde. Der Abstand wird dabei wie folgt berechnet: 𝑓(𝑥) = 𝑔(𝑥) + ℎ(𝑥). ( 4.1 ) Dabei steht 𝑔(𝑥), entsprechend wie beim Dijkstra in ( 2.7 ), für die Summe aus der Distanz des aktuellen Knotens und dem Abstand zum neuen Knoten. Zum aktuellen Knoten wurde der kürzeste Weg bereits gefunden, er befindet sich in der Closed List. ℎ(𝑥) ist der heuristisch geschätzte Abstand von Knoten 𝑥 zum Zielknoten 𝑡. Dieser darf den echten Abstand allerdings nicht überschreiten, da es sonst passieren könnte, dass der Algorithmus den Zielknoten zu weit hinten einreiht und keine korrekte Lösung findet. Als Schätzfunktion wird dabei häufig, aufgrund der einfachen Umsetzung die Luftlinie als monotone Heuristik benutzt, beziehungsweise im 2 Laufzeitoptimierung des Dijkstra Algorithmus 16 Laufzeitoptimierung des Dijkstra Algorithmus Dimensionalen der euklidische Abstand. Außerdem ist die Open List gleichzeitig auch Prioritätsliste: Der nächste zu untersuchende Knoten ist immer der mit dem geringsten 𝑓(𝑥). Die Closed List beinhaltet alle Knoten zu denen bereits der kürzester Weg gefunden wurde. Sie dient vor allem zur Vermeidung von der mehrmaligen Untersuchung eines Knotens. Wird ein Knoten zu dieser Liste hinzugefügt, dann werden seine unbesuchten Nachfolgeknoten in die Open List geschrieben. Ein Knoten wird immer dann zur Closed List hinzugefügt, wenn dieser besucht wurde. Die zwei Listen müssen aber nicht zwingend implementiert werden. Es reichen auch zwei boolesche Knotenattribute die den Zustand des Knotens speichern. Am Anfang ist die Closed List komplett leer, lediglich in der Open List befindet sich der Startknoten. Der Algorithmus endet wenn der Zielknoten den geringsten Wert 𝑓(𝑥) hat, dann wurde der kürzeste Weg gefunden. Er kann, genau wie beim Dijkstra in 2.2.1, durch die in den Knoten gespeicherten Vorgänger zurückverfolgt werden. Andererseits kann es auch passieren, dass sich an einem Punkt keine Knoten mehr in der Open List befinden. Dann existiert kein Weg von Start- zu Zielknoten und der Algorithmus sollte terminieren. Abschließend kann man sagen, dass der A* Algorithmus durch die Einbeziehung von zusätzlichen heuristischen Informationen eine gute Alternative zum Dijkstra ist. Man muss aber berücksichtigen, dass beim Anwenden, auch gerade durch das Einbeziehen von nicht sicheren geschätzten Daten, Fehler entstehen können (Ruopp 2012, S. 12). Deshalb wird in manchen Fällen trotzdem der Dijkstra bevorzugt. 4.3 CONTRACTION HIERARCHY Die als Contraction Hierachy bekannte Optimierungsmethode untersucht den Graphen und bewertet die Wichtigkeit der Knoten, um ihn damit hierarchisch so aufzubauen, dass der Dijkstra Algorithmus weniger Knoten zu untersuchen hat. Dadurch wird der Zeitaufwand geringer. Die folgende Beschreibung des Contraction Hierachy Verfahren orientiert sich vor allem an (Ruopp 2012, S. 14-18 und 25). Laufzeitoptimierung des Dijkstra Algorithmus 17 Laufzeitoptimierung des Dijkstra Algorithmus Jeder Knoten wird mit einem neuen Attribut, der eigenen hierarchischen Gewichtung, beziehungsweise der eigenen Ordnungsnummer ausgestattet. Die Gewichtung orientiert sich gewöhnlicher Weise an der Größe der Straßen an der die Kreuzung liegt und wird häufig mittels einer Heuristik festgelegt, vergleiche (Ruopp 2012, S. 17). So erhalten Autobahnen eine relativ große Zahl und Dorfstraßen eine sehr geringe. Der Algorithmus versucht nun bei der Auswahl des nächsten Knoten, immer einen mit einer höheren Ordnung zu wählen. Dabei ist es günstig eine bidirektionale Version zu nutzen, da der Zielknoten meist auch niederpriorisiert ist. Somit muss man in der Regel bei der Auswahl des nächsten Knoten nie eine Hierarchiestufe tiefer gehen. Der Lösungspfad ist hierbei wieder in der Schnittmenge der beiden Algorithmen zu finden. Bild 4-2: Kontraktion des Knotens b in Teilbild (a). Da der Weg von a nach c über b 1+1=2 Einheiten lang war, muss eine Kante dieser Länge von a nach c eingefügt werden, zu sehen in (c). Quelle der Abbildung: (Ruopp 2012, S. 15 Abb. 2.3) Um den Prioritätsgraphen zu erhalten, benutzt man das Kontraktionsverfahren für Knoten. Dabei werden ein bestimmter Knoten und alle seine Kanten komplett aus dem Graph entfernt. Wenn allerdings ein Weg über diesen möglich war, wird eine neue Kante mit dem Gewicht der Summe der beiden, die den Weg bildeten eingefügt, siehe Bild 4-2. Somit werden die Kosten des kürzesten Weges nicht verändert, aber die Knotenanzahl verringert, wodurch der Suchaufwand für den nachfolgenden „shortest path“ Algorithmus reduziert wird. Die Reihenfolge der Knoten bei der Kontraktion richtet sich dabei an die gegebene Knotenordnung. Das bedeutet, dass Knoten mit kleinerer Ordnung früher als andere Laufzeitoptimierung des Dijkstra Algorithmus 18 Anwendung in Navigationssystemen mit größerer kontrahiert werden. Außerdem ist es nach experimentellen Beobachtungen für den späteren Dijkstra Algorithmus laufzeittechnisch von Vorteil, Knoten zu entfernen, bei denen danach die Gesamtkantenanzahl kleiner als davor ist, vergleiche (Ruopp 2012, S. 17). Der bidirektionale Dijkstra wird dann für den so entstandenen reduzierten Graphen ausgeführt. Nach dem ein Weg im reduzierten Graphen gefunden wurde muss dieser allerdings noch auf den ursprünglichen Graphen zurückgeführt werden. Dies wird nach (Ruopp 2012, S. 17) auch unfolding genannt. 5 ANWENDUNG IN NAVIGATIONSSYSTEMEN In der Regel unterliegt der verwendete Algorithmus bei Herstellern von Navigationssystemen der Geheimhaltung. Allerdings gibt es auch die OpensourceSoftware (OpenStreetMap Wiki 2015, Abs. 4). Hier wird angegeben, dass neben anderen Verfahren auch der A* Algorithmus mit einem Contraction Hierachy Graphen als Optimierung implementiert ist. Außerdem wird dort geschrieben, dass die Entwickler zuerst den normalen Dijkstra benutzten wollten, dieser aber bei größeren Graphen zu lange Berechnungszeiten aufwies. In vielen wissenschaftlichen Arbeiten über dieses Thema wird meist eine der vorgestellten Methoden benutzt. Zum Beispiel in (Ruopp 2012, S. 7), bei der eine erweiterte bidirektionale Version verwendet wurde. Deshalb liegt der Schluss nahe, dass die beschriebenen Methoden tatsächlich in Navigationssystemen zum Einsatz kommen. Für die Seminararbeit wurde zur Überprüfung ein weiteres Beispielprogramm erstellt, welches die Laufzeit des normalen Dijkstra Algorithmus, des A* und des bidirektionalen Dijkstra unter gleichen Bedingungen bei steigender Knotenanzahl misst. Das „Contraction Hierarchy“-Verfahren konnte aufgrund der hohen Komplexität und der Änderungen welche am Graphen vorgenommen werden müssen, nicht implementiert werden. Laufzeitoptimierung des Dijkstra Algorithmus 19 Anwendung in Navigationssystemen Laufzeitvergleich des Dijkstra, A* und bidirektionalem Dijkstra 1000 900 Laufzeit in ms 800 700 600 500 Dijkstra 400 A* 300 bidirektionaler Dijkstra 200 100 1936 2304 2601 2916 3249 3600 3844 4225 4489 4761 5184 5476 5776 6084 6400 6724 7056 7396 7744 0 Anzahl der Knoten Bild 5-1: Ergebnis des Laufzeitvergleichs der Algorithmen Dijkstra, A* und bidirektionaler Dijkstra. Die Laufzeitmessungen wurden dabei speziell erst ab einer hohen Knotenanzahl durchgeführt, weil sich zum Beispiel beim bidirektionalen Dijkstra infolge des ThreadAufwands erst dann ein bemerkbarer Unterschied zeigt. In der Graphik Bild 5-1 kann man erkennen, dass beide Optimierungsvarianten schneller als der Dijkstra sind und auch langsamer steigen. Durch Kombinieren der beiden Methoden könnte man also schon eine enorme Verbesserung in dem Laufzeit-Knoten-Zuwachs erreichen. Hieraus ist ersichtlich, dass aus einem langsamen Algorithmus durch Optimierung ein schnelles alltagstaugliches System entstehen kann. Das Programm welches für den Versuch verwendete wurde, wird in 7.1 hinsichtlich der Bedienung genauer beschrieben. Zum Bau eines kommerziellen Navigationssystems benötigt es aber weitaus mehr. Neben der Navigationssoftware muss auch die gesamte Hardware designt, entwickelt und produziert werden. Zusätzlich wird noch wesentlich mehr Software, zum Beispiel für ein Userinterface oder die Ansteuerung eines mobilen Datenempfängers für aktuelle Verkehrsdaten, benötigt. Daraus ist erkennbar, dass die Entwicklung von solchen Geräten aufwendiger ist als vielleicht vermutet. Erst Laufzeitoptimierung des Dijkstra Algorithmus 20 Zusammenfassung eine intensive Beschäftigung mit der Funktionsweise eines modernen technischen Systems gibt einen Einblick in die Komplexität und den damit zusammenhängenden Realisierungsaufwand. 6 ZUSAMMENFASSUNG Zusammenfassend lässt sich sagen, dass der Dijkstra Algorithmus durchaus für den Einsatz in Navigationssystemen geeignet ist. Allerdings ist er für die Anwendung auf größeren Straßennetzten nicht optimal, da seine Laufzeit quadratisch zur Anzahl der Kreuzungen steigt und daraus eine sehr hohe Rechenzeit resultiert. Zur Minimierung dieser wurden folgende Optimierungsmethoden verwendet: Der „Bidirektionale Dijkstra Algorithmus“, welcher die Suche von Start- und Zielknoten ansetzt. Durch den verringerten Suchraum wird eine Geschwindigkeitssteigerung erreicht. Der „A* Algorithmus“, welcher durch heuristische Schätzfunktionen eine informierte Suche speziell in Richtung des Zielknotens laufen lässt. Das „Contraction Hierachy“ Verfahren, welches durch das Kontrahieren von nieder priorisierten Knoten die Knotenanzahl und somit den Suchaufwand verringert. Mithilfe der genannten Methoden lässt sich der Laufzeitzuwachs minimieren. Dann erfüllt der Dijkstra die Anforderungen eines Bedieners und er kann für den Echtzeiteinsatz in Navigationssystemen verwendet werden. Laufzeitoptimierung des Dijkstra Algorithmus 21 Anhänge 7 ANHÄNGE 7.1 BEDIENUNG DES SELBSTERSTELLTEN PROGRAMMES Bild 7-1: Hauptfenster der Bedienoberfläche. Hier können alle für den Laufzeitversuch nötigen Parameter eingestellt werden. Das Bild 7-1 zeigt das Hauptfenster der Bedienoberfläche. Hier können die zu vergleichenden Algorithmen ausgewählt, sowie die Funktion der Knotenanzahl bestimmt werden. Dementsprechend wurde bei dem Laufzeitversuch aus 3.2 bei der verwendeten Knotenfunktion ( 3.3 ) der Startwert 100, der Endwert 10000 und die Schrittgröße 100 gewählt. Laufzeitoptimierung des Dijkstra Algorithmus 22 Anhänge Bild 7-2: Konsolen-Tab des Graphical-User-Interfaces (GUI). Hier werden die Zwischenergebnisse ausgegeben. Das Bild 7-2 zeigt den Konsolen-Tab des GUI. Während des Laufzeitversuches werden hier die entsprechenden Zwischenergebnisse ausgegeben. Darunter befinden sich die Laufzeit der Algorithmen, die Zielknotenkette, die Länge des berechneten Weges und die echte Anzahl der Knoten im Graphen. Die Anzahl der Knoten weicht leicht ab, da für die Implementation ein quadratischer Graph besser geeignet war. Für die Berechnung der echten Knotenanzahl 𝑛real wurde deshalb folgende Formel benutzt. 𝑛real = (round(√𝑛 ))2 ( 7.1 ) Hierbei steht round() für eine Funktion, welche die übergebene reelle Zahl zu einer Ganzzahl rundet. Laufzeitoptimierung des Dijkstra Algorithmus Anhänge Bild 7-3: Konsolen-Tab am Ende des Versuchs 23 Laufzeitoptimierung des Dijkstra Algorithmus 24 Anhänge Am Ende des Versuchs werden alle Teilergebnisse noch einmal in einer Tabelle ausgegeben, welche in Bild 7-3 zu sehen ist. Diese Werte können jetzt, für die weitere Verwendung der Ergebnisse, in gleicher Form, im Haupt-Tab in Bild 7-1, in eine Textdatei gespeichert werden. Alternativ kann man das Ergebnis aber auch gleich in einem vorläufigen Diagramm im Graphen-Tab betrachten, siehe Bild 7-4. Bild 7-4: Der Graphen-Tab des Programmes ist gut geeignet um einen vorläufigen Überblick der Messwerte zu erhalten. Laufzeitoptimierung des Dijkstra Algorithmus 25 Anhänge 7.2 LAUFZEITBEWEIS AM QUELLCODE Bild 7-5: Laufzeitbeweis am eigenen Java Code nach dem Beispiel von (Brichzin et al. 2010, S. 148–149). Laufzeitoptimierung des Dijkstra Algorithmus 26 Abbildungsverzeichnis 8 ABBILDUNGSVERZEICHNIS Bild 2-1: Vom Straßennetz zum mathematischen Graphen. Die ursprünglichen Abbildungen stammen aus (Gallenbacher 2007, S. 2 und 7, Abb. 1.1 und 1.4) ............................................... 5 Bild 2-2: Anzahl der Kanten, beziehungsweise die aller Wege von A aus bei vollständigen Graphen mit 3, 4 und 5 Knoten. Bei der Anzahl der Wege sind auch Pfade enthalten, welche nicht zum Zielknoten führen. Diese Daten können mit der WegeFormel aus (Gallenbacher 2007, S. 30) berechnet werden............ 6 Bild 3-1: Ergebnis des Laufzeitexperiments ............................................... 11 Bild 4-1: Suchraum des Dijkstra Algorithmus, im Bild links, verglichen mit einer bidirektionalem Variante. Der Graph ist dabei das deutsche Straßennetz. Der Startknoten ist Rot und der Zielknoten grün markiert. Kanten welche während dieses Vorgangs untersucht wurden, sind fett gedruckt. Diese Abbildung stammt aus (Schütz 2004, S. 15 Fig. 5). ..................... 13 Bild 4-2: Kontraktion des Knotens b in Teilbild (a). Da der Weg von a nach c über b 1+1=2 Einheiten lang war, muss eine Kante dieser Länge von a nach c eingefügt werden, zu sehen in (c). Quelle der Abbildung: (Ruopp 2012, S. 15 Abb. 2.3) ................... 17 Bild 5-1: Ergebnis des Laufzeitvergleichs der Algorithmen Dijkstra, A* und bidirektionaler Dijkstra. .......................................................... 19 Bild 7-1: Hauptfenster der Bedienoberfläche. Hier können alle für den Laufzeitversuch nötigen Parameter eingestellt werden. ............... 21 Bild 7-2: Konsolen-Tab des Graphical-User-Interfaces (GUI). Hier werden die Zwischenergebnisse ausgegeben. ............................ 22 Bild 7-3: Konsolen-Tab am Ende des Versuchs ......................................... 23 Bild 7-4: Der Graphen-Tab des Programmes ist gut geeignet um einen vorläufigen Überblick der Messwerte zu erhalten. .............. 24 Laufzeitoptimierung des Dijkstra Algorithmus 27 Abbildungsverzeichnis Bild 7-5: Laufzeitbeweis am eigenen Java Code nach dem Beispiel von (Brichzin et al. 2010, S. 148–149). ........................................ 25 9 LITERATURVERZEICHNIS Bauer, Reinhard; Delling, Daniel; Sanders, Peter; Schieferdecker, Dennis; Schultes, Dominik; Wagner, Dorothea (2008): Combining Hierarchical and Goal-Directed Speed-Up Techniques for Dijkstra’s Algorithm. In: Catherine C. McGeoch (Hg.): Experimental Algorithms, Bd. 5038. Berlin, Heidelberg: Springer Berlin Heidelberg (Lecture Notes in Computer Science), S. 303–318. Online verfügbar unter http://algo2.iti.kit.edu/schultes/hwy/combine.pdf, zuletzt geprüft am 01.11.2015. Brichzin, Peter; Freiberger, Ulrich; Reinold, Klaus; Wiedemann, Albert (2010): Informatik. Oberstufe 2. 1. Aufl. München: Oldenbourg. Gallenbacher, Jens (2007): Abenteuer Informatik. IT zum Anfassen von Routenplaner bis Online-Banking. 1. Aufl. München: Elsevier, Spektrum Akad. Verl. Hermann-Rottmair, Ferdinand; Hoche, Detlef; Meyer, Lothar; Reichwald, Rainer; Schwarz, Oliver (2009): Physik. Bayern Gymnasium Lehrbuch für die 11. Klasse. 1. Aufl. Berlin, Mannheim: Duden-Schulbuchverl. OpenStreetMap Wiki (Hg.) (2015): OpenTripPlanner – OpenStreetMap Wiki. Online verfügbar unter http://wiki.openstreetmap.org/wiki/OpenTripPlanner, zuletzt aktualisiert am 02.10.2015, zuletzt geprüft am 03.10.2015. Ruopp, Manuel (2012): Contraction Hierarchies für komplexe Kostenmaße. Diplomarbeit. Universität Stuttgart, Stuttgart. Institut für Formale Methoden der Informatik Abteilung Algorithmik. Online verfügbar unter ftp://ftp.informatik.unistuttgart.de/pub/library/medoc.ustuttgart_fi/DIP-3232/DIP-3232.pdf, zuletzt geprüft am 01.11.2015. Schmidt, Thorsten; Fuchs, David (2004): A-Stern Algorithmus. Hg. v. Geosimulation. Online verfügbar unter http://www.geosimulation.de/methoden/a_stern_algorithmus.htm, zuletzt aktualisiert am 12.11.2004, zuletzt geprüft am 17.08.2015. Laufzeitoptimierung des Dijkstra Algorithmus 28 Abbildungsverzeichnis Schütz, Birk (2004): Partition-Based Speed-Up of Dijkstra's Algorithm. Studienarbeit. Universität Karlsruhe (TH), Karlsruhe. Institut für Logik, Komplexität und Deduktionssysteme. Online verfügbar unter http://i11www.iti.unikarlsruhe.de/_media/teaching/theses/files/studienarbeit-schuetz-05.pdf, zuletzt geprüft am 01.11.2015. Velden, Lisa (2014a): Der A*-Algorithmus. Hg. v. TU München. Online verfügbar unter https://www-m9.ma.tum.de/graph-algorithms/spp-a-star/index_de.html, zuletzt aktualisiert am 08.07.2015, zuletzt geprüft am 17.08.2015. Velden, Lisa (2014b): Der Dijkstra-Algorithmus. Hg. v. TU München. Online verfügbar unter https://www-m9.ma.tum.de/graph-algorithms/spp-dijkstra/index_de.html, zuletzt aktualisiert am 08.07.2015, zuletzt geprüft am 17.08.2015. Wagner, Dorothea; Willhalm, Thomas (2007): Speed-Up Techniques for ShortestPath Computations. Universität Karlsruhe (TH), Karlsruhe. Institut für Theoretische Informatik. Online verfügbar unter http://i11www.iti.unikarlsruhe.de/extra/publications/ww-sutsp-07.pdf, zuletzt geprüft am 01.11.2015. Wikipedia (Hg.) (2015a): A*-Algorithmus. Online verfügbar unter https://de.wikipedia.org/w/index.php?oldid=146613207, zuletzt aktualisiert am 02.10.2015, zuletzt geprüft am 06.10.2015. Wikipedia (Hg.) (2015b): Dijkstra-Algorithmus. Online verfügbar unter https://de.wikipedia.org/w/index.php?oldid=146740393, zuletzt aktualisiert am 23.10.2015, zuletzt geprüft am 26.10.2015. Wikipedia (Hg.) (2015c): Graphentheorie. Online verfügbar unter https://de.wikipedia.org/w/index.php?oldid=145232211, zuletzt aktualisiert am 23.10.2015, zuletzt geprüft am 26.10.2015. Wikiversity (Hg.) (2011): Projekt:Mathematik in Natur und Technik/dijkstra. Online verfügbar unter http://de.wikiversity.org/wiki/Projekt:Mathematik_in_Natur_und_Technik/dijkstra, zuletzt aktualisiert am 16.01.2015, zuletzt geprüft am 16.02.2015. Laufzeitoptimierung des Dijkstra Algorithmus Erklärung des Verfassers 10 ERKLÄRUNG DES VERFASSERS Ich habe diese Seminararbeit ohne fremde Hilfe angefertigt und nur die im Literaturverzeichnis angeführten Quellen und Hilfsmittel benutzt. Finning den 09.11.2015 ____________________ ____________________ Ort, Datum Unterschrift 29