Prof. Thomas Richter Institut für Analysis und Numerik Otto-von-Guericke-Universität Magdeburg [email protected] 1. Juni 2017 Material zur Vorlesung Algorithmische Mathematik II am 31.05.2017 Kürzeste Wege in gewichteten Graphen 1 Gewichtete Graphen + kürzeste Wege Oft macht es Sinn, den Knoten und Kanten in einem Graph ein Gewicht zuzuordnen. Wir betrachten als Graphen das Netz zwischen Städten (die Knoten) und Bahnlinien (die Kanten) welche die Städte verbinden. Ein mögliches Kantengewicht c(e) ∈ R für eine Kante e = (x, y) kann z.B. die Länge es verbindenden Bahnstrecke von x nach y sein, etwa in Kilometer oder aber in Zeit. Ein Knotengewicht c(x) ∈ R könnte z.B. die durchschnittliche Zeit sein, die ein Zug an diesem Bahnhof verbringt. Im Folgenden betrachten wir zunächst nur Kantengewichte. Folie 1 Definition 1 (Gewichteter Graph). Ein Tripel G = (V, E, fW ) bestehend aus Knoten V ⊂ N, Kanten E ⊂ V × V und einer Gewichtsfunktion fW : E → R heißt gewichteter Graph. Die Unterscheidung zwischen gerichteten und ungerichteten Graphen, vollständigen Graphen, Bäumen und Wäldern wird durch das zusätzliche Gewicht nicht geändert. 1 Folie 2 Zur Speicherung von gewichteten Graphen wird zusätzlich zur Adjazenzliste eine Gewichtsliste gespeichert. Algorithmus 1: Gewichteter Graph 1 2 3 4 5 6 7 8 9 10 c l a s s GraphGewichtet { public : / / A d j a z e n z l i s t e . a d j [ i ] s p e i c h e r t a l l e Knoten a d j [ i ] [ j ] , / / d i e m i t Knoten i d u r c h e i n e K a n t e v e r b u n d e n s i n d . vector <vector <int > > adj ; / / G e w i c h t s l i s t e . wgt [ i ] [ j ] s p e i c h e r t d a s G e w i c h t z u r / / K a n t e z w i s c h e n Knoten i und Knoten a d j [ i ] [ j ] v e c t o r < v e c t o r <double> > wgt ; }; Wir definieren nun das wesentliche Problem, welches wir in dieser Stunde untersuchen werden. Folie 3 8 Definition 2 (Kürzester Weg). Gegeben sei ein gewichteter Graph G sowie zwei Knoten x, y ∈ V(G). Gesucht ist der Weg P = (V 0 , E 0 , fW ) ⊂ G P := (V 0 , E 0 ) = ({x = x0 , . . . , xk = y}, {e1 , . . . , ek }) 6 7 1 5 6 2 5 8 5 4 9 mit kleinstem summierten Kantengewicht X fW (e) → min! 5 1 3 3 e∈P 2 0 2 7 Wir suchen z.B. den kürzesten Weg von Knoten 0 nach Knoten 8. 2 Kantengewichte fW (e) können im Allgemeinen negativ oder positiv sein. Wir beschränken uns hier auf den einfachen Fall von ausschließlich positiven Kantengewichten fW : E → R>0 . Angenommen, ein Graph (mit auch negativen Kantengewichten) hätte einen Kreis mit negativer Kantengewichtsumme. Durch mehrfaches durchlaufen dieses Kreises können wir Wege mit beliebig kleiner Gewichtssumme erzeugen. Dieses Beispiel ist extrem, es zeigt jedoch bereits, dass dieser allgemeine Fall schwerer ist. 2 2 Suche kürzester Wege - Dijkstra’s Algorithmus Bei der Suche nach kürzesten Wegen haben wir Landkarten vor Augen. Diese beinhalten zusätzlich zu den Knoten und Kanten - also Städten und Bahnlinien - auch geometrische Informationen, die uns beim Problem stark helfen: Wollen wir von Hamburg nach Kiel fahren, so werden wir nicht in einen Zug nach Berlin oder Hannover steigen. Wir suchen hier nach einfachen Algorithmen, die ohne solche Zusatzinformationen auskommen. Starten wir in einem Punkt x ∈ V, so können wir also nicht zielgerichtet in Richtung y ∈ V loslegen sonder werden den Graphen - ähnlich der Durchmusterung - allgemein durchlaufen. Hierbei werden wir für alle Knoten xk ∈ V jeweils kürzeste Wege von x berechnen. Treffen wir bei dieser Suche auf den Knoten y, so sind wir fertig. Ein Algorithmus zum Lösen des Problems (bei positiven Kantengewichten) heißt Dijkstra’s Algorithmus. Folie 4 Algorithmus 2: Dijkstra’s Algorithmus Gegeben ein gewichteter Graph G = (V, E, fW ) sowie ein Knoten x ∈ V 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Setze R = ∅ Setze Q = {x} Setze l(v) = 0 Solange Q 6= ∅ Wä hle v ∈ Q mit l(v) minimal Setze Q = Q \ {v} Setze R = R ∪ {v} Für e = (v, w) ∈ E mit w 6∈ R Setze ŵ = l(v) + fW (e) F a l l s w 6∈ Q oder ŵ < l(w) Setze l(w) = ŵ Setze Q = Q ∪ {w} Setze P(w) = e Setze T = {P(v) | v ∈ R \ {x}} Ausgabe (R, T ) Der Algorithmus erzeugt einen gerichteten Graphen (R, T ), der zu jedem Knoten v ∈ R den kürzesten Weg beinhaltet. Der Graph ist eine sogenannte Arboreszenz. Eine Arboreszenz ist ein zusammenhängendes Branching. Dabei ist ein Branching ein gerichteter Graph, dessen zugehöriger ungerichteter Graph ein Wald ist (Kreisfrei) und für den jeder Knoten nur eine eingehende Kante besitzt. Was ist wichtig für uns? Zu jedem Knoten v ∈ R \ {x} gibt es nur eine Kante e ∈ T mit e = (v 0 , x). Wollen wir nun den kürzesten Weg von x nach y bestimmen, so durchlaufen wir die entsprechenden Kanten von y aus einfach rückwärts. Anstelle von Zeile 13 speichern wir im Algorithmus daher keine Kanten sondern zu jedem Knoten w ∈ R den Knoten v ∈ R von dem aus wir w mit e = (v, w) erreicht haben. 3 13 Setze P[w] = v Suchen wir nur den Weg von x nach y so kann der Algorithmus abgebrochen werden, falls in Zeile 5 v = y gilt. Folie 5 Satz 1 (Dijkstr’as Algorithmus). Der Dijkstra Algorithmus löst das kürzeste-WegeProblem. Der Algorithmus kann in O(|V|2 + |E|) Operationen durchgeführt werden. Beweis. Der Beweis der Korrektheit, dass also kürzeste Wege von x ∈ V zu jedem erreichbaren Knoten y ∈ V gefunden werden findet sich z.B. in Hougardy, Vygen “Algorithmische Mathematik”. Wir gehen kurz auf den Aufwand ein. Wir speichern Q in einer Liste, also einem vector. R speichern wir in einem Vektor vom Typ vector<bool> R mit dem Wert true falls v ∈ R sonst false . Die Solange-Schleife wird höchstens |V| mal durchgeführt, da nur |V| Knoten der Menge Q hinzugefügt werden können. In jedem Durchgang werden die folgenden Schritte durchgeführt: • Suche des Elements v ∈ Q mit l(v) minimal. Bei Speicherung von Q als Liste kann dies in O(|Q|) = O(|V|) Operationen erfolgen. • Löschen von v ∈ Q erfordert bei einer Liste das Suchen (bereits erfolgt) ein Umkopieren des letzten Elements an die Stelle von v und ein Verkürzen, somit O(1) Operationen. • Hinzufügen von v an R erfolgt in O(1) Operationen. • Ebenso benötigt die Abfrage w 6∈ R in Schritt 8 nur O(1) Operationen • In Schritt 8 werden alle Kanten von v aus durchlaufen. Da dieser Schritt maximal für alle Knoten des Graphen durchlaufen wird wird die innere Schleife 8-13 somit maximal O(|E|) mal durchlaufen. Die Schritte 11-13 können jeweils in O(1) Operationen durchgeführt werden. Die Abfrage w 6∈ Q in Schritt 14 kann durch Auswertung des Gewichtes l(w) erfolgen, falls dieses vor Durchlauf des Verfahrens z.B. mit den Werten −1 initialisiert wird. Insgesamt ergibt sich somit der Aufwand O(|V|2 + |E|). 4 Folie 6 Zu einer effizienten Umsetzung des Algorithmus müssen mehrere Teilaufgaben schnell gelöst werden. Die Solange-Schleife ab Zeile 4 hat maximal n = |V| Iterationen. Die wesentlichen Bestandteile sind: • Suche des Elements v ∈ Q mit kleinstem Gewicht • Suche des Elementes w ∈ R (erledigt, wenn wir R als vector schreiben) • Hinzufügen in Q sowie entfernen aus Q • Hinzufügen in R (erledigt, bei R als vector) Kritisch in Bezug auf die Laufzeit ist die Suche in Q und R. Sind diese Mengen nicht günstig strukturiert, so kann die Suche jeweils O(n) Operationen benötigen, in denen die gesamte Liste durchlaufen wird. Die Gesamtllaufzeit wäre dann O(n2 ). Die Suche in der Menge R kann einfach gestaltet werden. Wird der Algorithmus komplett durchgeführt und ist der Graph G zusammenhängend, so hat R nach Beeindigung die gleiche Anzahl an Elementen. Statt einer Menge R kann zur Speicherung ein Vektor gewählt werden mit dem Wert 1, falls ein Knoten Teil von R ist, ansonsten Null. 5