Effiziente Algorithmen - Greedy-Algorithmen

Werbung
Effiziente Algorithmen
Greedy-Algorithmen
Vorlesender: Martin Aumüller
(nach Folien von Prof. Martin Dietzfelbinger)
Mai 2012
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
1
Kapitel 3: Greedy-Algorithmen
Greedy∗ -Algorithmen sind anwendbar bei Konstruktionsaufgaben zum
Finden einer optimalen Struktur.
Sie finden eine Lösung, die sie schrittweise aufbauen, ohne zurückzusetzen.
Es werden dabei nicht mehr Teillösungen konstruiert als unbedingt nötig.
∗
greedy (engl.): gierig.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
2
3.1 Zwei Beispiele
Beispiel 1: Hörsaalbelegung
Gegeben: Veranstaltungsort (Hörsaal), Zeitspanne [T0 , T1 ) und eine
Menge von n Aktionen (Vorlesungen oder ähnliches), durch Start- und
Endzeit spezifiziert:
[si , fi ), für 1 ≤ i ≤ n.
Gesucht: Belegung des Hörsaals, die möglichst viele Ereignisse mit
disjunkten Zeitspannen stattfinden lässt.
Nenne eine Menge A ⊆ {1, . . . , n} zulässig, wenn alle [si , fi ), i ∈ A,
disjunkt sind.
Aufgabe, formal: Finde zulässige Menge A mit |A| so groß wie möglich.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
3
T1
T0
Eingabe: Aktionen mit Beginn und Ende
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
4
T1
T0
Zulässige Lösung mit 4 Aktionen. Optimal?
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
5
T1
T0
Zulässige Lösung mit 5 Aktionen. Optimal?
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
6
Ansätze, die nicht funktionieren:
Zuerst kurze Ereignisse planen
T1
T0
Immer ein Ereignis mit möglichst früher Anfangszeit wählen
T1
T0
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
7
Trick: Bearbeite Ereignisse nach wachsenden Schlusszeiten. O.B.d.A.:
Veranstaltungen nach Schlusszeiten aufsteigend sortiert (Zeitaufwand
O(n log n)), also:
f1 ≤ f2 ≤ · · · ≤ fn .
4
9
7
3
1
5
2
11
12
8
6
10
T1
T0
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
8
Wiederhole: Wähle die wählbare Aktion mit der kleinsten Schlusszeit
und füge sie zum Belegungsplan hinzu.
Eine Aktion ist wählbar, wenn ihre Startzeit mindestens so groß wie die
Schlusszeit der letzten schon geplanten Veranstaltung ist.
T1
T0
Ausgabe von GS: Zulässige Lösung.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
9
Algorithmus Greedy Scheduling (GS)
Eingabe: [T0 , T1 ), [s1 , f1 ), . . . , [sn , fn ), nichtleere
reelle Intervalle, [si , fi ) ⊆ [T0 , T1 )
Ausgabe: Maximal großes A ⊆ {1, . . . , n} mit
[si , fi ), i ∈ A disjunkt
(1) Sortiere Intervalle gemäß f1 , . . . , fn aufsteigend;
(2) A ← {1};
(3) flast ← f1 ;
(4) for i from 2 to n do
(5)
if si ≥ flast then (∗ [si , fi ) wählbar ∗)
(6)
A ← A ∪ {i};
(7)
flast ← fi ;
(8) return A
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
10
Satz 3.1.1
Der Algorithmus Greedy Scheduling (GS) hat lineare Laufzeit (bis auf die
Sortierkosten von O(n log n)) und löst das Hörsaalplanungsproblem
optimal.
Beweis: Laufzeit: Sortieren kostet Zeit O(n log n); der restliche
Algorithmus hat offensichtlich Laufzeit O(n).
Korrektheit: Wir behaupten: Algorithmus GS liefert auf Inputs mit Größe
n eine optimale Lösung, und beweisen dies durch vollständige Induktion.
Der Fall n = 1 ist trivial.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
11
Sei nun n > 1.
I.V.: GS liefert für Inputs der Länge n0 < n eine optimale Lösung.
Ind.-Schritt: Sei B ⊆ {1, . . . , n} eine optimale Lösung, |B| = r . (Im
Beispiel: r = 5.)
T1
T0
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
12
1. Beobachtung:
Es gibt eine Lösung B 0 , die mit dem Intervall [s1 , f1 ) (dem ersten Schritt
des Greedy-Algorithmus) startet und ebenfalls r Ereignisse hat. (Also ist
B 0 auch optimal.)
T1
T0
Setze B 0 := (B − {min(B)}) ∪ {1}.
Intervalle aufsteigend sortiert ⇒ f1 ≤ fmin(B) .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
13
2. Beobachtung:
Die Menge B − {min(B)} löst das Teilproblem
(∗)
[f1 , T1 ), {[si , fi ) | si ≥ f1 }
optimal, das heißt:
in [f1 , T1 ) können maximal r − 1 Ereignisse untergebracht werden.
Wieso? Sonst würden wir [s1 , f1 ) mit einer besseren Lösung für (∗) zu einer
besseren Lösung für das Gesamtproblem kombinieren: Widerspruch zur
Optimalität von B.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
14
3. Beobachtung:
Algorithmus GS auf Eingabe {[si , fi ) | 1 ≤ i ≤ n} hat ab Iteration i = 2
genau dasselbe Verhalten wie wenn man GS auf [f1 , T1 ),
{[si , fi ) | si ≥ f1 } starten würde.
Nach I.V. liefert also dieser Teil des Algorithmus eine optimale Lösung mit
r − 1 Ereignissen für (∗).
Also liefert Greedy Scheduling insgesamt eine optimale Lösung der Größe
r.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
15
Beispiel 2: Fraktionales ( teilbares“) Rucksackproblem
”
Veranschaulichung: Ein Dieb stiehlt Säckchen mit Edelmetallkrümeln, die
beliebig teilbar sind. Der Wert pro Volumeneinheit ist unterschiedlich für
unterschiedliche Materialien. Was soll er in seinen Rucksack mit Volumen b
packen, um den Wert zu maximieren?
Gegeben: n Objekte mit positiven Volumina a1 , . . . , an und positiven
Nutzenwerten c1 , . . . , cn ,
sowie eine Volumenschranke b.
Gesucht: Vektor (λ1 , . . . , λn ) ∈ [0, 1]n , so dass
λ1 a1 + . . . + λn an ≤ b ( zulässig“)
”
und
λ1 c1 + . . . + λn cn maximal.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
16
Beim 0-1-Rucksackproblem“ werden nur 0-1-Vektoren mit λi ∈ {0, 1}
”
zugelassen.
Mitteilung (im Vorgriff auf BuK, 5. Sem.): Die {0, 1}-Version ist NP-vollständig,
besitzt also wahrscheinlich keinen effizienten Algorithmus.
Das fraktionale Rucksackproblem ist mit einem Greedy-Algorithmus in Zeit
O(n log n) lösbar.
Kern der Lösungsidee: Berechne Nutzendichte“
”
di = ci /ai , 1 ≤ i ≤ n,
und sortiere die Objekte gemäß di fallend.
Nehme von vorne beginnend möglichst viele ganze Objekte, bis schließlich
das letzte Objekt teilweise genommen wird, so dass die Gewichtsschranke
vollständig ausgenutzt wird.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
17
c1
c2
c
3
cn
a1
a2
a3
b
an
Input, sortiert nach Nutzendichte di = ci /ai .
Höhe von Kasten Nummer i ist di = ci /ai .
Breite ist ai .
Fläche ist ci .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
18
Algorithmus Greedy Fractional Knapsack (GFKS)
(1) for i from 1 to n do
(2)
di ← ci /ai ;
(3)
λi ← 0;
(4) Sortiere Objekte gemäß di fallend;
(5) i ← 0;
(6) r ← b; (* Inhalt r ist das verfügbare Rest-Volumen *)
(7) while r > 0 do
(8)
i++;
(9)
if ai ≤ r
(10)
then λi ← 1; r ← r − ai ;
(11)
else λi ← r/ai ;
(12)
r ← 0;
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
19
c1
c2
c
3
cn
a1
a2
a3
b
an
Vom Greedy-Algorithmus gelieferte Lösung.
Gesamtnutzen: grün.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
20
c1
c2
c
3
cn
a1
a2
a3
b
an
Zulässige Lösung, nicht optimal. Gesamtnutzen: blau.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
20
c1
c2
c
3
cn
a1
a2
a3
c1
c2
c
b
an
3
cn
a1
a2
a3
b
an
Anschaulich: Greedy-Lösung nie schlechter als beliebige Lösung.
Gleich: Vollständiger Beweis.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
21
Satz 3.1.2
Der Algorithmus GFKS ist korrekt (liefert eine zulässige Lösung mit
maximalem Gesamtnutzen) und hat Laufzeit O(n log n).
Beweis: Sei x = (a1 , . . . , an , c1 , . . . , cn , b) die Eingabe.
P
O.B.d.A.: i ai > b (sonst ist (λ1 , . . . , λn ) = (1, . . . , 1) optimal und wird
von GFKS gefunden).
Sei (λ1 , . . . , λn ) ∈ [0, 1]n die Ausgabe des Algorithmus.
Laufzeit: klar.
Korrektheit: Zulässigkeit klar. Zu zeigen: Optimalität.
Sei (λ01 , . . . , λ0n ) ∈ [0, 1]n eine optimale Lösung.
P 0
Offensichtlich: i λi ai = b (sonst verbesserbar).
P
P 0
Durch Induktion über n zeigen wir: i λi ci = i λi ci .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
22
I.A.: n = 1. Dann liefert der Algorithmus offensichtlich die optimale
Lösung: Packe genau den Bruchteil λ1 = b/a1 , der in den Rucksack passt.
Ind.-Schritt: n > 1.
1. Fall: a1 ≥ b.
GFKS wählt λ1 = b/a1 , und das ist optimal: Weil d1 ≥ di für alle i, gilt:
P 0
P 0
P 0
λ1 c1 = bd1 = ( i λi ai )d1 ≥ i λi ai di = i λi ci .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
23
(Ind.-Schritt:)
2. Fall: a1 < b.
Behauptung: Wir können o.B.d.A. λ01 = 1 annehmen.
( Der erste Schritt des Greedy-Algorithmus ist nicht falsch.“)
”
P
0
Denn: Wenn λ1 < 1, kann man wegen 1≤i≤n λ0i ai = b
Werte λ001 = 1, 0 ≤ λ002 ≤ λ02 , . . . , 0 ≤ λ00n ≤ λ0n finden, so dass
P
0
(1 − λ1 )a1 = 2≤i≤n (λ0i − λ00i )ai .
(∗)
(Verringere Gewicht bei späteren“ Objekten zugunsten von Objekt 1.)
”
Dann ist (λ001 , . . . , λ00n ) zulässig und
X
X
X
λ00i ci =
λ0i ci + (1 − λ01 )a1 d1 −
(λ0i − λ00i )ai di .
i
i
2≤i≤n
Aus (∗) und
d1 ≥ di folgt, dass die letzte Klammer nichtnegativ ist, also
P
P
00 c ≥
0 c gilt. (Sogar Gleichheit.)
λ
λ
i
i i
i i i
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
24
Wissen nun: 1 = λ1 = λ01 .
Wir wenden die Induktionsvoraussetzung auf den modifizierten Input
x − = (a2 , . . . , an , c2 , . . . , cn , b − a1 ) an.
GFKS läuft in Schleifendurchläufen 2 bis n ebenso wie GFKS auf x − ,
liefert also für x − eine optimale Lösung: (λ2 , . . . , λn ).
Klar: (λ02 , . . . , λ0n ) muss für x − optimal sein.
(Wenn (λ002 , . . . , λ00n ) besser wäre, dann wäre (1, λ002 , . . . , λ00n ) eine bessere Lösung
für x als (1, λ02 , . . . , λ0n ). Das kann nicht sein.)
Nach I.V. gilt
P
2≤i≤n λi ci =
0c .
λ
2≤i≤n i i
P
Also haben auch die Lösungen (1, λ02 , . . . , λ0n ) und (1, λ2 , . . . , λn ) für x
denselben Nutzenwert.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
25
An den Beispielen zu beobachtende Charakteristika der Greedy-Methode:
1
2
3
Der erste Schritt der Greedy-Lösung ist nicht falsch. Es gibt eine
optimale Lösung, die als Fortsetzung des ersten Schrittes
konstruiert werden kann.
Prinzip der optimalen Substruktur“: Entfernt man aus einer
”
optimalen Lösung die erste(n) Komponente(n), so bleibt als Rest die
optimale Lösung für einen Teil oder Rest des Inputs.
Der Korrektheitsbeweis wird mit vollständiger Induktion geführt.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
26
3.2 Huffman-Codes
Gegeben: Alphabet Σ und Wahrscheinlichkeiten“
P
”
p(a) ∈ [0, 1] für jeden Buchstaben a ∈ Σ. Also:
p(a) = 1.
a∈Σ
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 ∈ Σ ist binärer Code“ c(a) ∈ {0, 1}+ zugeordnet,
”
mit Eigenschaft Präfixfreiheit:
Für a, b ∈ Σ, 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 ) ∈ {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 ∈ Σ 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 ∈ Σ 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),
a∈Σ
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
0
1
0
B
1
1
I
0
A
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 ∈ Σ, 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 ∈ Σ − {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 ∈ Σ − {a, a0 }
gilt, haben wir
P
p(b) ≥ p(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. Huffman (1925–1999), am. Informatiker.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
41
Huffman-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
”
0
aufgefasst mit p(b) = p(a) + p(a ).
Neues Alphabet: Σ0 := (Σ − {a, a0 }) ∪ {b}; neue Verteilung:
p(d)
falls d 6= b
p 0 (d) :=
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 )a∈Σ ).
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 ∈ Σ 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 − 1, m = |Σ|.
Dabei repräsentieren
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“.
”
Das Array pred[1..2m − 1] speichert die Vorgänger der Knoten im
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 ∈ {1, . . . , 2m − 1};
Schlüssel: die Gewichte p[a].
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
48
Algorithmus Huffman(p[1..m])
Eingabe: Gewichtsvektor p[1..m]
Ausgabe: Implizite Darstellung eines Huffman-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 − 1] baut man den
Huffman-Baum wie folgt:
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
1
0
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 Huffman 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
pi · log(1/pi ).
i=1
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 = 23 .
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
i=1 qi
pi log(1/qi ) ≥
i=1
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
−1
=
pi ln
pi
pi
=
i=1
i=1
m
X
m
X
i=1
i=1
(qi − pi ) =
qi −
m
X
pi ≤ 0.
i=1
(∗): Es gilt ln(x) ≤ x − 1, für alle x ∈ R. (Summanden für i mit pi = qi = 0
lässt man einfach weg.)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
56
Satz 3.2.9 (Ungleichung von Kraft/McMillan)
Es seien m ≥ 1, l1 , . . . , lm ∈ N. Dann gilt: Es gibt einen Präfixcode mit
Codewortlängen l1 , . . . , lm genau dann wenn
m
X
2−li ≤ 1.
i=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.)
1 , . . . , lm so an, dass
Pm die l1l−l
l1 ≥ l2 ≥ · · · ≥ lm gilt. Nach Vor.: i=2 2 i < 2l1 ;
die Summe ist durch 2l1 −l2 teilbar.
Pm l1 −l
Also: i=2 2 i ≤ 2l1 − 2l1 −l2 .
Pm l1 −l
l
−l
1
2
Daraus: 2 · 2
+ i=3 2 i ≤ 2l1 , d. h.
Pm −l
−(l
−1)
2
2
+ i=3 2 i ≤ 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 (Huffman versus Entropie)
P
Ist p : Σ → [0, 1] mit a∈Σ 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 · li =
m
X
pi · log(1/2−li ) ≥ H(p1 , . . . , pm ).
i=1
Effiziente Algorithmen – Sommersemester 2012
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
pi · (log(1/pi ) + 1)
i=1
= H(p1 , . . . , pm ) +
m
X
pi
i=1
= H(p1 , . . . , pm ) + 1.
Bemerkung: Es gibt bessere Kodierungsverfahren als das von Huffman
(z.B. arithmetische Kodierung“; diese vermeiden den Verlust von bis zu
”
einem Bit pro Buchstabe), aber Huffman-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 Huffman-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
1
0
1
1
0
0
11
10
9
7
6
0
1
1
0
5
8
0
3
1
4
0
1
0
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
11
12
* *
1
B
2
3
E
F
4
5
E
G
8
7
H
F
9 10
H
FG KTuEA, TU Ilmenau
6
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
H
F
H
9
10
I
G
11
12
* *
1
B
2
3
E
F
4
5
E
G
8
7
H
F
9 10
H
FG KTuEA, TU Ilmenau
6
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
H
F
H
11
12
9
10
I
* * *
1
G
2
3
E
F
4
5
E
G
8
6
7
H
F
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
G
<
2
3
E
5
4
E
G
8
F
<
6
7
H
F
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
7
H
F
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
7
H
F
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] ← x. Damit wird A ein Abwärts-Fast-Heap bezüglich
Stelle m.
FG KTuEA, TU Ilmenau
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. Stell
(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
Herunterladen