Kürzeste-Wege-Algorithmen Institut für Informatik Universität zu Köln 19. August 2003 Inhaltsverzeichnis 0.1 Dijkstra-Algorithmus . . . . . . . . . . . . . . . . . . . . . . . . . 0.1.1 Dijkstra in Pidgin-Pascal . . . . . . . . . . . . . . . . . . . 2 3 0.2 Datenstrukturen und Laufzeiten . . . . . . . . . . . . . . . . . . . . 4 0.2.1 0.2.2 (a,b)-Bäume . . . . . . . . . . . . . . . . . . . . . . . . . . Fibonacci-Heaps . . . . . . . . . . . . . . . . . . . . . . . . 5 6 0.2.3 0.2.4 Buckets . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Redistributive Heaps . . . . . . . . . . . . . . . . . . . . . 15 0.3 0.4 Skalierungs-Verfahren . . . . . . . . . . . . . . . . . . . . . . . . . 18 D’Esopo-Pape-Algorithmus . . . . . . . . . . . . . . . . . . . . . . 18 0.5 0.4.1 D’Esopo-Pape-Verfahren . . . . . . . . . . . . . . . . . . . 19 2-Listen-Verfahren und Threshold-Verfahren . . . . . . . . . . . . . 19 0.5.1 0.5.2 0.6 0.7 2-Listen-Verfahren . . . . . . . . . . . . . . . . . . . . . . . 20 Threshold-Algorithmus . . . . . . . . . . . . . . . . . . . . 20 Future-Cost-Verfahren . . . . . . . . . . . . . . . . . . . . . . . . . 21 LP-Formulierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 1 2 INHALTSVERZEICHNIS Kürzeste-Wege-Algorithmen Gegeben sei ein gerichteter Graph G = (V, E) mit Knotenmenge V und Kantenmenge E und Entfernungen c(v, w) ≥ 0 für (v, w) ∈ E. Zusätzlich sei ein Knoten s ∈ V ausgezeichnet. 10 ea QQ 3 Q 9 Q Q Q s Q?e t * 2 s eH H HH Hj H ? 7 14 e b Definition 1. Ein Weg P von v ∈ V nach w ∈ V ist eine Folge v 0 , v1 , . . . , vk von Knoten, so daß v0 = v, vk = wPund (vi , vi+1 ) ∈ E für c(vi , vi+1 )0 ≤ i ≤ k − 1. k−1 Der Weg hat die Länge c(P ) := i=0 . Gesucht: Kürzeste Wege von s zu allen anderen Knoten. Idee des Verfahrens: Wir vergrößern schrittweise eine Menge M von Knoten, für deren Elemente v ∈ M wir bereits einen kürzesten Weg von s nach v gefunden haben. - allen anderen Knoten v ∈ / M ordnen wir die Länge des bisher gefundenen kürzesten Weges zu. 0.1 Dijkstra-Algorithmus Wir bezeichnen mitDist(v) die Länge des (bisher gefundenen) kürzesten Weges und mit V or(v) den Vorgänger auf einem solchen Weg. Setze Dist(s) := 0, M := {s} Für v ∈ V mit (s, v) ∈ E setze Dist(v) := c(s, v) und V or(v) := s Für v ∈ V mit (s, v) ∈ / E setze Dist(v) := +∞ und V or(v) := ∅ Bestimme u ∈ / M mit Dist(u) = min{Dist(v) : v ∈ / M }. Falls Dist(u) = ∞, dann ST OP. Andernfalls setze M = M ∪ {u}. Für alle v ∈ / M mit (u, v) ∈ E Falls Dist(v) > Dist(u) + c(u, v) dann setze: Dist(v) = Dist(u) + c(u, v) V or(v) = u Falls M 6= V, dann gehe zu 2, sonst STOP. INHALTSVERZEICHNIS 3 Satz 2. Der Dijkstra-Algorithmus berechnet kürzeste Wege von s zu allen anderen erreichbaren Knoten. Beweis: Wir zeigen per Induktion über k = 1, . . . , |M |: (i) v ∈ M ⇒ Dist(v) ist Distanz des kürzesten (s, v)-Weges (ii) v ∈ / M ⇒ Dist(v) ist Distanz des kürzesten (s, v)-Weges, der (außer v) nur Knoten aus M benutzt. Ist k = 1 (d.h. nur s ∈ M ), so sind die Aussagen korrekt. Seien die Aussagen also für k bewiesen und sei u der neu in M aufgenommene Knoten. Falls Dist(u) = ∞, so ist nichts mehr zu zeigen. Andernfalls: (i) Die Aussage gilt nach Induktionsannahme bereits für alle Knoten v ∈ M, v 6= u. Per Induktion ist Dist(u) die Länge eines kürzesten Weges von s nach u, der als innere Knoten nur Knoten aus M benutzen darf. Angenommen es existiert ein kürzerer Weg P von s nach u. P muß dann einen ersten Knoten w ∈ / M enthalten: s → · → · · · · → w → · → · · · → u. Sei P 0 der Anfang von P bis w. P 0 enthält im Inneren nur Knoten aus M. Per Induktion ist Dist(w) ≤ c(P 0 ). Nach Wahl von u gilt Dist(u) ≤ Dist(w). Somit Dist(u) ≤ Dist(w) ≤ c(P 0 ) ≤ c(P ) < Dist(u). Widerspruch. (ii) Nach Schritt 4 ist für v ∈ / M, Dist(v) die Länge des kürzesten Weges von s nach v, der nur innere Knoten aus M benutzt und der entweder u nicht oder als vorletzten Knoten benutzt. Angenommen es existiert ein kürzerer Weg P von s nach v über Knoten in M. Sei w der vorletzte Knoten in P. Dann ist w 6= u. Sei P 0 der in P enthaltene (s, w)Weg. Da w früher als u in M aufgenommen wurde, ist Dist(w) die Länge eines kürzesten (s, w)-Weges P 00 , der nur Knoten aus M \{u} benutzt. Somit Dist(v) > c(P ) ≥ c(P 0 ) + c(w, v) ≥ c(P 00 ) + c(w, v) ≥ Dist(v), da P 00 ∪ {(w, v)} ein (s, v)Weg über Knoten aus M \{u} ist. Widerspruch. 2 Korollar 3. (aus dem Beweis): Ist man nur an dem kürzesten Weg von s nach t interessiert, so genügt es, den Algorithmus zu stoppen, sobald t ∈ M. 2 0.1.1 Dijkstra in Pidgin-Pascal Knoten, die in die Menge M aufgenommen worden sind, heißen markiert. Für die Implementation des Dijkstra-Algorithmus ist es besser, an Stelle der Menge M die Menge U := {v ∈ V : Dist(v) < ∞, v ∈ / M } zu betrachten. Diese Menge, die aus den noch nicht markierten Knoten besteht, zu denen der Algorithmus schon einen provisorischen Weg gefunden hat, heißt Front. Neu formuliert heißt der Algorithmus jetzt so: Dist(s) := 0, U = ∅ for v ∈ V mit (s, v) ∈ E do Dist(v) = c(s, v), V or(v) = s U = U ∪ {v} endfor INHALTSVERZEICHNIS 4 while U 6= ∅ wähle u ∈ U mit Dist(u) minimal in U (finde Min) lösche u aus U (lösche Min) for all (u, v) ∈ E if Dist(u) + c(u, v) < Dist(v) then Dist(v) = Dist(u) + c(u, v), V or(v) = u. (verringere) if v ∈ / U then füge v zu U hinzu. (füge ein) endif endfor endwhile Abschätzung der Laufzeit Lemma 4. Die Laufzeit des Dijkstra-Algorithmus ist O (n · max{ O(finde Min), O(lösche Min), O(füge ein) }) + m· O(verringere). Beweis: Die while-Schleife kann höchstens n = |V | mal ausgeführt werden, da jeder Knoten höchstens einmal als Minimum auftreten kann. Die darin enthaltene for-Schleife kann aber auch nur einmal für jede Kante durchlaufen werden. Somit: n Ausführungen von finde Min, lösche Min, füge ein, m Ausführungen von Verringere. 2 0.2 Datenstrukturen und Laufzeiten Für die Implementierung des Dijkstra-Algorithmus benötigen wir Datenstrukturen, um den Graphen, die Distanz (Dist(v)) und den Vorgänger (V or(v)) eines Knotens sowie die Front (Menge U ) effizient zu verwalten. Der Graph kann z.B. in einer Adjazenzliste abgespeichert werden: Feld Tail [1, . . . , n]: Tail(i) zeigt auf Beginn einer verketteten Liste, die die Endknoten j und Kosten von Kanten (i, j) ∈ E enthält. Die Distanz und der Vorgänger eines Knotens können in Felder Dist[1 . . . n], V or[1 . . . n] abgelegt werden. Die Verwaltung der Front verlangt eine Datenstruktur, die die folgenden Operationen unterstützt. • finde Minimum • lösche Minimum • füge ein • verringere Inhalt eines gegebenen Elements. Hier gibt es viele verschiedene Möglichkeiten. Wir wollen nun einige vorstellen und zeigen, wie sich ihre Verwendung auf die Laufzeit auswirkt. 5 INHALTSVERZEICHNIS 0.2.1 (a,b)-Bäume Wir wählen einen ungeordneten (a, 2a)-Baum der wir folgt aufgebaut ist: 1. Die Blätter enthalten die Elemente (Dist(u), u) in beliebiger Reihenfolge 2. Jeder innere Knoten v enthält den minimalen Wert eines Blattes unter v mit Zeiger auf dieses Blatt. 3. Ein zusätzliches Zeigerfeld Pointer [1, . . . , n], wobei Pointer (u) auf das Blatt zeigt, das u enthält, bzw. Pointer (u) = ∅, falls u ∈ / U. Ein ungeordneter (a, 2a)-Baum mit n Blättern hat eine Höhe von O(log n/ log a). Damit ergeben sich folgende Laufzeiten für die einzelnen Operationen: • Finde Minimum: in O(1). • Lösche Minimum: entferne das Blatt (das durch den Minimumanzeiger gegeben ist), gehe aufwärts zur Wurzel und repariere den (a, 2a) Baum durch Verschmelzen und Stehlen mit konstant vielen Operationen pro Knoten. Das Minimum unter einem Knoten v wird durch das Minimum aus den Söhnen gebildet, d.h. es kann in O(2a) = O(a) Schritten neu berechnet werden. ⇒ O(a · log n/ log a) • Einfügen: analog zum Löschen werden Knoten gespalten und das Minimum jeweils neu berechnet. ⇒ O(a · log n/ log a) • Verringere Inhalt eines Blattes mit Zeiger. Wir verändern den Wert des Blattes und gehen rückwärts zur Wurzel. Hat ein Knoten einen Minimalwert der größer ist als der geänderte Wert, so biege das Minimum um. Nach Lemma 4 ergibt sich eine Laufzeit von O(n·a·log n/ log a+m·log n/ log a). Satz 5. Mit ungeordneten (a, 2a)-Bäumen kann das Kürzeste-Wege-Problem mit folgenden Laufzeiten gelöst werden. (i) O(n2 ) (ii) O(m log n) Beweis: (i) wähle a = n (ii) wähle a = 2. 2 6 INHALTSVERZEICHNIS 0.2.2 Fibonacci-Heaps Definition 6. Ein heap-geordneter Baum ist ein Wurzelbaum mit Heapstruktur, d.h. der Schlüssel eines Knotens ist höchstens so groß wie die Schlüssel seiner Söhne. Definition 7. Der Rang rang(x) eines Knotens x ist die Anzahl der Söhne von x. Definition 8. Seien T1 und T2 zwei heap-geordnete Bäume. Die link-Operation verschmilzt die beiden Bäume zu einem heap-geordneten Baum T. Implementation der Link-Operation: Vergleiche die Inhalte der Wurzeln r 1 von T1 und r2 von T2 . Sei o.B.d.A. Inhalt von r1 ≤ Inhalt von r2 . Mache r2 zum Sohn von r1 . W1 W2 W1 S S B B B S S j T1 j S S S S T2 B j PP B B T1 S PP S S S PP W2 P P j B B B T2 B B B ⇒ O(1). Definition 9. Ein Fibonacci-Heap ist eine Familie von (schlüssel)-disjunkten heap-geordneten Bäumen mit • Vater-Sohn-Zeiger • Sohn-Vater-Zeiger • Brüder in doppelt-verketteter Liste • Wurzeln in zirkulärer Liste • zusätzlicher Zeiger auf die Wurzel mit kleinstem Schlüssel. 7 INHALTSVERZEICHNIS Wurzel mit kleinstem Schlüssel l - l J J l - l 6 J J J J - J l j A A A Aj - j - l A A A A l A A - AAl In jedem Knoten sind gespeichert: • 4 Pointer für die Verwaltung des Heaps • der Rang rang(x) • ein weiteres Bit für “markiert” bzw. “unmarkiert”, dessen Bedeutung weiter unten erklärt wird. Einige Operationen auf F-heaps • Vereinige: Vereinige die zirkulären Wurzellisten zu einer zirkulären Wurzelliste und setze den Zeiger auf die minimale Wurzel neu. Diese Operation läßt sich wie folgt in O(1) Schritten durchführen. Seien H1 , H2 zwei F-heaps mit minimalen Wurzeln W1 , W2 , wobei o.b.d.A. Inhalt von W1 ≤ Inhalt von W2 . 8 INHALTSVERZEICHNIS ' $ = e Min W1 Min ? e & % Wurzeln von H1 e H Z ~ Z ' eW2 ? e & 6 e HH % Wurzeln von H2 W1= HH $ W2 HH H H HH HH e 6 HH je Wurzeln von H1 vereinigt H2 • Füge Schlüssel i in F-Heap H1 ein: Diese Operation ist ein Spezialfall der Vereinige-Operation und läßt sich in O(1) Schritten ausführen: 1. erzeuge einen F-Heap H2 , der aus i besteht 2. vereinige H1 und H2 • Finde Minimum: durch Pointer in O(1) • Lösche Minimum: Wenn wir das Minimum löschen, müssen wir die neue “Wurzelliste” nach dem neuen Minimum durchsuchen. Um nicht bei verschiedenen Löschschritten dieselben Knoten als Minimumskandidaten vorzufinden, ist es ratsam, mit dem Auffinden des neuen Minimums gleichzeitig einen neuen heap-geordneten Baum aufzubauen. Der folgende Algorithmus leistet dies: 1. lösche Minimum 2. konkateniere die Liste der übrigen Wurzeln mit der Liste der Söhne des alten Minimums zu einer neuen Wurzelliste. 3. gehe die neue Wurzelliste durch, um das neue Minimum zu bestimmen. 4. falls keine zwei Wurzeln den gleichen Rang haben, Stop. 9 INHALTSVERZEICHNIS 5. andernfalls seien T1 und T2 zwei Bäume mit Wurzeln r1 , r2 und rang(r1 ) = rang(r2 ). Führe link(T1 , T2 ) aus und gehe zu 3. Implementation von Schritt 3 und 4: Wir definieren ein Sortierfeld, Sortfeld [0 : M ax Rang], mit der Eigenschaft, daß Sortfeld (i) auf eine Wurzel mit Rang i zeigt. M ax Rang ist der größte vorkommende Rang. Wir werden später zeigen, daß M ax Rang = dlog ne ist. Sortfeld (i) = N il für 0 ≤ i ≤ MaxRang. While Wurzelliste 6= ∅ do entferne die erste Wurzel x aus der Liste setze AktWurzel := x setze AktRang := rang(x) = (# der Söhne von x) if Schlüssel (x) < Schlüssel (M in) then M in := W ert(x) while Sortf eld(AktRang) 6= Nil do link (AktWurzel, Sortfeld(AktRang)) setze AktWurzel auf die Wurzel der vereinigten Bäume Sortfeld (AktRang) := Nil AktRang := AktRang +1 endwhile Sortfeld (AktRang) := AktWurzel endwhile For i := 0 to MaxRang do if Sortfeld (i) 6= N il then füge Sortf eld(i) in Wurzelliste ein endfor Lemma 10. Die Laufzeit für “lösche Min” beträgt O(MaxRang + Anzahl der link-Operationen) 2 Beobachtung 11. Wenn wir auf einen anfänglich leeren F-heap nacheinander und mehrmals die Operationen “Einfügen”, “Finde min”, “lösche min” anwenden, so sind zu jedem Zeitpunkt die Bäume des F-heaps binomial, d.h.: B0 = {r0 } Bk = link(Bk−1 , Bk−1 ) 10 INHALTSVERZEICHNIS j j j B0 j % j % A A % j B1 B2 j j ``` j @ @ T T j j j j ``` j j B3 Denn: lediglich “lösche min”verändert die Bäume und der obige Algorithmus behält die binomiale Struktur bei. Lemma 12. (i) |Bk | = 2k (ii) Die Wurzel von Bk hat k Söhne (iii) x ∈ Bk ⇒ rang(x) ≤ k. Beweis: Induktion über k. (i) |Bk | = 2|Bk−1 | = 2 · 2k−1 . (ii) # Söhne von Wurzel von Bk = 1+ # Söhne von Bk−1 . (iii) x ∈ Bk−1 oder x ist die Wurzel von Bk . 2 Amortisierte Kosten der Operationen Wir haben bisher worst-case-Abschätzungen für die Laufzeiten unserer Operationen durchgeführt. Diese Operationen werden mehrmals hintereinander ausgeführt. Es ist daher durchaus möglich, daß wir bei einer Ausführung Arbeit investieren, die sich bei späteren Ausführungen durch eine geringere Laufzeit bezahlt machen. Die Analyse wird üblicherweise mit amortisierten Kosten gemacht. Sei dazu zu jedem Fibonacci-Heap H eine nichtnegative Zahl potential(H) gegeben, die wir später geeignet spezifizieren. Definition 13. Einer Operation Op, die den F-Heap H 1 in den F-Heap H2 überführt, ordnen wir wie folgt amortisierte Kosten zu: amortisierteKosten (Op) = Lauf zeit(Op) + potential(H 2 ) − potential(H1 ) Bemerkung 14. Wir starten mit einem leeren F-Heap und führen eine Folge von Operationen Op1 , Op2 , . . . , Opk durch, wobei Opi den F-Heap Hi−1 in den F-Heap Hi überführt. Dann ergeben sich die amortisierten Gesamtkosten als Summe der Laufzeiten + potential(Hk ). Da potential(Hk ) ≥ 0, gilt stets: Laufzeit ≤ amortisierte Kosten INHALTSVERZEICHNIS 11 Somit stellen die amortisierte Kosten eine obere Schranke für die Laufzeit dar. Es reicht also, die amortisierten Kosten zu berechnen, die meist besser als die Laufzeiten abgeschätzt werden können. Dabei ist es natürlich wichtig eine geeignete Potentialfunktion zu wählen. Definition 15. Sei H ein F-Heap mit Bäumen T1 , . . . , Tk . Das Potential von H ist gegeben durch: potential(H) = k = Anzahl der Bäume Wir wollen jetzt die Operationen finde Min, füge ein und lösche Min noch einmal amortisiert abschätzen. • füge ein: amortisierte Kosten O(1) • lösche Min: durch das Löschen der Wurzel in H1 erhöht sich die Anzahl der Bäume in H2 um höchstens log n ≥ rang(Wurzel) und jeder link-Schritt verringert die Anzahl um eins. Somit: potential(H2 ) ≤ potential(H1 ) + log n − Anzahl der link-Operationen Entsprechend ergeben sich die amortisierten Kosten von “lösche Min” als höchstens (vergl. Lemma 10: (log n+ Anzahl der link-Operationen) +(|Bäume inH 1 | + log n− Anzahl der link-Operationen) −|Bäume in H1 | = O(log n). • finde Min: amortisierte Kosten O(1) • verringere: Gegeben ein Zeiger auf das Element i, verringere den Schlüsselwert von i um ∆ ≥ 0 : Wir können versuchen, ∆ zu subtrahieren und danach die Heap-Struktur zu reparieren. Dies würde zu einer Laufzeit von O(log n) führen und damit die Gesamtlaufzeit gegenüber den (a, 2a)-Bäumen nicht verbessern. Wir wählen daher folgenden Ansatz: Subtrahiere ∆ vom Schlüssel in i Löse die Vater-Sohn-Verbindung von i zu seinem Vater und von i zu seinen Brüdern Verringere den Rang des Vaters Nimm i in die Wurzelliste auf Schreibe Minimalzeiger fort. in O(1) Aber: “verringere”verändert die Struktur, insbesondere die Binomialstruktur! Damit ist die Laufzeitabschätzung für lösche Min so nicht mehr gültig. Wir führen daher eine Zusatzregel ein, die dafür sorgen wird, daß die Laufzeitschranke auch weiterhin gelten wird (siehe Lemma 18). Zusatzregel: 1. wird ein Knoten x in einem link-Schritt Sohn eines anderen Knotens, lösche, wenn vorhanden, die Markierung von x INHALTSVERZEICHNIS 12 2. wird die Verbindung zwischen x und seinem Vater p(x) gelöst und ist p(x) 6= Wurzel: (a) p(x) unmarkiert ⇒ p(x) wird markiert (b) p(x) markiert ⇒ löse Verbindung p(x), p(p(x)) Bemerkung: Die Markierung zählt die von einem Knoten abgeschnittenen Söhne und begrenzt deren Anzahl. Vorsicht: der letzte Schritt kann propagieren. Werden bei einer “verringere”-Operation die Verbindungen zwischen p(x) und p 2 (x) bis hinauf zu pk (x) und pk+1 gelöst, so bewirkt die “verringere”-Operation k Folgeschnitte. Die Laufzeit ergibt sich damit als O(Anzahl der Folgeschnitte). Bemerkung: Durch die Zusatzregel haben wir erreicht, daß weiterhin die Größe eines jeden Baumes exponentiell im Rang der Wurzel ist (Korollar 17). Überlegen Sie, daß dies die einzige Eigenschaft binomialer Bäume war, die wir für die Laufzeitabschätzung von lösche Min benutzt haben, so daß diese weiterhin gültig ist (vergl. Lemma 18). Andererseits läuft “verringere” auf den ersten Blick jetzt nicht mehr in O(1). Hat eine “verringere”- Operation jedoch Folgeschnitte ausgelöst, verkleinert sich für viele andere Knoten die Anzahl der Folgeschnitte, die “verringere” auslösen könnte. Wir sind also in der typischen Situation, in der die Laufzeit mit Hilfe amortisierter Kosten und einer geeigneten Potentialfunktion besser abgeschätzt werden kann. Wir werden zeigen, daß bei mehreren Heap-Operationen die Summe der Folgeschnitte, die von einzelnen “verringere”- Operationen hervorgerufen werden, durch die Anzahl der “verringere”- Operationen beschränkt ist. Lemma 16. Sei x ein Knoten in einem F-heap und y 1 , . . . , yk die augenblicklichen Söhne von x in der Reihenfolge ihrer Link-Verbindungen zu x. Dann ist rang(yi ) ≥ i − 2. Beweis: Zum Zeitpunkt als yi zum Sohn von x wurde, hatte x mindestens i − 1 Söhne, nämlich y1 , . . . , yi−1 . Gemäß der Link-Operation, hatten x und y i den gleichen Rang, d.h. beide hatten Rang mindestens i − 1. Nach der Link-Operation kann yi höchstens einen Sohn verloren haben, da sonst die Verbindung zwischen x und yi gekappt worden wäre. 2 Korollar 17. Ein Knoten vom Rang k in einem F-Heap hat mindestens F k+2 ≥ τ k Nachfolger (sich selbst eingeschlossen, F 0 = 0, F1 = 1, F = Fk + Fk−1 und √ τ = (1 + 5)/2 der goldene Schnitt). Beweis: Sei Sk die kleinstmögliche Anzahl von Nachfolgern eines Knotens vom Rang k in einem F-Heap. Offensichtlich: S 0 = 1, und S1 = 2. Allgemein besteht ein Baum mit Wurzeln vom Rang k zumindest aus der Wurzel, dem “ältesten”Sohn und den Teilbäumen Nach obigem Lemma ist P unter den k − 1 “jüngeren”Söhnen. P dann Sk ≥ 2 + ki=2 Si−2 , für k ≥ 2. Es ist: Fk+2 = ki=2 Fi + 2 für k ≥ 2, P Pk−1 denn 2 + ki=2 Fi = 2 + i=2 Fi + Fk = Fk+1 + Fk = Fk+2 per Induktion. Somit Sk ≥ Fk+2 per Induktion und entsprechend Fk+2 ≥ τ k . 2 13 INHALTSVERZEICHNIS Lemma 18. (Amortisierte Kosten von “lösche Min”) Die amortisierten Kosten einer “lösche Min”- Operation in einem F-Heap, der durch die oben beschriebenen Operationen “finde Min”, “füge ein”, “lösche Min”, “verringere” verändert wird, betragen O(log n) Beweis: Für die Laufzeitabschätzung von “lösche Min”, haben wir den maximalen Rang mit Hilfe der binomialen Bäume durch log n abgschätzt. Dies ist jetzt nicht mehr möglich. Sei k der Rang des Minimums, dann gilt nach Korollar 17 τ k ≤ n und somit log n max Rang ≤ ≤ 1.5 log n log τ 2 Sei ck, c ∈ IR+ eine Laufzeitabschätzung für die “verringere”-Operation mit k Folgeschnitten Definition 19. Potential (H) = c(Anzahl der Bäume + 2∗ Anzahl der markierten Nichtwurzelknoten). Lemma 20. Bei einer “verringere”-Operation mit k Folgeschnitten erhöht sich das Potential erhöht sich um höchstens c(3 − k). Beweis: Da k Folgeschnitte ausgeführt werden, waren p(x), . . . , pk (x) markierte Nichtwurzeln, die zusammen mit x zu Wurzeln werden. Zudem ist p k+1 (x) Wurzel oder unmarkiert. Im letzteren Fall wird sie zu einer markierten Nicht-Wurzel. Die Anzahl der Bäume erhöht sich so um k + 1 und die Anzahl der markierten Nichtwurzeln sinkt um mindestens k − 1. Damit ergibt sich eine Potentialänderung um höchstens c(3 − k). 2 Lemma 21. Die amortisierten Kosten einer “verringere”-Operation in einem F-Heap, der durch die oben beschriebenen Operationen “finde Min”, “füge ein”, “lösche Min” und “verringere” verändert wird, betragen O(1). Beweis: Es bleibt lediglich die Aussage für die “verringere”-Operation zu zeigen. Sei dazu k die Anzahl der Folgeschnitte. Amortisierte Kosten ≤ ck + Potentialänderung ≤ ck + c(3 − k) = 3c = O(1) 2 Satz 22. Wenn wir mit einem leeren F-Heap starten und eine Folge von HeapOperationen ausführen, so ist die Laufzeit durch die amortisierte Zeit beschränkt. Beweis: Beide Potentialfunktionen sind positiv und am Anfang = 0 und somit folgt die Behauptung mit Bemerkung 13. 2 Satz 23. Die Laufzeit eines Dijkstra-Algorithmus, der die Front mit Hilfe eines F-Heaps verwaltet, beträgt O(m + n log n). INHALTSVERZEICHNIS 14 Beweis: Nach Satz 22 ist die Laufzeit durch die amortisierten Kosten beschränkt. Diese ergeben sich für jede Operation als Anzahl der Ausführungen dieser Operation mal ihren amortisierten Kosten. Insgesamt führen wir höchstens n füge-ein, n finde-Min, n lösche-Min und m verringere Operationen durch, also ergibt sich eine Laufzeit von O(n · 1 + n · 1 + n log n + m · 1) = O(m + n log n). 2 0.2.3 Buckets Lemma 24. Sei d := min{dist(u) : u ∈ U } und C = max{c(e) : e ∈ E}. Für alle v ∈ U gilt d ≤ dist(v) ≤ d + C. Beweis: Induktion über die while-Schleife, wobei wir mit d k , distk , Uk die entsprechenden Größen vor Ausführung der k-ten Iteration bezeichnen. Die Aussage ist offensichtlich richtig für k = 1. Sei u im Schritt k das Element mit minimaler Distanz und v ∈ Uk . Nach Wahl von u ist dk ≤ distk (v). Ist v ∈ Uk−1 , so gilt per Induktion , daß distk (v) ≤ distk−1 (v) ≤ dk−1 + C ≤ dk + C. Ist v∈ / Uk−1 , so ist distk (v) = dk + c(u, v) ≤ dk + C. 2 Wir können daher die Menge U in C doppelt verketteten Listen Eimer (0), . . . , Eimer (C) verwalten, wobei die Liste Eimer(k) alle Knoten v ∈ U enthält, für die gilt k = dist(v) mod (C + 1). Zusätzlich benutzen wir wieder einen Zeiger assign(v), der von v auf dasjenige Listenelement der Liste der Eimer zeigt, das v enthält. Eimer (0), Eimer (i) Eimer (C) Dist(v)mod(C + 1) = i ⇔ v ∈ Eimer (i) • füge ein: Füge v in den Eimer Dist(v)mod(C + 1) ein. • Finde Min: Sei i0 die Nummer des Eimers, der das letzte Minimum enthielt i=0 While Eimer ((i0 + i)mod(C + 1)) = ∅ und i ≤ C do i = i + 1. If i = C + 1 then Stop (Front ist leer) else das Minimum befindet sich im Eimer (i 0 + i)mod(C + 1). endif • lösche Min: Entferne das Minimum aus seinem Eimer. • Verringere: Entferne v aus dem Eimer OldDist(v)mod(C + 1) und füge v in den Eimer N ewDist(v)mod(C + 1) ein. Lemma 25. Die Laufzeit des Dijkstra-Algorithmus mit Buckets beträgt O(n · |C| + m) Beweis Füge-ein, Verringere und Lösche-Min können auf doppelt verketteten Listen in O(1) ausgeführt werden. Die Schleife in Find Min wird maximal C mal durchlaufen also ist Find Min in O(|C|) und die Behauptung folgt mit Lemma 4.2 15 INHALTSVERZEICHNIS 0.2.4 Redistributive Heaps Sei wie oben C = max cij und k = 1+dlog Ce. Im Unterschied zum vorigen EimerVerfahren, verwenden wir jetzt Eimer, deren Größen sich dynamisch ändern. Die Inhalte der Eimer werden durch Listen contents(0), . . . , contents(k) repräsentiert. Zusätzliche Zeiger assign(u) zeigen auf dasjenige Listenelement des Eimers, das den Knoten u repräsentiert. Die k +1 Eimer B0 , . . . , Bk in einem redistributiven Heap sollen folgende Bedingungen erfüllen: • Bi enthält die Knoten v mit dist(v) ∈ [li , ui ] =: range(i) • li > ui ⇒ Bi = ∅ • ui − li ≤ 2i−1 − 1 für i ≥ 1 • Weiter soll gelten: 1. dist(v) < ∞ ⇒ dist(v) ∈ [l0 , uk ] 2. v ∈ Bi , w ∈ Bj , i < j ⇒ dist(v) < dist(w) S 3. ki=0 [li , ui ] = [l0 , uk ] 4. |range(0)| = 1, |range(k)| ≥ C P 5. |range(i)| ≤ i−1 j=0 |range(j)| für i = 1, . . . , k Die Knoten mit dist(v) = ∞ verwalten wir in einem weiteren Eimer B k+1 , um in der Notation konsistent bleiben zu können. Wir wählen am Anfang range(0) = [0, 0] range(i) = [2i−1 , 2i − 1] f ür alle i = 1, . . . , k, was offensichtlich die obigen Bedingungen erfüllt. Die für die Implementation des Dijkstra-Verfahrens notwendigen Operationen können nun auf R−Heaps so realisiert werden: • Lösche: lösche Knoten u aus der Liste assign(u) : läßt sich in O(1) Schritten durchführen • Verringere: Wenn sich der Schüssel eines Knoten verringert, müssen wir den Knoten aus der Eimerliste des Eimers, der ihn momentan enthält, entfernen und in eine neue Eimerliste wiedereinfügen. Diese Prozedur nennen wir Wiedereinfügen. • Wiedereinfügen: füge einen Knoten u, der durch einen Zeiger auf die Position in seinem Eimer gegeben ist, in einen neuen Eimer ein. Diese Operation läßt sich wie folgt ausführen: procedure Wiedereinfügen (1) i = Eimer(assign(u)) (2) contents(i) = contents(i) − {u} (3) while dist(u) ∈ / range(i) do i = i − 1 INHALTSVERZEICHNIS 16 (4) contents(i) = contents(i) ∪ {u} Lemma 26. r Aufrufe der Prozedur Wiedereinfügen benötigen höchstens O(r+ nk) Schritte. Beweis: Jeder Aufruf benötigt O(1) Schritte und die Zeit, die die while-Schleife verbraucht. Bei jedem Schritt der while-Schleife wird aber der Zeiger des Eimers um 1 vermindert, d.h. jeder Knoten u kann höchstens k + 1 mal neu eingefügt werden. Damit ergibt sich eine Laufzeit von O(r + nk). 2 • Füge ein: Wie bereits gesagt, sind alle Knoten mit Dist = ∞ in einem zusätzlichen Eimer enthalten. Die Prozedur “Füge ein” muß dann dasselbe wie die Prozedur “Verringere” leisten. • Finde Min: Finde einen Knoten j mit minimaler Distanz dist(j) wie folgt (1) Suche den ersten nichtleeren Eimer B p (2) verteile den Inhalt von Bp auf die Eimer B0 , . . . , Bp (3) gib den Inhalt von B0 zurück (bzw. von B1 , falls B0 = ∅). Wir implementieren diese Idee in Form des folgenden Verfahrens: i=0 while contents(i) = ∅ do i = i + 1 p=i if p ∈ / {0, 1} then dmin = min{dist(j) : j ∈ contents(p)} l0 = u0 = dmin if p = k then for i = 1 to k do li = 2i−1 + dmin ui = 2i − 1 + dmin endfor else for i = 1 to p do li = 2i−1 + dmin ui = min{2i − 1 + dmin , up } endfor endelse for each j ∈ contents (p) : REINSERT j endif if contents(0) 6= ∅ : gib einen Knoten v ∈ contents(0) zurück, andernfalls einen v ∈ contents(1). INHALTSVERZEICHNIS 17 Lemma 27. Es gilt stets [dmin , dmin + C] ⊆ [l0 , uk ] Beweis: Diese Aussage ist offensichtlich zu Beginn des Verfahrens für l0 = 0, uk = 2k − 1 ≥ 2C − 1 und dmin = 0 erfüllt. Die einzige Operation, die die Struktur des R-Heaps verändert, ist Finde Min. Sei dazu d0min ∈ Bp das neue Minimum. Es ist l00 = d0min . Ist p = k, so gilt u0k = 2k − 1 + d0min ≥ d0min + 2C − 1. Ist p < k, so gilt nach Eigenschaft 4, uk − lk + 1 ≥ C, und somit uk ≥ C + lk − 1 ≥ C + d0min , da d0min ∈ / Bk . Damit folgt die Behauptung. 2 Lemma 28. Die Prozeduren Lösche, Wiedereinfügen, und Finde Min führen R-Heaps in R-Heaps über. Beweis: Lösche läßt offensichtlich die Bedingungen 1 - 5 invariant. (ii) Bei Finde Min bleiben die Eigenschaften 2 - 5 per Konstruktion erhalten. Die Eigenschaft 1 ergibt sich aus dem obigen Lemma. (iii) Wiedereinfügen läßt die Struktur des R-Heaps unverändert; es ist lediglich die Bedingung 1 zu überprüfen, nachdem die Distanz eines Knotens geändert wurde. Sei also dist(j) = dist(i) + cij die neue Distanz. Dann ist l0 = dmin = dist(i) ≤ dist(j) = dmin + cij ≤ dmin + C ≤ uk nach obigem Lemma. Damit haben nach Ausführung von Schritt 2 (Lösche) die Eimer wiederum R-Heap-Struktur. 2 Lemma 29. Gilt in der Prozedur Finde Min p ≥ 2, so wird der Inhalt des 0 Eimers Bp auf die neuen Eimer B00 , . . . , Bp−1 verteilt, d.h. Bp0 = ∅. Beweis: Wir zeigen, daß Bp0 = ∅. (i) p < k. Es ist up ≤ lp +|2p−1 |−1 < lp +2p−1 ≤ d0min +2p−1 . Nach Konstruktion ist lp0 = 2p−1 + d0min und u0p = min{up , 2p − 1 + d0min } < lp0 . Somit ist Bp0 = ∅. (ii) p = k : Es ist Bk0 = [d0min + C, d0min + 2C − 1]. Das letzte Intervall war Bk = [lk , uk ], wobei lk ≤ d0min und uk ≤ lk + 2k−1 − 1 ≤ d0min + C − 1 Daraus folgt, daß Bk ⊆ [l0 , d0min + C − 1]. Somit werden alle Knoten aus B k auf die Eimer 0 B00 , . . . , Bk−1 verteilt und Bk0 ist leer. 2 Satz 30. Das Dijkstra-Verfahren mit R-Heaps benötigt O(m + nlogC) Schritte. Beweis: Lösche kann höchstens n-mal aufgerufen werden mit einer Laufzeit von O(n). Wiedereinfügen wird höchstens m-mal aufgerufen, woraus sich eine Laufzeit von O(m + nk) ergibt. Weiter wird Finde Min höchstens n-mal benutzt. Jeder Aufruf benötigt höchstens O(k) Schritte, um einen ersten nichtleeren Eimer B p zu finden, und höchstens O(k) Schritte, um die Eimer neu zu erzeugen. Ist p ≥ 1, so werden zusätzlich noch O(|contents(p)|) viele Schritte für die Minimumsuche in Bp durchgeführt. Da bei jedem Durchsuchen eines Eimer der Inhalt auf Eimer mit kleinerem Index verteilt werden, kann die Gesamtanzahl der Suchschritte höchstens O(nk) betragen. Damit ergibt sich eine Gesamtlaufzeit von O(m + nk) = O(m + n log C). 2 18 INHALTSVERZEICHNIS 0.3 Skalierungs-Verfahren Definition 31. Eine Abbildung d : V → IR heißt Näherungslösung, wenn folgende Eigenschaften erfüllt sind: (i) ds = 0, (ii) di + cij ≥ dj , (iii) der maximale Abstand M axDist(i) von s nach i liegt im Intervall [d i , di + m]. Lemma 32. Gegeben eine Näherungslösung di , so läßt sich in O(m) Schritten eine exakte Lösung berechnen. Beweis: Setze c0ij = di + cij − dj . Es ist c0ij ≥ 0. Seien Dist0 (u) die im DijkstraVerfahren berechneten Distanzen bzgl. der Entfernungen c 0ij . Offensichtlich unterscheidet sich die Länge eines (s, u)-Weges bzgl. c0ij von der Länge dieses (s, u)Weges bzgl. cij nur um die Konstante du . Es gilt also stets Dist0 (u) = Dist(u)−du . Mit (iii) folgt somit Dist0 (u) ≤ m. Daher läßt sich in O(m) Schritten mit Hilfe eines Eimerverfahrens bei dem für jede mögliche Distanz ein Eimer zur Verfügung 2 gestellt wird, mit m Eimern eine Optimallösung bzgl. c0ij berechnen. Lemma 33. Sei di die doppelte Distanz von s nach i bzgl. der Kantenbewertung c0ij = bcij /2c. Dann ist di eine Näherungslösung bzgl. cij . Beweis: (i) ds = 0 (ii) Es gilt nach Beendigung des Dijkstra-Verfahrens: cij Dist0 (i) + c0ij ≥ Dist0 (j) ⇒ Dist0 (i) + b c ≥ Dist0 (j) 2 cij 0 ≥ Dist0 (j) ⇒ Dist (i) + 2 ⇒ 2 ∗ Dist0 (i) + cij ≥ 2 ∗ Dist0 (j) c (iii) Da 2b 2ij c ≤ cij ≤ 2b m = di + m. cij 2 c+1, ⇒ di + cij ≥ dj folgt di = 2∗Dist0 (i) ≤ Dist(i) ≤ 2∗Dist0 (i)+ 2 Skalierungsalgorithmus (1) Ist cij ≤ m n ∀(ij) ∈ E, so verwende Eimer-Verfahren. (2) Andernfalls berechne rekursiv die Distanzen i von s nach i, i ∈ V bzgl. bc ij /2c und die Näherungslösung di . (3) Berechne aus der Näherungslösung di die Distanz Dist(i) bzgl. cij . ⇒ Laufzeit O(m log C), wobei C = max{cij , (ij) ∈ E} 0.4 D’Esopo-Pape-Algorithmus Der Ansatz von D’Esopo und Pape versucht, die zeitaufwendige Minimumssuche zu vermeiden. Dabei wird aber in Kauf genommen werden müssen, daß ein Knoten mehrmals markiert wird. INHALTSVERZEICHNIS 0.4.1 19 D’Esopo-Pape-Verfahren Input: (Di)-Graph G = (V, E), Gewichte c(e) ∀e ∈ E, so wie Startknoten s Output: Kürzeste Wege von s nach v ∀v ∈ V und ihre Längen. Datenstrukturen: Dist(v), V or(v) ∀v ∈ V wie gehabt. (1) Dist(s) := 0 und Dist(v) = ∞, V or(v) := s ∀v ∈ V \{s} (2) Initialisiere eine Schlange Q und setze s in Q (3) Hole das erste Element aus der Schlange, sagen wir u for all (u, v) ∈ E do if Dist(u) + c(u, v) < Dist(v) then Dist(v) := Dist(u) + c(u, v); V or(v) = u if v noch nicht in Q war setze v an das Ende von Q else if v jetzt nicht in Q, setze v an den Anfang von Q endif endfor If Q 6= ∅ gehe zu 3 If Dist(v) < ∞, so enthält Dist(v) die Länge des kürzesten (s, v) Weges If Dist(v) = ∞, so gibt es keinen (s, v)-Weg. Lemma 34. Der D’Esopo-Pape Algorithmus arbeitet korrekt. Beweis: Für v ∈ V sei P ape(v) die vom d’Esopo-Pape-Algorithmus berechnete Distanz. Offensichtlich gilt P ape(v) ≥ Dist(v) für alle v ∈ V. Angenommen P ape(v) > Dist(v) für mindestens einen Knoten. Unter all diesen, wähle einen mit Dist(v) minimal und unter diesen wiederum einen, dessen kürzester Weg möglichst wenige Knoten enthält. Sei v ein solcher Knoten und u der Vorgänger von v auf einem kürzesten Weg. Per Konstruktion gilt Dist(u) = P ape(u). Zu dem Zeitpunkt, an dem u zum letzten Mal aus Q entfernt wird, muß Dist(u) = P ape(u) gelten, denn die Pape-Distanz von u bleibt danach unverändert. Danach gilt: P ape(v) ≤ P ape(u) + c(u, v) = Dist(u) + c(u, v) = Dist(v) 2 0.5 2-Listen-Verfahren und Threshold-Verfahren Während der d’Esopo-Pape-Algorithmus eine nicht polynomial beschränkte Laufzeit hat, läßt sich die folgende Variante durch O(n · m) beschränken. INHALTSVERZEICHNIS 0.5.1 20 2-Listen-Verfahren Setze Dist(u) = ∞, V or(u) = ∅ ∀u ∈ V \{s, Dist(s) = 0 und k = 0. Erzeuge zwei Listen (Stacks oder Queues) N EXT = ∅ N OW = {s}. Falls N OW = ∅, gehe zu 4. Andernfalls wähle u ∈ N OW. for all (u, v) ∈ E do if Dist(v) > Dist(u) + c(u, v), then Dist(v) = Dist(u) + c(u, v) Vor (v) = u If v ∈ / N OW ∪ N EXT : füge v zu N EXT hinzu endif endfor gehe zu 2. 2. If N EXT = ∅, ST OP Else setze N OW = N EXT, N EXT = ∅, k = k + 1 und gehe zu 2. endif (1) (2) (3) 1. Lemma 35. Sei dk = min{Dist(u) : u ∈ N EXT } am Anfang von Schritt 4 in der k-ten Iteration. Dann gilt: (i) dk ≤ dk+1 (ii) für alle u ∈ N EXT mit Dist(u) = dk ist ein kürzester Weg gefunden und u wird nicht mehr in einer N EXT -Liste auftauchen. Beweis: (i) Jeder Knoten in der N EXT -Liste der Iteration k+1 hat einen direkten Vorgänger aus der N EXT -Liste zum Zeitpunkt k. Da c ij ≥ 0, folgt dk ≤ dk+1 . (ii) folgt aus (i), da ein solcher Knoten nicht in N OW aufgenommen wird. 2 Satz 36. Der 2-Listen-Algorithmus berechnet die Länge der kürzesten Wege und hat eine Laufzeit von O(n · m). Beweis: In jeder Iteration werden schlimmstenfalls alle Kanten durchsucht. Da nach obigem Lemma in jeder Iteration stets mindestens einen Knoten der kürzeste Weg gefunden wird, kann es höchstens n Iterationen geben. 2 Beobachtung 37. : Das Lemma und der obige Satz benötigen lediglich die Tatsache, daß mindestens einer der Knoten u mit Dist(u) = d k von N EXT nach N OW geschoben wird. Es muß also nicht die gesamte Liste übernommen werden. 0.5.2 Threshold-Algorithmus Zusatzregel: Wähle eine Konstante K ∈ IN und modifiziere Schritt 4: If N EXT = ∅, STOP INHALTSVERZEICHNIS 21 else setze K Knoten aus N EXT nach N OW , die mindestens einen Knoten u enthalten, für den Dist(u) = d. k =k+1 gehe zu 2 endif z.B. wähle K − 1 beliebige Knoten und einen, für den Dist(u) = d. Satz 38. Der Threshold-Algorithmus berechnet kürzeste Wege und hat eine Laufzeit von O(n2 ). Beweis: Die Korrektheit folgt aus den obigen Bemerkungen. Die Liste N OW enthält höchstens K Knoten, von denen höchstens K(n − 1) Kanten ausgehen. D.h. in jeder Iteration sind höchstens O(n) Kanten zu benutzen. 2 0.6 Future-Cost-Verfahren Wie verdeutlichen die Idee des Future-Cost-Verfahrens zunächst an einem Beispiel: Angenommen, ich suche den kürzesten Weg von Köln nach München. Der Graph besteht z.B. aus dem Fernstraßennetz der Bundesrepublik, Knoten sind Städte bzw. Kreuzungen oder Abzweigungen von Straßen. Nach Korollar 3 genügt es, den Dijkstra-Algorithmus zu stoppen, sobald der Knoten “München”in M liegt. Allerdings wird dann immer noch z.B. Hamburg in M aufgenommen, da es näher zu Köln liegt als München. Dabei ist die Berechnung des Weges nach Hamburg unnötig, da es offensichtlich nicht auf dem optimalen Weg von Köln nach München liegt. Allein die Luftlinien-Entfernung zwischen Hamburg und München ist länger als der Weg von Köln nach München und bekanntlich ist kein Weg über Straßen kürzer als die Luftlinie. Die Idee ist nun, solche leicht berechenbaren unteren Schranken (wie hier die Luftlinie zwischen den Städten) zur Vermeidung unnötiger Berechnungen zu nutzen: Sei f : V × V → IR+ 0 eine Funktion mit f (v, v) = 0 für alle v ∈ V und f (v, w) + f (w, x) ≥ f (v, x) für alle v, w, x ∈ V für die gilt: f (v, w) ist untere Schranke für die Länge des kürzesten Weges von v nach w; v, w ∈ V. Um den kürzesten Weg von s nach t zu finden (s, t ∈ V ), definieren wir: c 0 (v, w) := c(v, w) + f (w, t) − f (v, t) für alle v, w ∈ V. 22 INHALTSVERZEICHNIS Wie man leicht sieht, gilt c0 (v, w) ≥ 0, für alle v, w ∈ V. = c(v, w) + f (w, t) − f (v, t) ≥ f (v, w) + f (w, t) − f (v, t) ≥ 0. c0 (v, w) Weiter gilt für alle Wege P von s nach t: c0 (P ) = c(P ) − f (s, t) Der kürzeste Weg von s nach t bzgl. c entspricht also dem kürzesten Weg bzgl. c0 . Damit ist es zulässig, den Algorithmus bzgl. der Kosten c 0 laufen zu lassen. Entsprechend gilt für alle Knoten u ∈ V, Dist0 (u) = Dist(u) − f (s, t) + f (u, t). Das bedeutet, daß die untere Schranke für die restliche Entfernung f (u, t) auf die Kosten Dist(u) addiert wird und so Knoten mit großer Entfernung zu t vermieden werden können. Je schärfer die untere Schranke f ist, desto effektiver arbeitet das Verfahren. Man überlege sich, wie sich der Dijkstra im schlechtesten Fall (f = 0) und im günstigsten Fall (f (v, w) ist minimale Weglänge von v nach w) verhält. Beachten Sie, daß f schnell berechenbar sein muß, da es bei jeder Kantenlänge benötigt wird. Man beachte weiter, daß das Future-Cost-Verfahren mit anderen Kürzeste-Wege-Verfahren kombinierbar ist. c0 0.7 LP-Formulierung Die Bestimmung eines kürzesten (s, t)-Weges läßt sich auch als ein lineares Optimierungsproblem formulieren. Sei dafür A die n × m Knoten-Kanten-Inzidenzmatrix mit +1 die Kante j beginnt in i aij = −1 die Kante j endet in i 0 sonst und bT = (1, 0, . . . , 0, −1) ∈ IRn . Beispiel: 1 ea Q 3 Q Q 4 Q Q Q s Q?e t * 3 s eH H HH Hj H ? 5 2 e b 23 INHALTSVERZEICHNIS s 1 1 0 0 0 a −1 0 1 1 0 A= b 0 0 0 −1 −1 t 0 0 0 −1 −1 Jeder einfache (s, t)-Weg entspricht einem 0 − 1-Vektor x = (x 1 , . . . , xm ) mit xj = 1 ⇔ Kante j liegt auf dem Weg und Ax = b, das heißt : es führt genau eine Kante aus s heraus, genau eine Kante nach t hinein und jeder andere Knoten wird entweder überhaupt nicht erreicht, oder es führt genau eine Kante hinein und eine hinaus. Somit: Jeder Inzidenzvektor x eines Weges löst Ax = b x ≥0 Umgekehrt: Betrachte das lineare Programmierungsproblem min cx (LP ) Ax = b x≥0 Lemma 39. Wenn das LP eine Optimallösung hat, dann auch eine, die Inzidenzvektor eines (s, t)-Weges ist. Beweis: Sei y eine Optimallösung. Betrachte den Teilgraphen G 0 = (V 0 , E 0 ) der durch die Kanten e mit ye > 0 induziert wird. Ist y nicht schon der Inzidenzvektor eines Weges, so existieren Knoten u, v ∈ V 0 , die in G0 durch zwei kantendisjunkte Wege P, P 0 miteinander verbunden werden. O.B.d.A. sei c(P ) ≤ c(P 0 ). Sei ye = min{yf : f ∈ P 0 }. Leite wie folgt um: , falls f ∈ / P ∪ P0 yf 0 y − ye , falls f ∈ P 0 yf = f yf + ye , falls f ∈ P y 0 ist zulässig für (LP) und cy 0 ≤ cy, d.h. y 0 ist wiederum optimal. Durch wiederholte Anwendung des obengenannten Verfahrens erhalten wir eine Optimallösung x, die Inzidenzvektor eines (s, t)-Weges ist. 2 Der Beweis zeigt unmittelbar, daß jeder einfache (s, t)-Weg in G 0 ein kürzester (s, t)-Weg in G ist. 24 INHALTSVERZEICHNIS Betrachte das zu (LP) duale Programm: . (DP ) max ys − yt yA ≤ c, . wobei die Nebenbedingungen von der Form yi − yj ≤ cij sind. Lemma 40. Sei y eine dual-zulässige Lösung. Dann liefert f (v, w) = yv − yw eine untere Schranke für die Länge des kürzesten (v, w)-Weges. Beweis: Sei P = u0 , u1 , . . . , uk mit u0 = v, uk = w ein kürzester (v, w)-Weg. Es ist c(P ) = k−1 X i=0 c(ui , ui+1 ) ≥ k−1 X i=0 yui − yui+1 = yu0 − yuk = yv − yw . 2 Da f offensichtlich eine Metrik ist, lassen sich die dual-zulässigen Lösungen im future-cost-Verfahren verwenden. Literaturverzeichnis [1] Ravindra K. Ahuja, Kurt Mehlhorn, James B. Orlin and Robert R. Tarjan. Faster Algorithms for the Shortest Path Problem. Journal of the ACM, 37 (1990), 213-223 [2] James D. Divoky and Ming S. Hung Performance of Shortest Path Algorithms in Network Flow Problems. Management Science, Vol. 36, No. 6, 1990 [3] Fred Glover, Randy Glover and Darwin Klingman. Computational Study of an Improved Shortest Path Algorithm. Networks, 14 (1984), 25-36 [4] Fred Glover, D. Klingmann and N. Phillips. A New Polynomially Bounded Shortest Path Algorithm. Operations Research, Vol. 33, 1985, 65-73 [5] Donald B. Johnson. A Priority Queue in which Initialization and Queue Operations take O(log log D) Time. Mathematical Systems Theory, 15, 295309, 1982 25