Priority Queues Operationen: z Fibonacci Heaps z z AK der Algorithmik 5, SS 2005 Hu Bin z Insert ExtractMax IncreaseKey Anwendungen: z z z z z kürzeste Wege mit Dijkstra Finden eines minimalen Schnittes MST mit Prim … Binäre Heaps Fibonacci Heapeigenschaft: Vater größer als Kinder z z z z z insert: O(log n) extractMax: O(log n) increaseKey: O(log n) „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?“ 12 8 3 4 6 2 5 Fibonacci Heaps z z z Max 8 8 ai+2 = ai+1 + ai a1 = 1 a2 = 1 Y a = <1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 134, …> z Laufzeitvergleich Menge von Bäumen, die die Heapeigenschaft erfüllen Globaler Max-Pointer Jeder Knoten ist angeregt oder nicht 17 ca. 1170 – 1250, Pisa, Mathematiker Berühmt wegen: Operation Binärer Heap insert Fibonacci Heap O(log n) O(1)* extractMax O(log n) O(log n)* increaseKey O(log n) O(1)* * Amortisierte Analyse 26 18 5 3 12 Frage: Kann extractMax schneller gehen? 12 11 7 1 Fibonacci Heaps: insert z Fibonacci Heaps: insert Max insert(v): 17 (1) mache v zu neuer Wurzel (2) setze v auf nicht aufgeregt (3) gegebenenfalls Max-Pointer umsetzen 8 26 8 18 5 insert(30) z 12 12 3 11 7 Trivial, dass die Laufzeit O(1) beträgt. Faulheit rächt sich jedoch bei extractMax Fibonacci Heaps: insert Fibonacci Heaps: extractMax Max 17 8 8 18 z 30 26 5 z 12 3 z 12 11 7 z Extrahiere Maximum (Max-Pointer) Mache alle Kinder zu nicht angeregter Wurzel Durchlaufe alle Wurzeln, um neuen Maximum zu finden Währenddessen den Haufen aufräumen, sodass alle Wurzeln paarweise unterschiedlichen Grad besitzen: z Fibonacci Heaps: extractMax Feld B[0..n-1] mit B[i] = v <=> deg(v) = i Fibonacci Heaps: extractMax Max 17 8 8 extractMax() 26 18 17 5 3 12 12 11 8 18 8 7 B[0] 5 12 3 B[1] 12 11 B[2] 7 B[3] 2 Fibonacci Heaps: extractMax 17 8 18 5 8 12 12 3 B[0] B[1] 11 B[2] Fibonacci Heaps: extractMax 17 7 B[3] 18 5 8 12 12 3 B[0] B[1] 8 B[1] 11 B[2] 7 B[3] 12 12 11 B[2] 7 B[3] 17 8 Fibonacci Heaps: extractMax 5 3 B[0] 8 8 18 8 17 17 8 8 Fibonacci Heaps: extractMax 17 18 5 8 8 3 B[0] B[1] 12 11 B[2] 17 17 8 8 18 12 7 B[3] 18 8 Fibonacci Heaps: extractMax 18 8 5 17 12 Fibonacci Heaps: extractMax 3 11 8 7 8 B[0] B[1] 5 18 12 17 12 12 3 11 7 8 B[2] B[3] 18 8 17 8 B[0] B[1] B[2] 5 3 B[3] 18 8 17 8 3 Fibonacci Heaps: extractMax 5 18 8 17 12 Fibonacci Heaps: extractMax 12 3 11 7 8 8 B[0] 17 12 12 3 11 7 8 B[1] B[2] B[3] B[0] B[1] 18 5 12 5 18 8 17 3 B[2] 18 5 12 8 17 3 8 B[3] 8 12 11 7 Fibonacci Heaps: extractMax Fibonacci Heaps: extractMax Max 5 12 18 8 3 B[0] B[1] 12 5 17 12 8 11 B[2] 5 3 3 18 8 7 B[3] z z 8 17 12 8 11 7 aufräumen: (1) für alle Wurzeln v: (2) d = deg(v); (3) wenn B[d] leer, dann B[d] = v; (4) sonst: (5) mache kleinere Wurzel zum Kind der größeren; (6) v = größere Wurzel; // deg(v) ist jetzt d + 1 (7) B[d] = leer; (8) goto (2); z 17 12 8 11 7 Laufzeit: O(d + m) z 18 Fibonacci Heaps: extractMax z 12 d = deg(gelöschter Wurzel) m = Anzahl der Wurzeln vor dem Löschen genaue Analyse später Fibonacci Heaps: increaseKey z increaseKey(v, newKey): (1) erhöhe den Wert von v auf newKey; (2) Heapeigenschaft verletzt? Nein fertig; (3) sonst: (4) u = Vater von v; (5) mache v zu nicht angeregter Wurzel; (6) ist u angeregt? (7) Nein rege ihn an; (8) Ja v = u; goto (4); 4 Fibonacci Heaps: increaseKey Fibonacci Heaps: increaseKey Max 17 8 8 Max 26 18 17 5 12 12 3 16 increaseKey(3, 16) 8 16 8 11 26 18 5 12 7 Fibonacci Heaps: increaseKey 11 8 16 8 Max 26 18 17 5 12 12 8 7 21 11 increaseKey(7, 21) Fibonacci Heaps: increaseKey Max 17 8 8 16 21 7 Fibonacci Heaps: increaseKey Max 17 12 12 26 11 18 5 12 8 16 21 26 18 5 12 12 11 Analyse: Gradbeschränkung Wir wollen zeigen, dass die Knotengrade im Fibonacci Heap durch O(log n) beschränkt sind. z Lemma: Sei x ein Knoten vom Grad d und seien y1,…,yd die Kinder gemäß der Reihenfolge beim Verlinken. Dann ist der Grad von yi mindestens i – 2 für i = 1,…,d. 5 Analyse: Gradbeschränkung z Beweis: Wenn yi zu x gelinkt wurde, hatte x schon y1,…, yi-1 Kinder. deg(x) = i - 1 deg(yi) = i - 1, da wir nur Knoten gleichen Grades verlinken. Seitdem hat yi höchstens nur 1 Kind verloren (denn ansonsten wäre er von x abgeschnitten) Analyse: Gradbeschränkung z Sei φ eine Zahl mit 1 < φ ≤ 2 und 1 + φ ≥ φ3, z.B. φ = 21/3 (ca. 1,26). z Lemma: Sei x ein Knoten vom Grad d. Dann gilt: size(x) ≥ φd ⇒ ⇒ z size(x) = Anzahl der Knoten, die im von x aufgespannten Unterbaum enthalten sind. Y deg(yi) = i - 1 oder i - 2 Analyse: Gradbeschränkung z z z z Beweis: Vollständige Induktion Induktionsanfang: Wahr für d = 0 Induktionsannahme: Wahr für alle d < k Induktionsbehauptung: d = k: size(x) ≥ φk Analyse: Gradbeschränkung z Korollar: Der Grad d jedes Knotens x ist beschränkt durch: O(log n). z Beweis: n ≥ size(x) ≥ φd size(x) = 1 + 3i≤k size(yi) ≥ Laut Induktionsannahme ≥ size(yk-1) + size(yk) ≥ ≥ φk-3 + φk-2 = φk-3 (1 + φ) ≥ ≥ φk-3 φ3 = φk. Analyse: Laufzeit z n mal insert, danach extractMax → Θ(n) z z z z z 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 ausschlaggebend. Wir brauchen die amortisierte Analyse. Y logφn ≥ d. Amortisierte Analyse Prinzip: Künstliche Verteuerung billiger Operationen, damit teure Operationen die amortisierten Kosten senken z z Potentialmethode mittels Funktion Φ Φ(D) besagt, wie schlecht die aktuelle Konfiguration der Datenstruktur D ist z z Hier: Φ bildet jeden Heap auf eine reelle Zahl ab Operation: amortisierte Kosten = tatsächliche Kosten + ∆Φ 6 Amortisierte Analyse z Sei o1,...,on eine Folge von n Operationen z z z z ai amortisierte Kosten für Operation oi ti tatsächliche Kosten für Operation oi Φi Potenzial direkt nach Operation oi Φ0 Potenzial vor o1 Amortisierte Analyse Dann ist die Summe der amortisierten Kosten 3ai = 3(ti + Φi - Φi-1) = 3ti + Φn - Φ0 z z Wir wählen eine Potenzialfunktion, die z z z z Amortisierte Analyse z Konkrete Wahl von Φ? z Was kann später große Kosten verursachen? z z Sicher Anzahl der Wurzeln W! Provisorisch: Φ = αW nichtnegativ und am Anfang gleich Null ist Dann ist Φn - Φ0 ≥ 0 und damit 3ai ≥ 3ti und ai = ti + ∆Φ A. Analyse: extractMax fängt mit W1 Wurzeln an, hört mit W2 auf, entfernter Knoten hatte Grad d z tatsächliche Kosten ti = c(d + W1), c … Konstante z ∆Φ = α(W2 – W1), amortisierte Kosten: ai = c(d + W1) + α(W2 – W1) = (c – α)W1 + cd + αW2 z z z Wir wählen α = c Y ai = cd + cW2 aber d = O(log n) und W2 = O(log n), denn alle Wurzeln haben unterschiedlichen Grad Y ai = O(log n) A. Analyse: increaseKey z z z z z schafft k neue Wurzeln tatsächliche Kosten ti = c´(k + 1), c´ … Konstante Problem: teuer und erhöht Potenzial... Zweite Quelle für Φ: aufgeregte Knoten A werden leicht zu Wurzeln und verursachen dabei ebenfalls Arbeit, also Φ = αW + βA ∆Φ ≤ αk + β(2 – k), denn alle neuen Wurzeln waren aufgeregt (bis auf vielleicht eine), höchstens ein Knoten regt sich neu auf. A. Analyse: increaseKey z amortisierte Kosten ai = c´(k + 1) + αk + β(2 – k) = (c´ + α – β)k + c´+ 2β z ai = 3c´ + 2α = O(1) z Wähle β = c´+ α, es folgt 7 Analyse z Müssen wir extractMax neu abschätzen? z z Nein, denn dort werden keine Knoten an- oder abgeregt und A bleibt unberührt. insert: z z Erhöht die Anzahl der Wurzeln um 1 ai = ti+ α = O(1+ α) = O(1) Resultat z Eine Folge von n insert- bzw. increaseKey- und m ≤ n extractMaxOperationen auf einem anfänglich leeren Fibonacci-Heap können in Zeit O(n + m log n) ausgeführt werden. Warum „Fibonacci“-Heap? Verschärfte Version eines Lemmas, das wir bewiesen haben: z Jeder Knoten vom Grad k hat mindestens Fk+2 Nachkommen. (k+2)-te Fibonacci-Zahl 8