Programmierpraktikum Das Canadian Traveller Problem Buckets Rainer Schrader Birgit Engels Anna Schulze Zentrum für Angewandte Informatik Köln 19. April 2006 1 / 20 2 / 20 Kürzeste Wege in Graphen Buckets Dijkstra-Algorithmus Lemma 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. (1) Dist (s) := 0, U = {s} (2) for v ∈ V \{s} do (3) Beweis: Dist (v ) = +∞, Vor (v ) = Null (4) endfor • Induktion über die while-Schleife. • Mit dk , distk , Uk werden die entsprechenden Größen vor Ausführung der (5) while U 6= ∅ (6) choose u ∈ U with Dist (u) minimal (finde Min) (7) U := U \ {u} (lösche Min) (8) for all (u, v ) ∈ E (9) Dist (v ) = Dist (u) + c(u, v ), Vor (v ) = u, (verringere) (11) U := U ∪ {v } (füge ein) (13) • • • • if Dist (u) + c(u, v ) < Dist (v ) then (10) (12) k-ten Iteration bezeichnet. endif endfor 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 , dass distk (v ) ≤ distk −1 (v ) ≤ dk −1 + C ≤ dk + C. • Ist v ∈ / Uk −1 , so ist distk (v ) = dk + c(u, v ) ≤ dk + C. (14) endwhile 3 / 20 2 4 / 20 Buckets Buckets • 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 • Die Menge U wird in C + 1 doppelt verketteten Listen Eimer(0), . . . , enthielt Eimer(C) verwaltet. 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((i0 + i ) mod (C + 1)). endif • Die Liste Eimer (k ) enthält alle Knoten v ∈ U , für die gilt k = dist (v ) mod (C + 1). • Zusätzlich benutzen wir wieder einen Zeiger assign(v ), er zeigt von v auf dasjenige Listenelement der Liste der Eimer, das v enthält. • Es gilt Dist (v ) mod (C + 1) = i ⇔ v ∈ Eimer (i ) . • Nach dem Lemma gilt, dass jeder Eimer in jeder Iteration nur Elemente enthält, deren Dist -Werte gleich sind. • 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 NewDist (v ) mod (C + 1) ein. 6 / 20 5 / 20 Buckets Lemma Die Laufzeit eines Dijkstra-Algorithmus, der die Front mit Hilfe von Buckets verwaltet, beträgt O(n · |C| + m). Beweis Redistributive Heaps • 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 + 1 mal durchlaufen also ist Find Min in O(|C|) und die Behauptung folgt mit dem folgenden weiter oben bewiesenem Lemma . 2 Lemma Die Laufzeit des Dijkstra-Algorithmus ist O(n · max{O(finde Min), O(lösche Min), O(füge ein)}) + m · O(verringere). 7 / 20 8 / 20 Redistributive Heaps Redistributive Heaps 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 • Sei C = max{c(e) : e ∈ E } und k = 1 + dlog Ce. • Im Unterschied zum vorigen Eimer-Verfahren, verwenden wir jetzt Eimer, deren Größen sich dynamisch ändern. • Weiter soll gelten: • Die Inhalte der Eimer werden durch Listen contents(0), . . . , contents(k ) 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 )| ≤ ij −1 =0 |range(j )| für i = 1, . . . , k repräsentiert. • Zusätzliche Zeiger assign(v ) zeigen auf dasjenige Listenelement des Eimers, das den Knoten v repräsentiert. • Die Knoten mit dist (v ) = ∞ verwalten wir in einem weiteren Eimer Bk +1 , um in der Notation konsistent bleiben zu können. 9 / 20 10 / 20 Redistributive Heaps Redistributive Heaps • Lösche: lösche Knoten u aus der Liste assign(u) : lässt sich in O(1) Schritten durchführen • Verringere: • Wir wählen am Anfang range(0) = [0, 0] range(i ) = [2i −1 , 2i − 1] f ür alle i = 1, . . . , k • 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. d.h. range(1) = [1, 1], range(2) = [2, 3], range(3) = [4, 7] usw. • Wiedereinfügen: • was offensichtlich die obigen Bedingungen erfüllt. • Die für die Implementation des Dijkstra-Verfahrens notwendigen • 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ässt sich wie folgt ausführen: procedure Wiedereinfügen i = Eimer (assign(u)) contents(i ) = contents(i ) − {u} while dist (u) ∈ / range(i ) do i = i − 1 contents(i ) = contents(i ) ∪ {u} Operationen können nun auf R−Heaps so realisiert werden: 11 / 20 12 / 20 Redistributive Heaps Redistributive Heaps Lemma r Aufrufe der Prozedur Wiedereinfügen benötigen höchstens O(r + nk ) Schritte. Beweis: • Finde Min: Finde einen Knoten j mit minimaler Distanz dist (j ) • Jeder Aufruf benötigt O(1) Schritte und die Zeit, die die while-Schleife verbraucht. wie folgt: • Bei jedem Schritt der while-Schleife wird aber der Zeiger des Eimers um Suche den ersten nichtleeren Eimer Bp Verteile den Inhalt von Bp auf die Eimer B0 , . . . , Bp Gib den Inhalt von B0 zurück (bzw. von B1 , falls B0 = ∅). 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 Wir implementieren diese Idee in Form des folgenden Verfahrens: • Füge ein: • Wie bereits gesagt, sind alle Knoten mit Dist = ∞ in einem zusätzlichen Eimer enthalten. • Die Prozedur “Füge ein” muss dann dasselbe wie die Prozedur “Verringere” leisten. 13 / 20 Redistributive Heaps 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 14 / 20 Redistributive Heaps 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). 15 / 20 16 / 20 Redistributive Heaps Redistributive Heaps Lemma Es gilt stets [dmin , dmin + C] ⊆ [l0 , uk ]. Lemma Die Prozeduren Lösche, Wiedereinfügen, und Finde Min führen R-Heaps in R-Heaps über. Beweis: 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. • • • • 0 Sei dazu dmin ∈ Bp das neue Minimum. 0 Es ist l00 = dmin . 0 0 Ist p = k , so gilt uk0 = 2k − 1 + dmin ≥ dmin + 2C − 1. Bei Finde Min bleiben die Eigenschaften 2 - 5 per Konstruktion erhalten. Die Eigenschaft 1 ergibt sich aus dem obigen Lemma. Wiedereinfügen lässt 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 Ist p < k , so gilt nach Eigenschaft 4, 0 0 uk − lk + 1 ≥ C, und somit uk ≥ C + lk − 1 ≥ C + dmin , da dmin ∈ / Bk . • Damit folgt die Behauptung. Lösche lässt offensichtlich die Bedingungen 1 - 5 invariant. obigem Lemma. 2 • Damit haben nach Ausführung von Schritt 2 (Lösche) die Eimer wiederum R-Heap-Struktur. 2 17 / 20 18 / 20 Redistributive Heaps Redistributive Heaps Satz Das Dijkstra-Verfahren mit R-Heaps benötigt O(m + n log C) Schritte. Lemma Gilt in der Prozedur Finde Min p ≥ 2, so wird der Inhalt des Eimers Bp auf die 0 neuen Eimer B00 , . . . , Bp−1 verteilt, d.h. Bp0 = ∅. Beweis: Beweis: • Lösche kann höchstens n-mal aufgerufen werden mit einer Laufzeit von O(n). • Wir zeigen, dass Bp0 = ∅. (i) p < k : • • • • • Wiedereinfügen wird höchstens m-mal aufgerufen, woraus sich eine Laufzeit von O(m + nk ) ergibt. 0 Es ist up ≤ lp + 2p−1 − 1 < lp + 2p−1 ≤ dmin + 2p−1 . 0 p−1 0 Nach Konstruktion ist lp = 2 + dmin und 0 } < lp0 . up0 = min{up , 2p − 1 + dmin Somit ist Bp0 = ∅. • Finde Min wird höchstens n-mal benutzt. • Jeder Aufruf benötigt höchstens O(k ) Schritte, um einen ersten nichtleeren Eimer Bp zu finden, und höchstens O(k ) Schritte, um die Eimer neu zu erzeugen. (ii) p = k : • • • • • • Ist p ≥ 1, so werden zusätzlich noch O(|contents(p)|) viele Schritte für 0 0 Es ist Bk0 = [dmin + C, dmin + 2C − 1]. Das letzte Intervall war Bk = [lk , uk ], wobei 0 0 lk ≤ dmin und uk ≤ lk + 2k −1 − 1 ≤ dmin + C − 1 = uk −1 . 0 Daraus folgt, dass Bk ⊆ [l0 , dmin + C − 1]. Somit werden alle Knoten aus Bk auf die Eimer B00 , . . . , Bk0 −1 verteilt und Bk0 ist leer. 2 die Minimumsuche in Bp durchgeführt. • Da bei jedem Durchsuchen eines Eimers 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 ) = 19 / 20 O(m + n log C). 2 20 / 20