Priority Queues Fibonacci Heaps und Amortisierte Analyse Operationen: Effiziente Algorithmen WS 2008/09 Anwendungen: Bin Hu Insert ExtractMax IncreaseKey Scheduling kürzeste Wege mit Dijkstra Sortieren MST mit Prim … Binäre Heaps Fibonacci Heapeigenschaft: Vater größer als Kinder insert O(log n) extractMax O(log n) increaseKey „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 O(log n) Fibonacci Heaps Max 8 8 ai+2 = ai+1 + ai a1 = 1 a2 = 1 a = <1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 134, …> 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 Fibonacci Heap insert 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 Fibonacci Heaps: insert Max insert(v): 17 (1) mache v zu neuer Wurzel (2) setze v auf nicht angeregt (3) gegebenenfalls Max-Pointer umsetzen 8 26 8 18 5 insert(30) 12 12 3 11 7 Laufzeit: O(1), trivial Faulheit rächt sich jedoch bei extractMax Fibonacci Heaps: insert Fibonacci Heaps: extractMax Max 17 8 8 18 30 26 5 12 3 12 11 7 Extrahiere Maximum (Max-Pointer) Mache alle Kinder zu nicht angeregter Wurzel Durchlaufe alle Wurzeln, um neuen Maximum zu finden Währenddessen den Misthaufen aufräumen: Paarweise unterschiedlicher Grad bei allen Wurzeln 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 17 12 8 11 7 B[3] 18 8 17 12 8 11 7 Fibonacci Heaps: extractMax 12 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); Fibonacci Heaps: extractMax Laufzeit: O(d + m) d = deg(gelöschter Wurzelknoten) m = Anzahl der Wurzeln vor dem Löschen Worst Case: n mal insert, danach extractMax → Θ(n) Damit schlechter als Binäre Heaps!! 4 Fibonacci Heaps: increaseKey Fibonacci Heaps: increaseKey Max increaseKey(v, newKey): 17 (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); 8 8 26 18 8 16 8 17 5 11 12 12 8 16 8 11 7 26 18 5 12 12 Fibonacci Heaps: increaseKey Max 8 8 16 21 Max war schon angeregt! 26 18 5 7 21 11 increaseKey(7, 21) Fibonacci Heaps: increaseKey 17 7 Max 26 18 12 Fibonacci Heaps: increaseKey Max 17 12 3 16 increaseKey(3, 16) Fibonacci Heaps: increaseKey 5 12 17 12 8 8 16 21 12 26 11 18 5 12 11 Laufzeit? 5 Worst Case Analyse Analysemethoden Operation Binärer Heap Fibonacci Heap insert O(log n) O(1) extractMax O(log n) O(n) increaseKey O(log n) O(h(Heap)) Best Case Worst Case Average Case Amortisierte Worst Case Wurde am Anfang zu viel versprochen? Amortisierte Analyse: Idee Prinzip: Künstliche Verteuerung billiger Operationen, damit teure Operationen die amortisierten Kosten senken Aggregat Methode Bankkonto Methode Potentialfunktion Methode Beispiel Binärzähler: Bankkonto Methode Idee: Immer zwei Gold für das Flippen von 0 auf 1 zahlen, damit jede 1 ein Gold auf dem Konto hat 0 1 1 Gold 0 1 Gold Worst Case Analyse einer Sequenz von Operationen Beispiel Binärzähler: Aggregat Methode Operation 0 1 2 3 4 5 6 Zählerstand Kosten 00000 00001 1 00010 2 00011 1 00100 3 00101 1 00110 2 Durchschnitt: 10/6 < 2 Σ = 10 Beispiel Binärzähler: Bankkonto Methode Operation 0 1 2 3 4 5 6 Zählerstand Kosten Konto 00000 0 00001 2 1 00010 2 1 00011 2 2 00100 2 1 00101 2 2 00110 2 2 1 Gold 6 Beispiel Binärzähler: Potentialfunktion Methode Potential mittels Funktion Φ Beispiel Binärzähler: Potentialfunktion Methode Sei o1,...,on eine Folge von n Operationen Φ(D) besagt, wie schlecht die aktuelle Konfiguration der Datenstruktur D ist Di = Stand nach der i-ten Operation Φi = Φ(Di) = Anzahl der Einsen in Di Beispiel Binärzähler: Potentialfunktion Methode Summe der amortisierten Kosten: Σai = Σ(ti + Φi – Φi-1) = Σti + Φn – Φ0 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 Bei jeder Operation: amortisierte Kosten = tatsächliche Kosten + ∆Φ → ai = ti + Φi – Φi-1 Beispiel Binärzähler: Potentialfunktion Methode i-te Operation: …(0/1)… 0 1 … 1 Bi-1 Einser bi Potenzialfunktion: nichtnegativ am Anfang gleich Null Dann ist Φn – Φ0 ≥ 0 und damit Σti ≤ Σai …(0/1)… 1 0 … 0 Bi = Bi-1 – bi + 1 ti = bi + 1 ai = (bi + 1) + (Bi-1 – bi + 1) – Bi-1 = 2 → Σti ≤ 2n Analyse: Gradbeschränkung Wir wollen zeigen, dass die Knotengrade im Fibonacci Heap durch O(log n) beschränkt sind. 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 entweder i – 1 oder i – 2 für i = 1,…,d. Zurück zu Fibonacci Heaps 7 Analyse: Gradbeschränkung Beweis: Wenn yi zu x gelinkt wurde, hatte x schon y1,…,yi-1 als 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) Sei φ eine Zahl mit 1 < φ ≤ 2 und 1 + φ ≥ φ3, z.B. φ = 21/3 (ca. 1,26). Lemma: Sei x ein Knoten vom Grad d. Dann gilt: size(x) ≥ φd 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 Korollar: Der Grad d jedes Knotens x ist beschränkt durch: O(log n). Beweis: n ≥ size(x) ≥ φd size(x) = 1 + Σ i≤k size(yi) ≥ Laut Induktionsannahme ≥ size(yk-1) + size(yk) ≥ ≥ φk-3 + φk-2 = φk-3 (1 + φ) ≥ ≥ φk-3 φ3 = φk. Analyse: Laufzeit Wiederholung: n mal insert, danach extractMax → Θ(n) Worst Case, schlechter als binärer Heap! Aber selten so schlecht size(x) = Anzahl der Knoten, die im von x aufgespannten Unterbaum enthalten sind. deg(yi) = i – 1 oder i – 2 Analyse: Gradbeschränkung Analyse: Gradbeschränkung 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üge-Operationen) → durchschnittliche Kosten über alle Operationen ausschlaggebend → logφn ≥ d. Analyse: Laufzeit Potentialmethode mittels Funktion Φ Φ(D) besagt, wie schlecht die aktuelle Konfiguration der Datenstruktur D ist Hier: Φ bildet jeden Heap auf eine reelle Zahl ab ai = ti + ∆Φ 8 Analyse: extractMax Konkrete Wahl von Φ? Was kann später große Kosten verursachen? Sicher Anzahl der Wurzeln W Provisorisch: Φ = αW Analyse: extractMax 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), amortisierte Kosten: ai = c(d + W1) + α(W2 – W1) = (c – α)W1 + cd + αW2 Wir wählen α = c → ai = cd + cW2 aber d = O(log n) und W2 = O(log n), denn alle Wurzeln haben am Ende unterschiedlichen Grad → ai = O(log n) Analyse: increaseKey schafft k neue Wurzeln tatsächliche Kosten ti = c´(k + 1), c´ … Konstante Problem: teuer und erhöht Potenzial... Zweite Quelle für Φ: angeregte Knoten (A Stück) werden leicht zu Wurzeln und verursachen dabei ebenfalls Arbeit, also Φ = αW + βA ∆Φ ≤ αk + β(2 – k), denn alle neuen Wurzeln waren angeregt (bis auf vielleicht eine), höchstens ein Knoten regt sich neu auf. Analyse: Neuabschätzung Müssen wir extractMax neu abschätzen? Nein, denn dort werden keine Knoten an- oder abgeregt und A bleibt unberührt. insert: Erhöht die Anzahl der Wurzeln um 1 ai = ti + α = O(1 + α) = O(1) Analyse: increaseKey amortisierte Kosten ai = c´(k + 1) + αk + β(2 – k) = (c´ + α – β)k + c´+ 2β Wähle β = c´+ α, es folgt ai = 3c´ + 2α = O(1) Folgerung 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. 9 Warum „Fibonacci“-Heap? Zusammenfassung Verschärfte Version eines Lemmas, das wir bewiesen haben: Jeder Knoten vom Grad k hat mindestens Fk+2 Nachkommen. (k+2)-te Fibonacci-Zahl Fibonacci Heaps als Implementierung von Priority Queues haben (theoretisch) gute Laufzeiten Worst Case Analyse in vielen Fällen zu pessimistisch Amortisierte Analyse betrachtet das Worst Case Verhalten einer Sequenz von Operationen 10