3.2 Hu↵man-Codes Gegeben: Alphabet ⌃ und Wahrscheinlichkeiten“ P ” p(a) 2 [0, 1] für jeden Buchstaben a 2 ⌃. Also: p(a) = 1. a2⌃ Beispiel: a p(a) A 0,15 B 0,08 C 0,07 D 0,10 E 0,21 F 0,08 G 0,07 H 0,09 I 0,06 K 0,09 Herkunft der Wahrscheinlichkeiten: (1) Buchstabenhäufigkeit in natürlicher Sprache oder (2) empirische relative Häufigkeiten in einem gegebenen Text w = a1 . . . an : Anteil des Buchstabens a an w ist (p(a) · 100)%. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 27 Gesucht: ein guter“ binärer Präfixcode für (⌃, p). ” Definition 3.2.1 Präfixcode: Jedem a 2 ⌃ ist binärer Code“ c(a) 2 {0, 1}+ zugeordnet, ” mit Eigenschaft Präfixfreiheit: Für a, b 2 ⌃, a 6= b ist c(a) kein Präfix von c(b). Beispiel: A 1100 B 0110 C 000 D 111 E 10 F 0011 G 010 H 0010 I 0111 K 1101 Codierung von Wörtern (Zeichenreihen): c(a1 . . . an ) = c(a1 ) · · · c(an ) 2 {0, 1}⇤ . Zur Codierung benutzt man (konzeptuell) direkt die Tabelle. Beispiel: c(F E I G E ) = 0011 10 0111 010 10. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 28 Kompakte Repräsentation des Codes als Binärbaum: 0 0 0 C 1 1 0 H 1 1 F 0 G 0 E 0 1 0 B 1 1 I 0 A 1 D 1 K Blätter sind mit Buchstaben markiert; Weg von der Wurzel zum Blatt gibt das Codewort wieder (links: 0, rechts: 1). Decodierung: Laufe Weg im Baum, vom Codewort gesteuert, bis zum Blatt. Wiederhole mit dem Restwort, bis nichts mehr übrig ist. – Präfixeigenschaft ) keine Zwischenräume nötig. Beispiel: 001111000000010 liefert FACH“. ” FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 29 1. Idee: Mache alle Codewörter c(a) gleich lang; am besten ist dann eine Länge von dlog2 |⌃|e Bits. ) c(a1 . . . an ) hat Länge dlog2 |⌃|e · n. (Beispiele: 52 Groß- und Kleinbuchstaben plus Leerzeichen und Satzzeichen: log 64 = 6 Bits pro Codewort. ASCII-Code: 8 Bits pro Codewort.) 2. Idee: Einsparmöglichkeit: Häufige Buchstaben mit kürzeren Codes codieren als seltenere Buchstaben. Ein erster Ansatz zur Datenkompression (platzsparendes Speichern, zeitsparendes Übermitteln)! Hier: verlustfreie Kompression“ – die Information ist unverändert ” vorhanden. Gegensatz: MP3: Informationsverlust bei der Kompression. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 30 p(a) c1 c2 A 0,15 0000 1100 B 0,08 0001 0110 C 0,07 0010 000 D 0,10 0011 111 E 0,21 0100 10 F 0,08 0101 0011 G 0,07 0110 010 H 0,09 0111 0010 I 0,06 1000 0111 K 0,09 1001 1101 Wir codieren eine Datei T mit 100000 Buchstaben aus ⌃, wobei die relative Häufigkeit von a 2 ⌃ durch p(a) gegeben ist. Mit c1 (fixe Codewortlänge): 400000 Bits. Mit c2 (variable Codewortlänge): (4 · (0, 15 + 0,08 + 0,08 + 0,09 + 0,06 + 0,09) + 3 · (0,07 + 0,10 + 0,07) + 2 · 0,21) · 100000 = 334000 Bits. Bei langen Dateien und wenn die Übertragung teuer oder langsam ist, lohnt es sich, die Buchstaben abzuzählen, die relativen Häufigkeiten p(a) zu bestimmen und einen guten Code mit unterschiedlichen Codewortlängen zu suchen. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 31 Definition 3.2.2 Ein Codierungsbaum für ⌃ ist ein Binärbaum T , in dem die Kante in einem inneren Knoten zum linken bzw. rechten Kind (implizit) mit 0 bzw. 1 markiert ist; jedem Buchstaben a 2 ⌃ ein Blatt (externer Knoten) von T exklusiv zugeordnet ist. cT (a) ist die Kanteninschrift auf dem Weg von der Wurzel zum Blatt mit Inschrift a. Die Kosten von T unter p sind definiert als: X B(T, p) = p(a) · dT (a), a2⌃ wobei dT (a) die Tiefe des a-Blatts in T ist. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 32 Beispiel: Wenn T unser Beispielbaum ist und p die Beispielverteilung von oben, dann ist B(T , p) = 3,34. 0 0 0 C 1 1 0 H 1 1 F 0 G 0 E 1 0 B 1 I 0 A 1 0 1 D 1 K Leicht zu sehen: B(T , p) = |cT (a1 . . . an )|/n, wenn die relative Häufigkeit von a in w = a1 . . . an durch p(a) gegeben ist, oder B(T , p) = die erwartete Bitzahl pro Buchstabe, wenn die Buchstabenwahrscheinlichkeiten durch p(a), a 2 ⌃, gegeben sind. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 33 Definition 3.2.3 Ein Codierungsbaum T für ⌃ heißt optimal oder redundanzminimal für p, wenn B(T , p) B(T 0 , p) für alle Codierungsbäume T 0 für ⌃. Aufgabe: Zu gegebenem p : ⌃ ! [0, 1] finde einen optimalen Baum T . Existiert immer ein optimaler Baum? (Zu ⌃ gibt es unendlich viele Codierungsbäume!) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 34 Lemma 3.2.4 Wenn T Codierungsbaum und p Verteilung für ⌃ ist, dann gibt es einen Codierungsbaum T 0 mit B(T 0 , p) B(T , p), so dass in T 0 jeder innere Knoten zwei Kinder hat. Beweisidee: T: T’: w w 1 1 v x 0 Tx u fehlt Umbau: Überspringe v x u Tx Tu Tu Resultat: T 0 für ⌃ mit denselben markierten Blättern wie T , und B(T 0 , p) B(T , p). Folgerung: Man kann sich bei der Suche nach optimalen Bäumen auf solche beschränken, in denen jeder innere Knoten zwei Kinder hat. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 35 Weil es für festes ⌃ nur endlich viele ⌃-Codierungsbäume T gibt, in denen jeder innere Knoten zwei Kinder hat, gibt es für (⌃, p) optimale Codierungsbäume. Optimale Bäume sind i. A. nicht eindeutig. Aufgabe: Gegeben (⌃, p), finde einen optimalen Baum. Methode: Greedy“. ” FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 36 Lemma 3.2.5 Es seien a, a0 zwei Buchstaben mit p(a), p(a0 ) p(b) für alle b 2 ⌃ {a, a0 }. (a, a0 sind zwei seltenste“ Buchstaben.) ” Dann gibt es einen optimalen Baum, in dem die a- und a0 -Blätter Kinder desselben inneren Knotens sind. Beweis: Starte mit beliebigem optimalen Baum T . O.B.d.A. (Le. 3.2.4): Alle inneren Knoten haben zwei Kinder. a und a0 sitzen in Blättern von T , Tiefen dT (a) und dT (a0 ). O.B.d.A.: (⇤) dT (a) dT (a0 ) (sonst umbenennen). Der a-Knoten hat einen Geschwisterknoten v (innerer Knoten oder Blatt). FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 37 T: a Tv : v a’ 1. Fall: Der a0 -Knoten liegt im Unterbaum Tv mit Wurzel v . Wegen (⇤) muss er gleich v sein, und a-Knoten und a0 -Knoten sind Geschwisterknoten in T . FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 38 T: a’ a Tv : v 2. Fall: Der a0 -Knoten liegt nicht in Tv . Weil Tv mindestens ein Blatt hat und p(b) p(a0 ) für alle b 2 ⌃ gilt, haben wir P p(b) p(a0 ). {a, a0 } b in Tv FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 39 T: T’: Tv : a’ a Tv : v v a a’ Vertauschen von Blatt a0 und Tv liefert Baum T 0 , in dem a und a0 Geschwister sind, mit: X 0 0 0 B(T , p) B(T , p) = (dT (a) dT (a )) · (p(a ) p(b)) 0. b in Tv Das heißt: auch T 0 ist ein optimaler Baum für (⌃, p). FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 ⇤ 40 Damit ist der erste Schritt zur Realisierung eines Greedy-Ansatzes getan! Man beginnt den Algorithmus mit Mache die beiden seltensten Buchstaben zu Geschwistern“. ” Dann ist man sicher, dass dies stets zu einer optimalen Lösung ausgebaut werden kann. Diese optimale Lösung findet man rekursiv (konzeptuell) bzw. dann in der Realisierung iterativ. Algorithmus stammt von D. A. Hu↵man (1925–1999), am. Informatiker. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 41 Hu↵man-Algorithmus (rekursiv): Wir bauen bottom-up“ einen Baum auf. ” Wenn |⌃| = 1: fertig, man benötigt nur einen Knoten, der auch Blatt ist. Optimalität: Klar. Sonst werden zwei seltenste“ Buchstaben a, a0 aus ⌃ zu benachbarten ” Blättern gemacht. 0 1 a a’ b pa + pa’ pa p a’ FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 42 Die Wurzel des so erzeugten Mini-Baums wird als ein Kunstbuchstabe“ b ” aufgefasst mit p(b) = p(a) + p(a0 ). Neues Alphabet: ⌃0 := (⌃ p 0 (d) := ⇢ {a, a0 }) [ {b}; neue Verteilung: p(d) falls d = 6 b p(a) + p(a0 ) falls d = b Nun bauen wir durch rekursive Verwendung des Algorithmus einen Baum T 0 für ⌃0 und p 0 . In T 0 fügen wir an der Stelle des b-Knotens den a, a0 -Baum ein. Ergebnis: Ein Codierungsbaum T für ⌃, mit B(T , p) = B(T 0 , p) + (p(a) + p(a0 )). FG KTuEA, TU Ilmenau (Checken!) Effiziente Algorithmen – Sommersemester 2012 43 Lemma 3.2.6 T ist optimaler Baum für (⌃, p). Beweis: Durch Induktion über die rekursiven Aufrufe. Nach Lemma 3.2.5. gibt es einen optimalen Baum T1 für (⌃, p), in dem a- und a0 -Knoten Geschwister sind. Aus T1 bilden wir T10 durch Ersetzen des a, a0 -Teilbaums durch den Kunstknoten b. Dann ist T10 Codierungsbaum für ⌃0 . Nach I.V. für den rekursiven Aufruf ist T 0 optimal für (⌃0 , p 0 ), also B(T 0 , p 0 ) B(T10 , p 0 ). Daher: B(T , p) = B(T 0 , p 0 ) + p(a) + p(a0 ) B(T10 , p 0 ) + p(a) + p(a0 ) = B(T1 , p). Weil T1 optimaler Baum für (⌃, p) ist: B(T , p) = B(T1 , p), und auch T ist optimal. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 ⇤ 44 Noch nachzutragen: Implementierungsdetails. Laufzeitanalyse. Vergleich von B(T , p) für optimale Bäume mit der Entropie H((pa )a2⌃ ). FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 45 Man könnte nach dem angegebenen Muster eine rekursive Prozedur programmieren. PQ: Datenstruktur Priority Queue, Einträge: Buchstaben und Kunstbuchstaben; Schlüssel: die Gewichte p(b), b (Kunst-)Buchstabe. Operationen: PQ.insert: Einfügen eines neuen Eintrags; PQ.extractMin: Entnehmen des Eintrags mit kleinstem Schlüssel. Beide Operationen benötigen logarithmische Zeit (siehe 3.3). Anfangs in PQ: Buchstaben a 2 ⌃ mit Gewichten p(a) als Schlüssel. Ermitteln und Entfernen der beiden leichtesten“ Buchstaben a, a0 durch ” zwei Aufrufe PQ.extractMin; Einfügen des neuen Kunstbuchstabens b durch PQ.insert(b). FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 46 Iterative Implementierung wird effizienter. Eine spezielle Repräsentation des Baums (nur Vorgänger-Zeiger, die durch Indizes dargestellt werden) ermöglicht es, ganz ohne Zeiger auszukommen. Datenstruktur: Arrays p, pred, mark mit Indizes 1..2m Dabei repräsentieren 1, m = |⌃|. die Positionen 1, . . . , m die Buchstaben a1 , . . . , am in ⌃, also die Blätter des Baumes, die Positionen m + 1, . . . , 2m 1 die m 1 Kunstbuchstaben“, also ” die inneren Knoten des Baums. Für den Algorithmus tut man so, als ob 1, . . . , m die Buchstaben und m + 1, . . . , 2m 1 die Kunstbuchstaben“ wären. ” FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 47 p[1..2m 1] ist ein Array, das in den Positionen 1, . . . , m die Gewichte p1 , . . . , pm enthält. Positionen p[m + 1..2m 1]: Gewichte der m 1 Kunstbuchstaben“. ” 1] speichert die Vorgänger der Knoten im Das Array pred[1..2m Baum (Knoten 2m 1 ist die Wurzel und hat keinen Vorgänger). In Bitarray mark[1..2m 1] führt man mit, ob ein Knoten linkes ( 0“) ” oder rechtes ( 1“) Kind ist. ” PQ: Priority Queue, Einträge: a 2 {1, . . . , 2m 1}; Schlüssel: die Gewichte p[a]. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 48 Algorithmus Hu↵man(p[1..m]) Eingabe: Gewichtsvektor p[1..m] Ausgabe: Implizite Darstellung eines Hu↵man-Baums (1) for a from 1 to m do (2) PQ.insert(a); (⇤ Buchstabe a hat Priorität p[a] ⇤) (3) for b from m + 1 to 2m 1 do (4) a PQ.extractMin; (5) aa PQ.extractMin; (6) pred[a] b; (7) mark[a] 0; (8) pred[aa] b; (9) mark[aa] 1; (10) p[b] p[a] + p[aa]; (11) PQ.insert(b); (12) Ausgabe: pred[1..2m 1] und mark[1..2m 1]. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 49 Aus pred[1..2m 1] und mark[1..2m Hu↵man-Baum wie folgt: 1] baut man den Allokiere ein Array leaf[1..m] mit Blattknoten-Objekten und ein Array inner[m + 1..2m 1] mit Objekten für innere Knoten. (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) for i from 1 to m do leaf[i].letter Buchstabe ai . if mark[i] = 0 then inner[pred[i]].left leaf[i] else inner[pred[i]].right leaf[i] for i from m + 1 to 2m 2 do if mark[i] = 0 then inner[pred[i]].left inner[i] else inner[pred[i]].right inner[i] return inner[2m 1] (⇤ Wurzelknoten ⇤) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 50 ai i pi pred mark A 1 0,15 B 2 0,08 C 3 0,07 D 4 0,10 E 5 0,21 i pi pred mark 11 12 13 14 15 FG KTuEA, TU Ilmenau F 6 0,08 G 7 0,07 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 18 I 9 0,06 K 10 0,09 19 51 ai i pi pred mark A 1 0,15 B 2 0,08 C 3 0,07 D 4 0,10 E 5 0,21 i pi pred mark 11 0,13 12 13 14 15 FG KTuEA, TU Ilmenau F 6 0,08 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 18 I 9 0,06 11 1 K 10 0,09 19 51 ai i pi pred mark A 1 0,15 B 2 0,08 C 3 0,07 D 4 0,10 E 5 0,21 i pi pred mark 11 0,13 12 13 14 15 FG KTuEA, TU Ilmenau F 6 0,08 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 18 I 9 0,06 11 1 K 10 0,09 19 51 ai i pi pred mark A 1 0,15 B 2 0,08 i pi pred mark 11 0,13 12 0,15 FG KTuEA, TU Ilmenau C 3 0,07 12 0 D 4 0,10 E 5 0,21 13 14 15 F 6 0,08 12 1 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 18 I 9 0,06 11 1 K 10 0,09 19 51 ai i pi pred mark A 1 0,15 B 2 0,08 C 3 0,07 12 0 D 4 0,10 E 5 0,21 i pi pred mark 11 0,13 12 0,15 13 14 15 FG KTuEA, TU Ilmenau F 6 0,08 12 1 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 18 I 9 0,06 11 1 K 10 0,09 19 51 ai i pi pred mark A 1 0,15 B 2 0,08 13 0 i pi pred mark 11 0,13 12 0,15 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 D 4 0,10 E 5 0,21 14 15 F 6 0,08 12 1 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 B 2 0,08 13 0 i pi pred mark 11 0,13 12 0,15 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 D 4 0,10 E 5 0,21 14 15 F 6 0,08 12 1 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 B 2 0,08 13 0 i pi pred mark 11 0,13 12 0,15 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 D 4 0,10 14 0 14 0,19 E 5 0,21 15 F 6 0,08 12 1 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 14 1 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 B 2 0,08 13 0 i pi pred mark 11 0,13 12 0,15 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 D 4 0,10 14 0 14 0,19 E 5 0,21 15 F 6 0,08 12 1 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 14 1 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 D 4 0,10 14 0 14 0,19 E 5 0,21 15 0,28 F 6 0,08 12 1 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 14 1 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 D 4 0,10 14 0 14 0,19 E 5 0,21 15 0,28 F 6 0,08 12 1 G 7 0,07 11 0 16 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 14 1 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 16 0 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 16 1 D 4 0,10 14 0 14 0,19 E 5 0,21 15 0,28 F 6 0,08 12 1 G 7 0,07 11 0 16 0,32 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 14 1 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 16 0 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 16 1 D 4 0,10 14 0 14 0,19 E 5 0,21 15 0,28 F 6 0,08 12 1 G 7 0,07 11 0 16 0,32 Effiziente Algorithmen – Sommersemester 2012 17 H 8 0,09 14 1 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 16 0 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 16 1 D 4 0,10 14 0 14 0,19 17 1 E 5 0,21 17 0 15 0,28 F 6 0,08 12 1 G 7 0,07 11 0 16 0,32 Effiziente Algorithmen – Sommersemester 2012 17 0,40 H 8 0,09 14 1 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 16 0 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 16 1 D 4 0,10 14 0 14 0,19 17 1 E 5 0,21 17 0 15 0,28 F 6 0,08 12 1 G 7 0,07 11 0 16 0,32 Effiziente Algorithmen – Sommersemester 2012 17 0,40 H 8 0,09 14 1 18 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 16 0 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 16 1 D 4 0,10 14 0 14 0,19 17 1 E 5 0,21 17 0 15 0,28 18 0 F 6 0,08 12 1 G 7 0,07 11 0 16 0,32 18 1 Effiziente Algorithmen – Sommersemester 2012 17 0,40 H 8 0,09 14 1 18 0,60 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 16 0 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 16 1 D 4 0,10 14 0 14 0,19 17 1 E 5 0,21 17 0 15 0,28 18 0 F 6 0,08 12 1 G 7 0,07 11 0 16 0,32 18 1 Effiziente Algorithmen – Sommersemester 2012 17 0,40 H 8 0,09 14 1 18 0,60 I 9 0,06 11 1 K 10 0,09 13 1 19 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 16 0 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 16 1 D 4 0,10 14 0 14 0,19 17 1 E 5 0,21 17 0 15 0,28 18 0 F 6 0,08 12 1 G 7 0,07 11 0 16 0,32 18 1 Effiziente Algorithmen – Sommersemester 2012 17 0,40 19 0 H 8 0,09 14 1 18 0,60 19 1 I 9 0,06 11 1 K 10 0,09 13 1 19 1,00 51 ai i pi pred mark A 1 0,15 15 0 B 2 0,08 13 0 i pi pred mark 11 0,13 15 1 12 0,15 16 0 FG KTuEA, TU Ilmenau C 3 0,07 12 0 13 0,17 16 1 D 4 0,10 14 0 14 0,19 17 1 E 5 0,21 17 0 15 0,28 18 0 F 6 0,08 12 1 G 7 0,07 11 0 16 0,32 18 1 Effiziente Algorithmen – Sommersemester 2012 17 0,40 19 0 H 8 0,09 14 1 18 0,60 19 1 I 9 0,06 11 1 K 10 0,09 13 1 19 1,00 – – 51 Resultierender optimaler Codierungsbaum T : 19 0 1 18 17 5 0 0 1 8 11 0 1 p(a) c3 A 0,15 100 B 0,08 1110 C 0,07 1100 D 0,10 010 E 0,21 00 12 1 G 7 F 0,08 1101 6 1 0 F C 3 13 1 0 I 9 1 0 1 A H D 4 16 15 14 E 1 0 1 0 B 2 G 0,07 1010 K 10 H 0,09 011 I 0,06 1011 K 0,09 1111 B(T , p) = 0,21 · 2 + (0,1 + 0,09 + 0,15) · 3 + 0,45 · 4 = 3,24. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 52 Satz 3.2.7 Der Algorithmus Hu↵man ist korrekt und hat Laufzeit O(m log m), wenn m die Anzahl der Buchstaben des Alphabets ⌃ bezeichnet. Beweis: Laufzeit: Aufbau der Priority Queue dauert O(m log m); mit Tricks könnte man auch mit Zeit O(m) auskommen. Die Schleife wird (m 1)-mal durchlaufen. In jedem Durchlauf gibt es maximal 3 PQ-Operationen, mit Kosten O(log m). Korrektheit: Folgt aus der Korrektheit der rekursiven Version. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 53 Kann man B(T , p) für einen optimalen Baum einfach aus den Häufigkeiten p(a1 ), . . . , p(am ) berechnen, ohne T zu konstruieren? Antwort: Ja, zumindest näherungsweise. Definition Sind p1 , . . . , pm 0 mit Pm i=1 pi = 1, setzt man H(p1 , . . . , pm ) := m X i=1 pi · log(1/pi ). H(p1 , . . . , pm ) heißt die Entropie der Verteilung p1 , . . . , pm . (Wenn pi = 0 ist, setzt man pi log(1/pi ) = 0, was vernünftig ist, weil limx&0 x · log(1/x) = 0.) Bsp.: H( 12 , 14 , 14 ) = FG KTuEA, TU Ilmenau 1 2 ·1+2· 1 4 · 2 = 32 . Effiziente Algorithmen – Sommersemester 2012 54 Interessant: Zusammenhang zwischen Entropie H(p1 , . . . , pm ) und der erwarteten Bitlänge eines Textes, in dem m = |⌃| Buchstaben mit Wahrscheinlichkeiten p1 , . . . , pm auftreten. Klassisches Resultat: Lemma 3.2.8 (Lemma von Gibb) Sind q1 , . . . , qm > 0 mit m X Pm pi log(1/qi ) i=1 i=1 qi m X 1= Pm i=1 pi , so gilt pi log(1/pi ) = H(p1 , . . . , pm ). i=1 (Voraussetzung kann zu pi > 0 ) qi > 0 abgeschwächt werden.) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 55 Beweis: Weil log2 x = ln x/ ln 2 ist, darf man mit dem natürlichen Logarithmus rechnen. ✓ ◆ X ✓ ◆ m m X 1 1 pi ln pi ln pi qi i=1 i=1 ✓ ◆ (⇤) X ✓ ◆ m m X qi qi = pi ln pi 1 pi pi = i=1 m X i=1 (⇤): Es gilt ln(x) x lässt man einfach weg.) FG KTuEA, TU Ilmenau i=1 (qi pi ) = m X i=1 qi m X i=1 pi 0. 1, für alle x 2 R. (Summanden für i mit pi = qi = 0 Effiziente Algorithmen – Sommersemester 2012 56 Satz 3.2.9 (Ungleichung von Kraft/McMillan) Es seien m 1, l1 , . . . , lm 2 N. Dann gilt: Es gibt einen Präfixcode mit Codewortlängen l1 , . . . , lm genau dann wenn m X i=1 2 li 1. )“: Nach den Bemerkungen am Anfang von Abschnitt 3.2 kann man ” statt Existenz eines Präfixcodes“ auch Existenz eines Binärbaums mit ” ” Blättern auf Tiefe l1 , . . . , lm“ sagen. http://www.tu-ilmenau.de/iti/lehre/lehre-ss-2011/ algorithmen-und-datenstrukturen/ AuD-Vorlesung 2011, 3. Kapitel, Lemma 3.3.4, Folie 44. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 57 Pm (“: Nun seien l1 , . . . , lm 0 gegeben, mit i=1 2 li 1. Wir benutzen ” Induktion über m 1 (äquivalent: einen rekursiven Algorithmus), um die behauptete Existenz eines passenden präfixfreien Codes zu zeigen. m = 1: Wähle ein beliebiges Codewort x1 aus {0, 1}l1 . (Achtung: Wenn l1 = 0, ist x1 = ", das leere Wort. Dies entspricht einem Codierungsbaum, der nur aus der Wurzel besteht.) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 58 Nun sei m 2. Wir ordnen (o.B.d.A.) so an, dass Pm die l1l1 ,l . . . , lm l1 l2 · · · lm gilt. Nach Vor.: i=2 2 i < 2l1 ; die Summe ist durch 2l1 l2 teilbar. P m l1 l Also: i=2 2 i 2l1 2l1 l2 . P m l1 l l l 1 2 Daraus: 2 · 2 + i=3 2 i 2l1 , d. h. Pm (l 1) 2 2 + i=3 2 li 1. Setze l10 := l2 1 und finde (nach Induktionsvoraussetzung bzw. rekursiv) einen Präfixcode {x10 , x3 , x4 , . . . , xm } für l10 , l3 , . . . , lm . Nun bilde x2 := x10 1 und x1 := x10 0 . . . 0 (mit l1 l2 + 1 angehängten Nullen). Es ist leicht zu sehen, dass auch {x1 , . . . , xm } präfixfrei ist. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 59 Beispiel: (l1 , . . . , l6 ) = (1, 4, 5, 3, 6, 3); sortiert: (6, 5, 4, 3, 3, 1). Dies führt zu rekursiven Aufrufen für: 1) (4, 4, 3, 3, 1) 2) (3, 3, 3, 1) 3) (2, 3, 1), sortiert: (3, 2, 1) 4) (1, 1) 5) (0). FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 60 Die Präfixcodes für diese Aufrufe: 5) {✏} 4) {0, 1} 3) {000, 01, 1}, also {01, 000, 1} 2) {010, 011, 000, 1} 1) {0100, 0101, 011, 000, 1} Gesamtlösung: {010000, 01001, 0101, 011, 000, 1} FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 61 Satz 3.2.10 (Hu↵man versus Entropie) P Ist p : ⌃ ! [0, 1] mit a2⌃ p(a) = 1 gegeben, so gilt für einen optimalen Codierungsbaum T zu (⌃, p): H(p1 , . . . , pm ) B(T , p) H(p1 , . . . , pm ) + 1. (Informal: Setze pi = p(ai ), für ⌃ = {a1 , . . . , am }. Die erwartete Zahl von Bits, die man braucht, um einen Text t1 . . . tN über ⌃ zu codieren, liegt zwischen N · H(p1 , . . . , pm ) und N · (H(p1 , . . . , pm ) + 1).) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 62 Beweis: 1. Ungleichung: Es seien l1 , . . .P , lm die Tiefen der Blätter in T zu li 1 nach Satz 3.2.9. den Buchstaben a1 , . . . , am . Dann gilt m 2 i=1 Damit können wir Lemma 3.2.8 mit qi = 2 li anwenden und erhalten B(T , p) = m X i=1 FG KTuEA, TU Ilmenau pi · l i = m X i=1 pi · log(1/2 li ) Effiziente Algorithmen – Sommersemester 2012 H(p1 , . . . , pm ). 63 Für die 2. Ungleichung genügt es zu zeigen, dass ein Codierungsbaum T 0 für a1 , . . . , am existiert, in dem B(T 0 , p) H(p1 , . . . , pm ) + 1 gilt. (Der optimale Baum T erfüllt ja B(T , p) B(T 0 , p).) Wir setzen li := dlog(1/pi )e, für 1 i m, und beobachten: m X 2 li = i=1 = m X i=1 m X 2 dlog(1/pi )e m X 2 log(1/pi ) i=1 pi = 1. i=1 FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 64 Nach Satz 3.2.9 existiert also ein Präfixcode mit Codewortlängen (l1 , . . . , lm ); im entsprechenden Codierungsbaum T 0 ordnen wir dem Blatt auf Tiefe li den Buchstaben ai zu. Dann ist B(T 0 , p) = m X i=1 pi · li m X i=1 pi · (log(1/pi ) + 1) = H(p1 , . . . , pm ) + m X pi i=1 = H(p1 , . . . , pm ) + 1. Bemerkung: Es gibt bessere Kodierungsverfahren als das von Hu↵man (z.B. arithmetische Kodierung“; diese vermeiden den Verlust von bis zu ” einem Bit pro Buchstabe), aber Hu↵man-Kodierung ist ein guter Anfang ... FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 65 3.3 (Hilfs-)Datenstruktur Priority Queues (oder: Vorrangswarteschlangen) Datensätze mit Schlüssel aus sortiertem Universum (U, <) werden eingefügt und entnommen. Beim Entnehmen wird immer der Eintrag mit dem kleinsten Schlüssel gewählt. Schlüssel = b Prioritäten“. ” Beim Hu↵man-Algorithmus: Datensätze sind (Kunst-)Buchstaben, Prioritäten/Schlüssel sind die Gewichte. Vorlesung Algorithmen und Datenstrukturen“: ” Spezifikation und Realisierung mit Binärheaps. http://www.tu-ilmenau.de/iti/lehre/lehre-ss-2011/ algorithmen-und-datenstrukturen/, Kapitel 6. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 66 Operationen: empty – leere PQ anlegen. isempty – PQ auf Leerheit prüfen. insert – neues Element einfügen. extractMin – ein Element mit kleinster Priorität löschen. decreaseKey – die Priorität eines Elements in der PQ senken. Wir nutzen zur Implementierung von Prioritätswarteschlangen Binärheaps. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 67 Ein linksvollständiger Binärbaum: Alle Levels j = 0, 1, . . . voll (jeweils 2j Knoten) bis auf das tiefste. Das tiefste Level l hat von links her gesehen eine ununterbrochene Folge von Knoten. Können gut als Arrays dargestellt werden! FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 68 Nummerierung von Knoten in unendlichem, vollständigen Binärbaum in Levelorder: 1 1 0 2 0 0 1 1 0 1 0 7 0 11 0 1 6 1 10 9 3 0 5 8 0 1 4 0 1 1 12 0 1 14 13 1 0 1 0 1 0 15 0 1 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 0 32 1 1 46 47 63 0 Zeiger zum linken bzw. rechten Kind mit 0 bzw. 1 markiert. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 69 Knoten Knoten Knoten Knoten 1 ist die Wurzel; 2i ist linkes Kind von i; 2i + 1 ist rechtes Kind von i; i 2 hat Vater bi/2c. Damit: Array-Darstellung für linksvollständige Binärbaume: Speichere Einträge in Knoten 1, . . . , n in Array A[1 . . n]. Spart den Platz für die Zeiger! FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 70 Definition 3.3.1 Sei (U, <) Totalordnung. Ein (Teil-)Array A[1 . . k] ist ein Heap, falls für 1 i k gilt: 2i k ) A[i].key A[2i].key und 2i + 1 k ) A[i].key A[2i + 1].key. Aufgrund besserer Darstellbarkeit betrachten wir im weiteren Verlauf die Baumdarstellung. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 71 B E F G N E P H F N NB: T heapgeordnet ) in Knoten v steht der minimale Eintrag des Teilbaums Tv mit Wurzel v . FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 72 Beispiel: U = {A, B, C, . . . , Z} mit der Standardordnung. Im Beispiel: Daten weggelassen. Ein Min-Heap und der zugeordnete Baum (k = 10): 1 2 3 4 5 6 7 8 9 10 B E F G E H F H I G 1 2 * * B E F 5 E G 6 H F 7 9 10 H FG KTuEA, TU Ilmenau 12 3 4 8 11 I G Effiziente Algorithmen – Sommersemester 2012 73 Implementierung einer Priority Queue: Neben Array A[1 . . m]: Pegel k: Aktuelle Zahl k von Einträgen. (Überlaufprobleme werden ausgeklammert. Verdoppelungsstrategie, falls nötig.) empty(m): Lege Array A[1 . . m] an; (⇤ Jeder Eintrag ist ein Paar (key, data). ⇤) k 0. (⇤ Zeitaufwand: O(1) oder O(m) ⇤) isempty(): return(k = 0); (⇤ Zeitaufwand: O(1) ⇤) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 74 extractMin: Implementierung von extractMin(P): Ein Eintrag mit minimalem Schlüssel steht in der Wurzel, d. h. in Arrayposition 1. Entnehme A[1] (hier B“) und gib es aus. ”2 3 4 5 6 7 8 1 B E F G E 1 2 H 10 I G 11 12 * * B 3 F 5 E G 6 H F 7 9 10 H FG KTuEA, TU Ilmenau F E 4 8 H 9 I G Effiziente Algorithmen – Sommersemester 2012 75 extractMin: Implementierung von extractMin(P): Ein Eintrag mit minimalem Schlüssel steht in der Wurzel, d. h. in Arrayposition 1. Entnehme A[1] (hier B“) und gib es aus. ”2 3 4 5 6 7 8 1 G E F G E 1 2 F H 12 10 I * * * G 3 E F 4 5 E G 8 H 11 9 6 H F 7 9 10 H I * Loch“ im Binärbaum. – stopfen“ mit dem letzten Eintrag. ” ” FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 75 Heaps reparieren 11 12 1 2 3 4 5 6 7 8 9 10 G E F G E H F H I * * * 1 < 2 3 E 5 E G F < 4 8 G 6 H F 7 9 H I Vergleich der beiden Kinder des aktuellen Knotens. Vergleich des kleineren“ Kinds mit dem aktuellen Knoten. ” Vertauschen des kleineren“ Kinds (E) mit dem aktuellen Knoten (G). ” FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 76 Heaps reparieren Ergibt Abwärts-Fast-Heap, aktueller Knoten ein Level tiefer. 11 12 1 2 3 4 5 6 7 8 9 10 E G F G E H F H I * * * 1 E 2 3 G F > 4 5 G > 8 E 6 H F 7 9 H I Vergleich der beiden Kinder des aktuellen Knotens. Vergleich des kleineren“ Kinds mit dem aktuellen Knoten. ” Vertauschen des kleineren“ Kinds (E) mit dem aktuellen Knoten (G). ” FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 76 Heaps reparieren Ergibt Abwärts-Fast-Heap, aktueller Knoten ein Level tiefer. 11 12 1 2 3 4 5 6 7 8 9 10 E E F G G H F H I * * * 1 E 2 3 F E 5 4 G G 8 6 H F 7 9 H I Aktueller Knoten hat keine Kinder. Kein Fehler mehr: Reparatur beendet. Andere Möglichkeit für Ende: Eintrag im aktuellen Knoten ist nicht größer als der im kleineren Kind“. ” FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 76 Erinnerung: Wenn A ein Abwärts-Fast-Heap bzgl. Stelle k ist, dann gibt es einen Schlüssel z A[k].key, so dass ein Heap entsteht, wenn man A[k].key durch z ersetzt. Lemma 3.3.2 Sei A ein Abwärts-Fast-Heap bzgl. Stelle k. 1 Falls A[k] maximal so groß wie jeder seiner Kindschlüssel ist (sofern diese existieren), dann ist A ein Heap. 2 Sei A[m] das kleinste Kind von A[k] und A[k] > A[m]. Nach Vertauschen von A[k] und A[m] ist A ein Abwärts-Fast-Heap bzgl. Stelle m. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 77 Beweis von Lemma 3.3.2. Für die nachstehenden Überlegungen ist folgendes Bild nützlich, welches die Beziehungen zwischen den Prioritäten verdeutlicht, die aus der Eigenschaft Abwärts-Fast-Heap an Stelle k“ ” folgen: c1 z c1 bk/2c y y z k x z x c2 c2 z O.B.d.A. nehmen wir an, dass m = 2k. Das linke Kind (c1 ) ist also das kleinere Kind. (Sonst: Umbenennen!) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 77 1. Fall: Es gilt c1 x und c2 x. Des Weiteren gilt (immer) y x. Wenn A mit A[k] z ein Heap ist, dann auch mit A[k] = x. 2. Fall: Wir vertauschen in 2 Schritten: 1 Setze A[k] c1 . Da y z und z c1 , ist also y c1 . Damit ist A, unter der Voraussetzung dass A ein Abwärts-Fast-Heap bzgl. Stelle k ist, ein Heap. 2 Setze A[m] Stelle m. FG KTuEA, TU Ilmenau x. Damit wird A ein Abwärts-Fast-Heap bezüglich Effiziente Algorithmen – Sommersemester 2012 77 bubbleDown(1, k) (⇤ Heap-Reparatur in A[1 . . k] ⇤) (⇤ Muss wissen: A[1 . . k] ist Abwärts-Fast-Heap bzgl. Position 1 ⇤) (1) j 1; m 2; done false; (2) while not done and m + 1 k do (⇤ Abwärts-Fast-Heap bzgl. Position j, A[j] hat 2 Kinder ⇤) (3) if A[m+1].key < A[m].key (4) then (⇤ A[m]: kleineres“ Kind ⇤) ” (5) m m + 1; (6) if A[m].key < A[j].key (7) then vertausche A[m] mit A[j]; j m; m 2 ⇤ j; (8) (⇤ nun wieder: Abwärts-Fast-Heap bzgl. j ⇤) (9) else (⇤ fertig, kein Fehler mehr ⇤) (10) done true; (11) if not done then (12) if m k then (⇤ Abwärts-Fast-Heap bzgl. j, ein Kind in A[m] ⇤) (13) if A[m].key < A[j].key (14) then vertausche A[m] mit A[j]; FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 78 Korrektheit: Folgt unmittelbar aus den Überlegungen aus Lemma 3.3.2. Kosten: Im Binärbaum gibt es maximal dlog(k + 1)e Levels; für jedes Level maximal einen Schleifendurchlauf. Also Kosten: O(log k). FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 79 extractMin (⇤ Entnehmen eines minimalen Eintrags aus Priority-Queue ⇤) (⇤ Ausgangspunkt: Pegel k, A[1 . . k] ist Heap, k 1 ⇤) (1) x A[1].key; d A[1].data; (2) A[1] A[k]; (3) k--; (4) if k > 1 then bubbleDown(1, k); (5) return (x, d); Korrektheit: klar wegen Korrektheit von bubbleDown(1, k). Zeitaufwand: O(log(k)). FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 80 Implementierung von insert(x, d): Voraussetzung: A[1..k] ist Heap; 1 i k < m. k++; A[k] (x, d). An der Stelle k ist nun eventuell die Heapeigenschaft gestört (x zu klein). Wir nennen A[1..k] einen Aufwärts-Fast-Heap bzgl. k. Heißt: Es gibt einen Schlüssel z A[k].key, so dass ein Heap entsteht, wenn man A[k].key durch z ersetzt. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 81 Heap: 1 2 3 4 5 6 7 8 9 10 C E F G J K F H L Q 11 12 * * 1 C 2 3 E F 4 5 G J 8 6 7 K F 9 10 H FG KTuEA, TU Ilmenau L Q Effiziente Algorithmen – Sommersemester 2012 82 Einfügen von D“ an Stelle k = 11. ” 1 2 3 4 5 6 7 8 9 10 C E F G J K F H L Q 11 D 12 * 1 C 2 3 E F 4 5 G J 8 9 10 H FG KTuEA, TU Ilmenau 6 L 7 K F 11 Q D Effiziente Algorithmen – Sommersemester 2012 82 Heapreparatur mittels bubbleUp. 1 2 3 4 5 6 7 8 9 10 C E F G J K F H L Q 11 D 12 * 1 C 2 3 E F 4 5 G J 8 9 10 H FG KTuEA, TU Ilmenau 6 L 7 K F 11 Q D Effiziente Algorithmen – Sommersemester 2012 82 Heapreparatur mittels bubbleUp. 1 2 3 4 5 6 7 8 9 C E F G D K F H L 10 11 Q J 12 * 1 C 2 3 E F 4 5 G 6 K D 8 9 10 H FG KTuEA, TU Ilmenau L 7 F 11 Q J Effiziente Algorithmen – Sommersemester 2012 83 Heapreparatur mittels bubbleUp. 1 2 3 4 5 6 7 8 9 C E F G D K F H L 10 11 Q J 12 * 1 C 2 3 E F 4 5 G 6 K D 8 9 10 H FG KTuEA, TU Ilmenau L 7 F 11 Q J Effiziente Algorithmen – Sommersemester 2012 82 Heapreparatur mittels bubbleUp. 1 2 3 4 5 6 7 8 9 C D F G E K F H L 10 11 Q J 12 * 1 C 2 3 D F 4 5 G 6 K E 8 9 10 H FG KTuEA, TU Ilmenau L 7 F 11 Q J Effiziente Algorithmen – Sommersemester 2012 82 Heapreparatur mittels bubbleUp. 1 2 3 4 5 6 7 8 9 C D F G E K F H L 10 11 Q J 12 * 1 C 2 3 D F 4 5 G 6 K E 8 9 10 H FG KTuEA, TU Ilmenau L 7 F 11 Q J Effiziente Algorithmen – Sommersemester 2012 82 Heapreparatur mittels bubbleUp. 1 2 3 4 5 6 7 8 9 C D F G E K F H L 10 11 Q J 12 * 1 C 2 3 D F 4 5 G 6 K E 8 9 10 H FG KTuEA, TU Ilmenau L 7 F 11 Q J Effiziente Algorithmen – Sommersemester 2012 82 Lemma 3.3.3 Sei A ein Aufwärts-Fast-Heap bzgl. j. 1. Falls j = 1 oder A[j] A[bj/2c] dann ist A ein Heap. 2. Sei j > 1 und A[j] < A[bj/2c]. Nach Vertauschen von A[j] und A[bj/2c] ist A ein Aufwärts-Fast-Heap bzgl. Stelle bj/2c. Beweis: 1. Erinnerung: Wenn A Aufwärts-Fast-Heap an Stelle j ist, dann existiert Schlüssel z A[j].key, so dass ein Heap entsteht, wenn man A[j].key durch z ersetzt. Es folgt, dass Schlüssel A[j].key maximal so groß ist wie die Kindschlüssel. (Diese sind mindestens so groß wie z.) Nach Voraussetzung ist er nicht kleiner als Vaterschlüssel (falls existent), also ist A ein Heap. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 83 2. Wir betrachten das folgende Bild. c1 z bj/2c y y z j x x <y z c1 c2 c2 z Wir vertauschen in 2 Schritten: 1 2 Überschreibe A[j] mit y . Dies liefert nach Voraussetzung einen Heap. Überschreibe A[bj/2c] mit x. Nun ist A ein Aufwärts-Fast-Heap bzgl. Stelle bj/2c. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 84 Prozedur bubbleUp(i) (⇤ Heapreparatur ab A[i] nach oben, Aufruf nur wenn A ein Aufwärts-Fast-Heap bzgl. Stelle (1) j i; (2) h j div 2; (3) while h 1 and A[h].key < A[j].key do (4) vertausche A[j] mit A[h]; (5) j h; (6) h j div 2. Klar: Wenn A Aufwärts-Fast-Heap bzgl. Stelle i ist, so ist A nach Aufruf von bubbleUp(i) ein Heap. (Folgt unmittelbar aus den Überlegungen von Lemma 3.3.3.) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 85 1 2 3 4 5 6 7 8 9 C D F G E K F H L 10 11 Q J 12 * 1 C 2 3 D F 4 5 G 6 8 9 10 H L 7 K E F 11 Q J Anschaulich: Auf dem Weg von A[i] zur Wurzel werden alle Elemente, deren Schlüssel größer als x (= der neue Eintrag in A[i]) ist, um eine Position (auf dem Weg) nach unten gezogen. Eintrag A[i] landet in der freigewordenen Position. Man kann dies auch effizienter (wie in StraigtInsertionSort) programmieren. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 86 Prozedur insert(x, d) (⇤ Einfügen eines neuen Eintrags in Priority-Queue ⇤) (⇤ Ausgangspunkt: Pegel k, A[1 . . k] ist Heap, k < m ⇤) (1) if k = m then Überlauf-Fehler“; ” (2) k++; (3) A[k] (x, d); (4) bubbleUp(k). Korrektheit: klar wegen Korrektheit von bubbleUp(k). Zeitaufwand: O(log(k)). FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 87 Wir können bubbleUp(i) sogar für eine etwas allgemeinere Operation verwenden (Korrektheitsbeweis gilt weiter): Wir ersetzen einen beliebigen Schlüssel im Heap (Position i) durch einen kleineren. (Dadurch ist A ein Aufwärts-Fast-Heap bzgl. Stelle i.) Wie zuvor: Mit bubbleUp(i) kann die Heapeigenschaft wieder hergestellt werden. Prozedur decreaseKey(x, i) (⇤ (Erniedrigen des Schlüssels an Arrayposition i auf x) ⇤) (1) if A[i].key < x then Fehlerbehandlung; (2) A[i].key x; (3) bubbleUp(i). (⇤ Zeitaufwand: O(log(i)) ⇤) FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 88 Satz 3.3.4 Der Datentyp “Priority Queue” kann mit Hilfe eines Heaps implementiert werden. Dabei erfordern empty und isempty (und das Ermitteln des kleinsten Eintrags) konstante Zeit und insert, extractMin und decreaseKey benötigen jeweils Zeit O(log n). Dabei ist n jeweils der aktuelle Pegelstand, also die Anzahl der Einträge in der Priority Queue. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 89 Technisches Problem: Wie soll man der Datenstruktur mitteilen“, welches Objekt gemeint ” ist, wenn decreaseKey auf einen Eintrag angewendet werden soll? Bei (binärem) Heap: Positionen der Einträge im Array ändern sich die ganze Zeit, durch die durch insert und extractMin verursachten Verschiebungen im Array. Technisch unsauber (widerspricht dem Prinzip der Kapselung einer Datenstruktur): dem Benutzer stets mitteilen, an welcher Stelle im Array ein Eintrag sitzt. Wir werden einen Lösungsansatz in einer Übungsaufgabe besprechen. FG KTuEA, TU Ilmenau Effiziente Algorithmen – Sommersemester 2012 90