WS Algorithmentheorie 15 – Fibonacci-Heaps Tobias Lauer
2WS Fibonacci-Heaps als lazy Binomial Queues Verschmelze Bäume nur dann, wenn ohnehin alle Wurzeln betrachtet werden müssen Lasse auch Bäume zu, die keine Binomialbäume sind
3WS Fibonacci-Heaps: Operationen Q.initialize: Q.root = null Q.insert(e): F = new F-Heap(e) Q.meld(F) Zeit = O(1)
4WS Fibonacci-Heaps: deletemin Q.deletemin(): 1. Entferne den min-Knoten und hänge stattdessen die Liste seiner Söhne in die Wurzelliste ein. 2. Gehe die Wurzelliste durch: (a) bestimme den neuen Minimalknoten (b) konsolidiere dabei die Liste, d.h. verbinde Bäume mit gleichem Wurzelgrad (link) Lösche dabei evtl. vorhandene Markierungen der Knoten, die zu Söhnen eines anderen werden.
5WS deletemin: Beispiel
6WS deletemin: Beispiel
7WS deletemin: Beispiel
8WS deletemin: Beispiel
9WS deletemin: Beispiel
10WS Kosten von deletemin Das eigentliche Entfernen geht in O(1) Kosten hängen im Wesentlichen vom Konsolidierungsprozess ab, d.h. von der Länger der Wurzelliste und der Anzahl der notwendigen link-Operationen Wie lässt sich das Konsolidieren effizient bewerkstelligen? Beobachtungen: Jeder Wurzelknoten muss mindestens einmal betrachtet werden Am Ende darf es für jeden möglichen Rang höchstens einen Knoten geben
11WS consolidate: Beispiel Rang-Array:
12WS consolidate: Beispiel Rang-Array:
13WS consolidate: Beispiel Rang-Array:
14WS consolidate: Beispiel Rang-Array:
15WS consolidate: Beispiel Rang-Array:
16WS consolidate: Beispiel
17WS Kosten von deletemin Vorläufig: O(maxRank(n)) + #links Dabei ist maxRank(n) der höchstmögliche Array-Eintrag, d.h. der größtmögliche Wurzelgrad (Rang). Im worst case ist #links = n – 1 Aber dies kann nur unter bestimmten Voraussetzungen eintreten! Amortisierte Analyse liefert realistischere Laufzeitabschätzung
18WS Fibonacci-Heaps: decreasekey Q.decreasekey(FibNode N, int k) : Setze den Schlüsselwert von N auf k herab. Wenn die Heap-Bedingung nicht mehr erfüllt ist ( k < N.parent.key ): Trenne N von seinem Vater ab (mit cut) und hänge ihn (rechts vom Minimalknoten) in die Wurzelliste ein Falls der Vater markiert ist ( N.parent.mark == true ), trenne auch ihn von seinem Vater ab; wenn auch dessen Vater markiert ist, trenne auch diesen ab usw. (cascading cuts) Markiere den Knoten, dessen Sohn zuletzt abgetrennt wurde (sofern dieser kein Wurzelknoten ist). Aktualisiere den Minimum-Zeiger (falls k < min.key ).
19WS Beispiel für decreasekey Setze den Schlüssel 64 auf 14 herab.
20WS Beispiel für decreasekey
21WS Beispiel für decreasekey
22WS Beispiel für decreasekey
23WS Beispiel für decreasekey
24WS Kosten von decreasekey Schlüssel neu setzen und Vergleich mit Vater: O(1) Abtrennen vom Vaterknoten und in Wurzelliste:O(1) Cascading cuts:#cuts Markieren des letzten Knotens:O(1) Kosten hängen von der Anzahl der cascading cuts ab. Im worst case kann auch #cuts = n – 1 sein. Dafür muss aber eine ganz bestimmte Folge von Operationen vorangegangen sein. Amortisierte Analyse betrachtet Laufzeit einer Operation im Kontext der vorangegangenen Operationen!
25WS Amortisierte Analyse – Potentialmethode Ordne jedem Zustand i der Datenstruktur einen Wert Ф i (Potential) zu Die amortisierten Kosten a i der i-ten Operation sind definiert als a i = c i + (Ф i – Ф i-1 ) d.h. die tatsächlichen Kosten zuzüglich der Änderung des Potentials, die durch die i-te Operation herbeigeführt wird. Für Fibonacci-Heaps definieren wir Ф i = w i + 2m i mit w i = Zahl der Wurzelknoten und m i = Zahl der markierten Knoten, die nicht Wurzeln sind
26WS Potential: Beispiel w i = m i = Ф i =
27WS Potentialmethode: Kosten von insert Insert: Füge den neuen Knoten in die Wurzelliste ein tatsächliche Kosten: c i = O(1) Potential erhöht sich um 1, also Ф i – Ф i-1 = 1 Amortisierte Kosten:a i = c i + 1
28WS Potentialmethode: Kosten von decreasekey Tatsächliche Kosten: c i = O(1) + #cascading cuts Potentialänderung: w i w i #cascading cuts m i m i – #cascading cuts Ф i – Ф i-1 = w i + 2m i – (w i-1 +2m i-1 ) = w i – w i-1 + 2(m i – m i-1 ) 1 + #cascading cuts + 2(1 – #cascading cuts) = 3 – #cascading cuts Amortisierte Kosten: a i = c i + (Ф i – Ф i-1 ) O(1) + #cascading cuts + 3 – #cascading cuts
29WS Potentialmethode: Kosten von deletemin Tatsächliche Kosten: c i = O(maxRank(n)) + #links Potentialänderung: w i = w i-1 – 1 + rank(min) – #links w i-1 + maxRank(n) – #links m i m i-1 Ф i – Ф i-1 = w i + 2m i – (w i-1 + 2m i-1 ) = w i – w i-1 + 2(m i – m i-1 ) maxRank(n) – #links Amortisierte Kosten: a i = c i + (Ф i – Ф i-1 ) O(maxRank(n)) + #links + maxRank(n) – #links
30WS Bestimmung von maxRank maxRank(n) ist die maximal mögliche Zahl von Söhnen, die ein Knoten in einem Fibonacci-Heap mit n Elementen haben kann. Wir wollen zeigen, dass diese Zahl durch O(log n) beschränkt ist. Umgekehrt bedeutet dies, dass jeder Knoten eine Mindestzahl an Nachfahren haben muss, die exponentiell in der Anzahl seiner Söhne ist.
31WS Berechnung von maxRank(n) Lemma 1: Sei N ein Knoten in einem Fibonacci-Heap und k = N.rank. Betrachte die Söhne C 1,..., C k von N in der Reihenfolge, in der sie (mit link) zu N hinzugefügt wurden. Dann gilt: (1)C 1.rank 0 (2) C i.rank i - 2für i = 2,..., k Beweis: (1) klar (2)Als C i zum Sohn von N wurde, waren C 1,..., C i-1 schon Söhne von N, d.h. es war N.rank i-1. Da durch link immer Knoten mit gleichem Rang verbunden werden, war beim Einfügen also auch C i.rank i-1. Seither kann C i höchstens einen Sohn verloren haben (wegen cascading cuts), daher muss gelten: C i.rank i - 2
32WS Berechnung von maxRank(n) Lemma 2: Sei N ein Knoten in einem Fibonacci-Heap und k = N.rank. Sei size(N) = die Zahl der Knoten im Teilbaum mit Wurzel N. Dann gilt:size(N) F k k D.h. ein Knoten mit k Söhnen hat mind. F k+2 Nachkommen (inkl. sich selbst). Beweis: Sei S k = min {size(N) | N mit N.rank = k}, d.h. die kleinstmögliche Größe eines Baums mit Wurzelrang k. (Klar: S 0 = 1 und S 1 = 2.) Seien wieder C 1,..., C k die Söhne von N in der Reihenfolge, in der sie zu N hinzugefügt wurden. Es ist size(N) S k =
33WS Berechnung von maxRank(n) Beweis: Sei S k = min {size(N) | N mit N.rank = k}, d.h. die kleinstmögliche Größe eines Baums mit Wurzelrang k. Seien wieder C 1,..., C k die Söhne von N in der Reihenfolge, in der sie zu N hinzugefügt wurden. Es ist size(N) S k =
34WS Berechnung von maxRank(n) Erinnerung: Fibonacci-Zahlen F 0 = 0 F 1 = 1 F k+2 = F k+1 + F k für k 0 Die Folge der Fibonacci-Zahlen wächst exponentiell mit F k k Es gilt außerdem:F k+2 = (einfacher Beweis durch vollständige Induktion über k.)
35WS Zusammenfassung Es ist außerdem S 0 = 1 = F 2 und S 1 = 2 = F 3 Damit ist klar: S k = F k+1 (Beweis per Induktion) Für einen Knoten N mit Rang k ist also size(N) S k = F k k
36WS Berechnung von maxRank(n) Satz: Der maximale Rang maxRank(n) eines beliebigen Knotens in einem Fibonacci-Heap mit n Knoten ist beschränkt durch O(log n). Beweis: Sei N ein beliebiger Knoten eines Fibonacci-Heaps mit n Knoten und sei k = N.rank. Es istn size(N) k (nach Lemma 2) Daher istk log (n) = O(log n)
37WS Zusammenfassung Lineare Liste(Min-)Heap Fibonacci-Heap insert: O(1)O(log n) O(1) min: O(n)O(1) O(1) deletemin: O(n) O(log n) O(log n)* decreasekey: O(1)O(log n) O(1)* delete: O(n) O(log n) O(log n)* *Amortisierte Kosten