Operationen: Insert ExtractMax Prioritätswarteschlangen IncreaseKey Fibonacci-Heaps z Ausgangsfrage: Wie kann man den Datentyp Prioritätswarteschlange realisieren? z Sternstunden der Algorithmik 8. Juni 2004 Gunnar W. Klau Anwendungen: z z z z z kürzeste Wege mit Dijkstra Finden eines minimalen Schnittes MST mit Prim … Erste Antwort: Binäre Heaps (AlgoDat I) z Problem: (theoretisch) eher langsam Binäre Heaps Fibonacci Heapeigenschaft: Vater ist nicht kleiner als seine Söhne z z 8 z z z insert: O(log n) extractMax: O(log n) increaseKey: O(log n) 6 3 Teuer, z.B., in Dijkstra: n insert → O(n log n) n extractMin → O(n log n) m decreaseKey →O(m log n) 4 5 2 Fibonacci-Heaps 1 1 n Knoten Fredman & Tarjan, 1987, Darst. hier in Anlehnung an T. Hagerup Eher von theoretischem Interesse,da Implementierung relativ aufwändig, aber: Frage: Kann z gehen? z Bin. Heap Fibonacci-Heap das besser Insert O(log n) O(1)* ExtractMax O(log n) O(log n)* IncreaseKey O(log n) O(1)* * Nicht ganz fair, weil amortisierte Analyse 2 1 3 2 4 3 5 5 6 7 8 9 10 11 12 8 13 21 34 55 89 134 Fibonacci-Heaps: Features z Operation ai+2 = ai+1 + ai a1 = 1 a2 = 1 „A certain man put a pair of rabbits in a place surrounded on all sides by a wall. How many pairs of rabbits can be produced from that pair in a year if it is supposed that every month each pair begets a new pair which from the second month on becomes productive?“ 7 1 O((n + m) log n) z z ca. 1170 – 1250, Pisa Mathematiker, ziemlich viel herumgekommen Berühmt wegen: z Menge von Bäumen, die alle die Heapeigenschaft haben (Wurzeln durch Liste verbunden) Globaler max-Pointer Bäume nicht zwangsläufig binär Jeder Knoten ist entweder aufgeregt oder nicht max 17 8 8 26 18 5 3 12 12 11 7 1 Fibonacci-Heaps: insert z z Fibonacci-Heaps: extractMax Mache v zu neuer Wurzel; Setze v auf nicht aufgeregt; Hänge eventuell max-Pointer um; Klar, dass das in O(1) Zeit geht. Die Faulheit rächt sich aber bei extractMax... insert(v): z z z z Es ist einfach, das Maximum zu finden (max-Pointer) Mache alle Kinder zu Wurzeln Durchlaufe alle Wurzeln, um neues Maximum zu finden Idee: Wenn man das schon machen muss, kann man dabei auch aufräumen z z Wir sorgen dafür, dass keine zwei Wurzeln den gleichen Grad haben Dazu: Feld B[0..n-1] mit der Interpretation B[i] = v ↔ deg(v) = i max 17 Fibonacci-Heaps: extractMax z 8 26 8 18 5 Für alle Wurzeln v: z repeat z z z z 12 3 Erhält die d = deg(v); Heapeigenschaft! if B[d] leer then setze B[d] = v; else mache kleinere Wurzel zur Tochter der größeren; v = größere Wurzel; // deg(v) ist jetzt d + 1 B[d] = leer; // wieder frei 12 11 7 17 18 in B[1], 8 in B[0], 18 in B[0], geht nicht, B[0] auf leer, 8 in B[1], 8 18 8 until (leeres Feld gefunden) 5 in B[2], B[1] auf leer, geht nicht, 12 in B[1], 12 in B[0], 3 17 11 7 18 8 in B[2], geht nicht, Fibonacci-Heaps: extractMax max 5 12 3 z z Ähnlich binärer Addition! Laufzeit: O(d + m), z z z 18 8 z 8 wobei d = deg(gelöschte Wurzel) und m = #Wurzeln vor dem Löschen genauere Analyse: später 11 7 12 in B[3], B[2] auf leer 8 11 7 increaseKey(v,newKey): z 12 17 Fibonacci-Heaps: increaseKey z 17 8 z Erhöhe Wert von v auf newKey; Heapeigenschaft verletzt? Nein → fertig. Ja: z wiederhole Mache v zu neuer nicht angeregter Wurzel; Sei u Ex-Mutter von v. u angeregt? Nein: rege u an; fertig. // einmal darf das passieren... Ja: v = u; // ... aber nicht zu oft! 2 Fibonacci-Heaps: increaseKey Erhöhe 43 auf 76 92 32 Fibonacci-Heaps: increaseKey 83 54 32 80 Erhöhe 43 auf 76 Erhöhe 58 auf 81 92 77 83 54 74 41 71 13 80 68 36 65 43 58 24 49 77 74 41 71 13 68 36 20 65 76 58 24 49 6 Fibonacci-Heaps: increaseKey 76 92 24 32 Erhöhe 58 auf 81 80 6 Fibonacci-Heaps: increaseKey 68 83 54 77 76 92 24 32 81 83 54 74 41 71 13 68 36 65 81 49 20 20 49 80 77 68 20 77 65 6 74 41 71 13 36 Erhöhe 58 auf 81 6 Fibonacci-Heaps: Analyse Fibonacci-Heaps: Analyse Wir wollen jetzt zeigen, dass die Knotengrade im Fibonacci-Heap durch O(log n) beschränkt sind (deswegen auch die „Aufregung“). z.B. φ = 21/3 (ca. 1,26). Lemma: Sei u ein Knoten vom Grad d und seien v1,...,vd die Kinder von u in der zeitlichen Reihenfolge, in der sie das letzte Mal Kinder von u wurden. Dann ist der Grad von vi mindestens i – 2 für i = 1,...,d. Beweis: Tafel. Sei φ eine Zahl mit 1 < φ ≤ 2 und 1 + φ ≥ φ3, Lemma: Sei u ein Knoten vom Grad mind. k. Dann hat u wenigstens φk Nachkommen. Beweis: Tafel. Feld B viel kleiner als vorher gedacht Korollar: Der Grad jedes Knotens ist O(log n). Beweis: Aus φk ≤ n folgt k ≤ logφn. 3 Fibonacci-Heaps: Analyse z Oops: n mal insert, 1 extractMax → Θ(n) z z z z z Amortisierte Analyse Worst case. Schlechter als binärer Heap! Aber selten so schlecht. Intuition: insert baut Spannung/Potenzial auf, das extractMax verlangsamt, sich aber dabei abbaut. Nachfolgende extractMax haben es dafür leichter... Potenzial baut sich nur langsam auf (über n Einfügeoperationen) → durchschnittliche Kosten über alle Operationen O(1). Wir brauchen eine amortisierte Analyse. z z Einfacher Ausflug: Stacks Operationen z Laufzeit für Folge von n Operationen? z z z z z Nichts für eine Realzeitumgebung. z Gleiches Ergebnis mit Potenzialfunktion Φ Φ(D) sagt, wie gut oder schlecht die aktuelle Konfiguration der Datenstruktur D ist z z Dazu Potenzial mathematisch beschreiben: z Amortisierte Kosten einer Operation sind z z Analogie: Bankkonto z z z Künstliche Verteuerung billiger Operationen („Einzahlen“) Teure Operationen dürfen „abheben“, um die amortisierten Kosten zu senken z z z z Fibonacci-Heaps: Analyse Dann ist die Summe der amortisierten Kosten n n n i=1 i=1 i=1 Wir wählen eine Potenzialfunktion, die z z z z Σ ti ≤ Σ a i ai ti Φi Φ0 amortisierte Kosten für Operation oi tatsächliche Kosten für Operation oi Potenzial direkt nach Operation oi Potenzial vor o1 Zurück zu den Stacks: z Φ(S) = |S| z Reale Kosten push, pop: 1, multipop: k z nichtnegativ und am Anfang gleich Null ist Dann ist Φn – Φ0 ≥ 0 und damit tatsächliche Kosten + ∆Φ also auch Belastung durch zukünftige Kosten (insert) oder Entschärfung von teuren Operationen (∆Φ negativ). Amortisierte Analyse Σai = Σ(ti + Φi – Φi-1) = Φn – Φ0 + Σti z Funktion Φ, die jeden Heap auf eine reelle Zahl abbildet Sei o1,...,on eine Folge von n Operationen z z jedes Element kann nur einmal durch eine pop/multipop-Operation gelöscht werden: O(n) Fibonacci-Heaps: Analyse Amortisierte Analyse z push: O(1), pop: O(1) multipop: O(n) → O(n2) etwas pessimistisch z z push(S,x), pop(S) und multipop(S,k) z Betrachte i-te Operation z push: ai = 1 + |Si| - |Si -1| = 1 + (|Si -1| + 1) - |Si -1| = 2 z multipop: ai = k + |Si| - |Si -1| = k + (|Si -1| - k) - |Si -1| = 0 Folge von n Operationen hat also amortisierte Kosten ∑ni =1 ai · 2n = O(n) 4 Fibonacci-Heaps: Analyse z z Zurück zu den Fibonacci-Heaps Konkrete Wahl von Φ? z z z Fibonacci-Heaps: Analyse z extractMax: z Was kann später große Kosten verursachen? Sicher Anzahl der Wurzeln W! Provisorisch: Φ = αW z z z fängt mit W1 Wurzeln an, hört mit W2 auf, entfernter Knoten hatte Grad d tatsächliche Kosten ti = c(d + W1), c Konstante ∆Φ = α(W2 – W1), also amortisierte Kosten: ai = c(d + W1) + α(W2 – W1) = (c – α)W1 + cd + αW2 Wir wählen α = c, also ai = cd + αW2 z z Fibonacci-Heaps: Analyse z increaseKey: z z z z z z z z amortisierte Kosten ai = c´(k + 1) + αk + β(2 – k) = (c´ + α – β)k + c´+ 2β Wähle β = c´+ α, es folgt ai = 3c´ + 2α = O(1) ∆Φ ≤ αk + β(2 – k), denn alle neuen Wurzeln waren aufgeregt (bis auf vielleicht eine), höchstens ein Knoten regt sich neu auf. (s. Bsp. auf voriger Folie) Nein, denn dort werden keine Knoten an- oder abgeregt und A bleibt unberührt. insert: z z Werden leicht zu Wurzeln und verursachen dabei noch Arbeit, also Φ = αW + βA Müssen wir extractMax neu abschätzen? z increaseKey (Forts.): tatsächliche Kosten ti = c´(k + 1), c´ Konstante Fibonacci-Heaps: Analyse z z Problem: teuer und erhöht Potenzial... Zweite Quelle für Φ: aufgeregte Knoten A z also ai = O(log n) Fibonacci-Heaps: Analyse schafft k neue Wurzeln z aber d = O(log n) und W2 = O(log n), denn alle Wurzeln haben unterschiedlichen Grad Erhöht die Anzahl der Wurzeln um 1 ai = ti + α = O(1+ α) = O(1) Resultat z Eine Folge von n insert-, min- und increaseKey- und m · n deleteMax-Operationen auf einem anfänglich leeren Fibonacci-Heap können in Zeit O(n + m log n) ausgeführt werden. Klar, dass Φ nichtnegativ und am Anfang 0 5 Warum „Fibonacci“-Heap? z Verschärfte Version eines Lemmas, das wir bewiesen haben: Jeder Knoten vom Grad k hat mindestens Fk+2 Nachkommen. (k+2)-te Fibonacci-Zahl 6