ADS – Vorlesung Prof. Dr. Wolfram Conen
ADS – Vorlesung Prof. Dr. Wolfram Conen
ADS – Vorlesung Prof. Dr. Wolfram Conen
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
<strong>ADS</strong> <strong>–</strong> <strong>Vorlesung</strong><br />
<strong>Prof</strong>. <strong>Dr</strong>. <strong>Wolfram</strong> <strong>Conen</strong><br />
Inhalte:<br />
- Greedy-Algorithmen<br />
- Matroide<br />
- Greedy-Beispiel Huffman-Codierung<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 1
Greedy-Optimierung<br />
• Nehmen Sie an, sie brechen nachts in eine Villa ein und<br />
wollen<br />
soviel an Werten mitnehmen, wie sie tragen können<br />
so schnell wie möglich wieder raus<br />
• Es gelten folgende Nebenbedingungen<br />
Die Bewohner sind verreist, wir haben im Prinzip<br />
„reichlich Zeit“<br />
Wir kennen den Wert aller Wertgegenstände<br />
Wir können nicht alles mitnehmen, nur ein bestimmtes<br />
Gewicht (sonst wäre „Alles mitnehmen“ optimal)<br />
Die Gewichte kennen wir auch (Handwaage ist immer<br />
dabei!)<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 2
Greedy-Optimierung<br />
• Dieses Problem ist (in krimineller Verkleidung) das<br />
klassische Knapsack Problem (Rucksack-Problem), formal:<br />
Gegeben ist eine Menge M = {1,..,n} von Gegenständen<br />
mit den Werten v i und den Gewichten w i . Gesucht ist M‘<br />
⊆ M mit<br />
∑ i ∈ M‘ w i ≤ w max<br />
<br />
d.h. die Summe der Gewichte der Gegenstände in M‘<br />
überschreitet ein vorgegebenes Maximalgewicht nicht.<br />
Der Wert der Gegenstände soll so groß wie möglich sein:<br />
Max M‘ ⊆ M ← ∑ i ∈ M‘ v i<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 3
Greedy-Optimierung<br />
• Lösungsidee:<br />
<br />
<br />
Gegenstände nach dem Verhältnis von Wert zu Gewicht<br />
(„Wert pro Kilo“) absteigend sortieren (der mit dem<br />
größten Wert pro Gewichtseinheit steht also vorne), z.B.<br />
in einer PQueue<br />
Dann nehmen wir solange Gegenstände aus der PQueue,<br />
wie wir sie noch tragen können<br />
• Diese Strategie ist „gierig“! (English: Greedy) <strong>–</strong> man nimmt<br />
das „Vielversprechendste“ zuerst usw.<br />
• Ist das immer sinnvoll?<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 4
Greedy-Optimierung<br />
• Natürlich nicht (würde ich sonst so fragen...)<br />
• Wir können maximal 50kg tragen, es gibt die folgenden<br />
Gegenstände<br />
<br />
<br />
<br />
Lehrbuch über Algorithmen und Datenstrukturen,<br />
Gewicht w 1<br />
= 10kg, Wert v 1<br />
= 60 Euro, d.h. 6 Euro pro kg<br />
Taschenrechner mit eingebautem Dijkstra,<br />
Gewicht w 2<br />
= 20kg, Wert v 2<br />
= 100 Euro, d.h. 5 Euro pro kg<br />
Allegorische Statue, die die ewige Schönheit von (bottom-up)<br />
Heapsort symbolisiert,<br />
Gewicht w 3<br />
= 30kg, Wert v 3<br />
= 120 Euro, d.h. 4 Euro pro kg<br />
• Was liefert die Greedy-Strategie als Resultat?<br />
• Wie sieht das optimale Resultat aus?<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 5
Greedy-Optimierung<br />
• Aber manchmal geht es auch...sogar immer, wenn die<br />
Problemstellung entsprechend ist.<br />
• Andere Gegenstände:<br />
Ein Haufen Schnipsel mit Klausurlösungen,<br />
Gewicht 10kg, Wert 60 Euro, 6 Euro pro kg<br />
Ein Haufen Goldstaub,<br />
<br />
Gewicht 20kg, Wert 100 Euro, 5 Euro pro kg<br />
Ein Haufen Silberstaub,<br />
Gewicht 30kg, Wert 120 Euro, 4 Euro pro kg<br />
• Jetzt können wir die Gegenstände beliebig teilen:<br />
• Also nehmen wir die Klausurschnipsel, den Goldstaub, und füllen<br />
unseren Rucksack mit Silberstaub auf (zu einem Gesamtwert von<br />
60+100+80 = 240 Euro)<br />
• Besser geht es natürlich nicht!<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 6
Greedy-Optimierung<br />
<br />
Probleme des ersten Typs (mit unteilbaren Gegenständen)<br />
heißen<br />
0-1 Knapsack Probleme<br />
<br />
Probleme des zweiten Typs (mit beliebig teilbaren<br />
Gegenständen) heißen<br />
Fractional Knapsack<br />
<br />
<br />
Fractional Knapsack Probleme lassen sich immer „greedy“<br />
lösen!<br />
0-1 Knapsacks nur ab und an „zufällig“!<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 7
Greedy-Optimierung<br />
<br />
Unser „kürzeste-Wege-Problem“ lässt sich auch „Greedy“<br />
lösen <strong>–</strong> und genau das tut der Dijkstra auch!<br />
<br />
Das gleiche gilt für das „Minimum Spanning Tree“-<br />
Problem, und auch Kruskal bzw. Prim lösen das Problem<br />
„greedy“<br />
<br />
Ein guter Hinweis darauf ist oft die Verwendung einer<br />
PQueue...<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 8
Arbeitsweise von Greedy-Algorithmen<br />
<br />
Wir haben folgendes zur Verfügung<br />
• Eine Menge von Kandidaten C, aus denen wir die Lösung<br />
konstruieren wollen (z.B. Kanten beim MST)<br />
• Eine Teilmenge S ⊆ C, die bereits ausgewählt wurde<br />
• Boole‘sche Funktion solution, die sagt, ob eine Menge von<br />
Kandidaten eine legale Lösung des Problems darstellt<br />
(unabhängig davon, ob die Lösung optimal ist)<br />
• Eine Testfunktion feasible, die sagt, ob eine Teillösung unter<br />
Umständen zu einer kompletten legalen Lösung erweitert<br />
werden kann<br />
• Eine Auswahlfunktion select, die den nächsten<br />
„vielversprechendsten“ Kandidaten liefert<br />
• Eine Zielfunktion value, die uns den Wert einer Lösung<br />
angibt<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 9
Arbeitsweise von Greedy-Algorithmen<br />
Function greedy(c)<br />
S ← ∅<br />
while not solution(S) and C ≠ ∅ do<br />
x ← select(C)<br />
C ← C - {x}<br />
if feasible(S ∪ {x}) then<br />
S ← S ∪ {x}<br />
if solution(S) then<br />
return S<br />
else return „There_is_no_solution“<br />
Mit der Funktion value(S) kann man am Ende den Wert der<br />
gefundenen Lösung bestimmen (falls es eine gab...)<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 10
Welche Probleme sind „greedy“ lösbar?<br />
• Wenn das Problem sich als Matroid modellieren läßt, dann kann<br />
man es „Greedy“ lösen!<br />
• Aber was ist ein Matroid? [s. Literaturempfehlungen]<br />
• Es gilt sogar: ein Problem lässt sich genau dann „greedy“ lösen<br />
(allgemein für jede Gewichtungsfunktion der Elemente in die<br />
positiven reellen Zahlen), wenn es eine Matroid-Struktur aufweist<br />
(s. auch „Das Geheimnis des kürzesten Weges“, Literaturhinweis<br />
zu Gin1b)<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 11
Noch ein wichtiges Problem, das man „greedy“ angehen kann:<br />
Datenkompression<br />
• Ist ihre Festplatte ständig zu klein?<br />
• ...oder ihre Internet-Anbindung zu langsam?<br />
• Dann ist Datenkompression ein Thema für Sie!<br />
• Ziele:<br />
<br />
<br />
möglichst platzsparende Datenspeicherung bzw.<br />
Übertragung<br />
entpacken möglich, ohne Fehler in den Daten zu<br />
hinterlassen<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 12
Datenkompression<br />
• Zwei Teilprozesse:<br />
<br />
<br />
Kompression: Ein Prozess, der Daten in einer komprimierte,<br />
also „kleinere“, Form überführt<br />
Expansion: Ein Prozess, der aus der komprimierten Form die<br />
Ausgangsdaten rekonstruiert<br />
• Beispielfälle für „Original“-Daten:<br />
<br />
<br />
Ein Text aus 256.000 Zeichen, jedes der 256 möglichen<br />
(ASCII-)Zeichen tritt genau 1000-mal auf<br />
Der Text besteht aus 256.000-mal dem Zeichen ‚a‘<br />
• Beide Texte nehmen 256.000*8 = 2.048.000 Bit Plattenplatz ein.<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 13
Datenkompression<br />
<br />
Betrachten wir einmal die Wahrscheinlichkeit, dass an<br />
einer bestimmten Stelle der Files ein bestimmtes Zeichen<br />
auftaucht:<br />
• Im ersten File ist die Wahrscheinlichkeit für das<br />
Auftauchen jedes Zeichens an der ersten betrachteten<br />
Position gleich, nämlich 1/256, z.B. für ‚a‘<br />
An später betrachteten Positionen verschieben sich<br />
die Wahrscheinlichkeiten abhängig von den vorher<br />
bereits betrachteten Zeichen (wenn es z.B. gar kein<br />
a mehr gibt), aber „ungefähr“ bleibt es bei der<br />
Wahrscheinlichkeit 1/256 auch an den anderen<br />
Positionen<br />
• Im zweiten Fall ist die Wahrscheinlichkeit für das<br />
Auftauchen von ‚a‘ an jeder Position 1.<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 14
Datenkompression<br />
• Im ersten Fall besteht eine hohe Unsicherheit darüber,<br />
welches Zeichen an der betrachteten Position auftritt.<br />
• Um zwischen den verschiedenen möglichen „Ereignissen“<br />
(das Auftreten eines bestimmten der 256 Zeichen) zu<br />
unterscheiden, müssen wir den aufgetretenen Fall genau<br />
angeben<br />
• Um 256 Ereignisse zu unterscheiden, brauchen wir<br />
log 2 256 Bit, also 8 ;-)<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 15
Datenkompression<br />
• Im zweiten Fall wissen wir mit Sicherheit, welches<br />
Zeichen an der betrachteten Position auftritt (nämlich „a“)<br />
• Um zwischen den verschiedenen möglichen „Ereignissen“ zu<br />
unterscheiden, brauchen wir eigentlich gar keine<br />
Information<br />
• Wir müssen nur wissen, wieviele „a“ auftreten<br />
• Insgesamt können wir das File durch „Jetzt kommen<br />
256.000 ‚a‘“ vollständig beschreiben (also mit ungefähr 150<br />
Bit)<br />
• Intuitiv ist klar, dass man im ersten Fall nicht sehr viel<br />
komprimieren kann, im zweiten aber schon!<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 16
Datenkompression<br />
• Zum Komprimieren muss man den „Eingabetext“ sinnvoll<br />
kodieren.<br />
• Elementare Idee:<br />
Zeichen, die häufig vorkommen, erhalten<br />
vergleichsweise kurze Codes<br />
• Das nennt man „variable Kodierung“<br />
• Sie soll den folgenden Ausdruck minimieren<br />
∑ l(c i )*f(c i ), 1 ≤ i ≤ n<br />
<br />
<br />
<br />
n = Anzahl der Zeichen<br />
l(c i ) = Länge der Codierung des Zeichens c i<br />
f(c i ) = Häufigkeit von c i im Text<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 17
Datenkompression<br />
a b c d e f<br />
Häufigkeit 45 13 12 16 9 5<br />
ASCII 01100001 01100010 01100011 01100100 01100101 01100110<br />
Code 1 0 011 100 101 0011 1100<br />
Code 2 0 101 100 111 1101 1100<br />
Was ist schlecht am Code 1? Probieren Sie mal, 001100101 zu expandieren!<br />
Man könnte natürlich die Länge des nächsten Codes abspeichern, aber so<br />
recht macht das keinen Sinn...bei Code 2 geht das auch so!<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 18
Datenkompression<br />
a b c d e f<br />
Häufigkeit 45 13 12 16 9 5<br />
Code 1 0 011 100 101 0011 1100<br />
Code 2 0 101 100 111 1101 1100<br />
• Dekodieren des Wortes 001100101 mit Code 1:<br />
• Stellen sie sich vor, sie lesen die Eingabe Zeichen für Zeichen<br />
von links nach rechts und müssen Entscheidungen treffen...<br />
0<br />
0<br />
f f<br />
1 f<br />
a,b,c,d,e,f<br />
1<br />
0<br />
0<br />
c,d,f<br />
b,e<br />
a<br />
1<br />
0<br />
0<br />
c,d<br />
b<br />
e<br />
1<br />
1<br />
0<br />
b<br />
d<br />
c<br />
1 1 e e<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 19
Datenkompression<br />
• Dekodieren des Wortes 001100101 mit Code 1:<br />
• Stellen sie sich vor, sie lesen die Eingabe Zeichen für Zeichen<br />
von links nach rechts<br />
• Zu Beginn sind noch alle Codes möglich<br />
• Aber bereits beim ersten Knoten können sie nicht exakt<br />
entscheiden, ob die 0 für ein a oder für b oder e steht...<br />
1<br />
f<br />
0<br />
f<br />
0<br />
f<br />
a,b,c,d,e,f<br />
0?<br />
1<br />
0<br />
0<br />
c,d,f<br />
b,e<br />
a<br />
0<br />
1<br />
0<br />
c,d<br />
b<br />
e<br />
1<br />
1<br />
0<br />
b<br />
d<br />
c<br />
1 1 e e<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 20
Datenkompression<br />
a b c d e f<br />
Häufigkeit 45 13 12 16 9 5<br />
Code 2 0 101 100 111 1101 1100<br />
• Dekodieren des Wortes 001100101 mit Code 2:<br />
1<br />
d<br />
1<br />
a,b,c,d,e,f<br />
0<br />
d,e,f<br />
1 0<br />
e,f<br />
b,c,d,e,f<br />
0<br />
b,c<br />
1 b<br />
0 c<br />
a<br />
1 e<br />
0 f<br />
aafb<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 21
Datenkompression<br />
• Definition: Eine Codierung heißt präfix-frei, wenn kein<br />
Code Anfangsstück (=Präfix) eines anderen Codes ist<br />
• Einen solchen Code kann man offensichtlich mit Hilfe von<br />
binären Entscheidungsbäumen beschreiben:<br />
<br />
<br />
<br />
<br />
Die Blätter sind die zu kodierenden Zeichen<br />
Die Codes ergeben sich aus den Wegen im Baum, die<br />
von der Wurzel zu den Zeichen führen<br />
Eine Zweig nach links steht für ein 0, ein Zweig nach<br />
rechts für eine 1<br />
Eine solche Kodierung ist immer präfix-frei, weil zu jeder<br />
Zeit klar ist, wie es im Baum weitergeht (es gibt keine<br />
Wahlmöglichkeiten)<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 22
Präfix-freie Codes<br />
• Einige mögliche binäre Codebäume für die Zeichen a,b,c<br />
a,b,c<br />
a,b,c<br />
a,b,c<br />
•„unschlau“<br />
a<br />
b,c<br />
b<br />
a,c<br />
a<br />
b,c<br />
b<br />
c<br />
a<br />
c<br />
b<br />
c<br />
c<br />
a,b,c<br />
a,b<br />
a b<br />
•„normal“<br />
• Welcher Baum ist nun optimal?<br />
• Der rechte Baum auf keinen Fall!<br />
• Bei den anderen drei Bäumen hängt<br />
das von den Auftretenshäufigkeiten der<br />
Buchstaben ab<br />
c<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 23
Präfix-freie Codes<br />
• Einige mögliche binäre Codebäume für die Zeichen a,b,c<br />
(1) a,b,c (2) a,b,c (3)<br />
a,b,c<br />
a<br />
b,c<br />
b<br />
a,c<br />
c<br />
a,b<br />
b<br />
c<br />
a<br />
c<br />
a<br />
b<br />
• Welcher Baum ist nun optimal?<br />
a/1,b/1,<br />
c/1<br />
Baum 1 1+2+2=<br />
5<br />
Baum 2 2+1+2=<br />
5<br />
Baum 3 2+2+1=<br />
5<br />
a/2,b/1,<br />
c/1<br />
2*1+2+2=<br />
6<br />
2*2+1+2=<br />
7<br />
2*2+2+1=<br />
7<br />
a/3,b/1,<br />
c/1<br />
3*1+2+2<br />
=7<br />
3*2+1+2<br />
=9<br />
3*2+2+1<br />
=9<br />
a/3,b/2,<br />
c/1<br />
3*1+2*2+2<br />
=9<br />
3*2+2*1+2<br />
=10<br />
3*2+2*2+1<br />
=11<br />
a/1,b/2,c/2<br />
1*1+2*2+2*2<br />
=9<br />
1*2+2*1+2*2<br />
=8<br />
1*2+2*2+2*1<br />
=8<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 24
Präfix-freie Codes<br />
• Zwei mögliche binäre Codebäume für die Zeichen a,b,c,d,e,f<br />
„balanciert“<br />
a,b,c,d,e,f<br />
a,b,c,d,e,f<br />
„kettenförmig“<br />
a,b,c<br />
d,e,f<br />
a<br />
b,c,d,e,f<br />
a<br />
b,c<br />
d<br />
e,f<br />
b<br />
c,d,e,f<br />
b<br />
c<br />
e<br />
f<br />
c<br />
d,e,f<br />
• Welcher Baum ist nun optimal?<br />
• Alle Buchstaben gleichhäufig, z.B. einmal:<br />
2+3+3+2+3+3 = 16 (links),<br />
1+2+3+4+5+5 = 20 (rechts)<br />
• Häufigkeiten: a/6,b/5,c/4,d/3,e/2,f/1<br />
6*2+5*3+4*3+3*2+2*3+1*3=54 (links),<br />
6*1+5*2+4*3+3*4+2*5+1*5=55 (rechts)<br />
d<br />
e<br />
e,f<br />
f<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 25
Präfix-freie Codes<br />
• Zwei mögliche binäre Codebäume für die Zeichen a,b,c,d,e,f<br />
„balanciert“<br />
a,b,c,d,e,f<br />
a,b,c,d,e,f<br />
„kettenförmig“<br />
a,b,c<br />
d,e,f<br />
a<br />
b,c,d,e,f<br />
a<br />
b,c<br />
d<br />
e,f<br />
b<br />
c,d,e,f<br />
b<br />
c<br />
e<br />
f<br />
c<br />
d,e,f<br />
• Welcher Baum ist nun optimal?<br />
• Häufigkeiten: a/12,b/10,c/8,d/6,e/4,f/2<br />
12*2+10*3+8*3+6*2+4*3+2*3=108 (links),<br />
12*1+10*2+8*3+6*4+4*5+2*5=110 (rechts)<br />
• Häufigkeiten: a/50,b/30,c/12,d/6,e/4,f/2<br />
50*2+30*3+12*3+6*2+4*3+2*3=256 (links),<br />
50*1+30*2+12*3+6*4+4*5+2*5=200 (rechts)<br />
d<br />
e<br />
e,f<br />
f<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 26
Präfix-freie Codes<br />
• Zwei mögliche binäre Codebäume für die Zeichen a,b,c,d,e,f<br />
„balanciert“<br />
a,b,c,d,e,f<br />
104 104<br />
a,b,c,d,e,f<br />
„kettenförmig“<br />
a<br />
a,b,c<br />
b<br />
b,c<br />
c<br />
d,e,f<br />
• Warum ist der rechte Baum soviel besser bei der<br />
letzten Verteilung?<br />
• Eine gleichmäßige Aufteilung führt zu einer<br />
Minimierung der gewichteten Höhen<br />
(=Codelänge * Vorkommen) der Knoten!<br />
a<br />
b,c,d,e,f<br />
92 12<br />
54<br />
50<br />
42<br />
d<br />
6<br />
e<br />
e,f<br />
30 12 4 2<br />
f<br />
6<br />
50<br />
b<br />
30<br />
c<br />
12<br />
d<br />
6<br />
c,d,e,f<br />
d,e,f<br />
e<br />
e,f<br />
24<br />
f<br />
4 2<br />
12<br />
6<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 27
Präfix-freie Codes<br />
• Geht das noch besser? Hm... probieren wir mal...<br />
104<br />
a,b,c,d,e,f<br />
104<br />
a,b,c,d,e,f<br />
a,f<br />
b,c,d,e<br />
a f b c,d,e<br />
50 2 30<br />
30<br />
c d,e<br />
12<br />
12<br />
6 4 6<br />
• Links von oben „lokal optimal“ aufgeteilt<br />
• Chiffrelänge links: 244, rechts: 200! Aua!<br />
a<br />
50<br />
b<br />
b,c,d,e,f<br />
52 52 54<br />
d e<br />
d<br />
c<br />
c,d,e,f<br />
22 24<br />
d,e,f<br />
10 12<br />
e,f<br />
e f<br />
4 2<br />
6<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 28
Datenkompression<br />
• Unsere „neue“ Aufgabe ist also:<br />
<br />
<br />
Gegeben ist eine Datei mit zu komprimierenden Daten<br />
Man konstruiere einen binären Entscheidungsbaum, der<br />
zu einer optimalen präfix-freien Codierung des<br />
Dateiinhalts führt.<br />
• Das Problem ist das Finden der besten Struktur des<br />
Baumes!<br />
• <strong>Dr</strong>ei Ideen folgen...<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 29
Datenkompression<br />
• Idee 1 („Brute-Force“, „vollständige Enumeration“):<br />
Wir bauen alle möglichen Binärbaumstrukturen ohne überflüssige<br />
Knoten (also keine Bäume vom Typ „unschlau“)<br />
Wir bestimmen die Chiffrelängen all dieser Bäume<br />
..und wählen dann den besten Baum aus (also den bzw, einen<br />
derjenigen mit der kürzesten Chiffrelänge)<br />
• Problem hier:<br />
Es gibt „fürchterlich viele“ mögliche Baumstrukturen!<br />
Wieviele können sie für 3 Knoten bauen?<br />
Wieviele für 4 Knoten, wieviele für n Knoten?<br />
• Diese Lösungsidee ist VIEL ZU TEUER!<br />
• Aber: sie ist optimal, d.h. sie wählt sicher den besten Baum aus!<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 30
Datenkompression<br />
• Idee 2 (Shannon-Fano):<br />
Wir ordnen die Buchstaben nach Häufigkeit absteigend in einer Liste an,<br />
z.B. [13, 9,4,3,2,2,1]<br />
Jetzt suchen wir einen sogenannten Splitpunkt, an dem wir die Liste in<br />
zwei Teile spalten. Diese Suche wird rekursiv für jede Teilliste wiederholt.<br />
Dieser Splitpunkt soll unter allen möglichen Splitpunkten so gewählt sein,<br />
dass die Summen der Häufigkeiten in den beiden Listenteilen möglichst<br />
nah beieinander liegen - mit anderen Worten: die Differenz zwischen den<br />
beiden Häufigkeitsummen soll minimiert werden<br />
Mögliche Aufteilungen:<br />
• [] (Gewicht 0), [13,9,4,3,2,2,1] (Gewicht 34), Differenz: 34<br />
• [13] (Gewicht 13), [9,4,3,2,2,1] (Gewicht 21), Differenz: 8<br />
• [13,9] (22) [4,3,2,2,1] (12), Differenz: 10<br />
• [13,9,4] (18) [3,2,2,1] (8) ...<br />
• Wenn man von links die Differenzen betrachtet, dann hat man einen<br />
Splitpunkt gefunden, sobald die Differenz wieder anwächst (die wird<br />
erst kleiner und steigt dann unweigerlich wieder)<br />
• Es kann Gleichstände zwischen max. zwei Splitpunkten geben, dann<br />
wählt man den linken/oberen oder rechten/unteren, je nach Vorgabe<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 31
Datenkompression<br />
• Idee 2 (Shannon-Fano):<br />
Vorteile:<br />
• Das Verfahren ist sehr günstig zu rechnen <strong>–</strong> die Knoten<br />
werden sortiert (n log n) und dann werden n-1 Splitpunkte<br />
bestimmt (schlimmstenfalls wird immer bis zur „Mitte“+1<br />
jeder Liste aufsummiert, O(log n * (n/2 + 1)), insgesamt<br />
also O(n log n).<br />
<br />
Nachteile:<br />
• Das Verfahren ist NICHT optimal!<br />
• Beispiel s. Übung<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 32
Datenkompression<br />
• Idee 3 (Huffman):<br />
Wir ordnen Knoten, die die einzelnen Buchstaben repräsentieren, nach Häufigkeit<br />
AUFSTEIGEND in einer Liste an, z.B. [a/1,b/2,c/2,d/3,e/4,f/9,g/13], Ablage am<br />
besten in einer PQueue<br />
Jetzt entnehmen wir die beiden vorderen Knoten und erzeugen daraus einen neuen<br />
Vaterknoten für die beiden entnommenen Knoten.<br />
Der neue Knoten erhält als Gewicht die Summen der Gewichte seiner beiden Kinder.<br />
Er wird in die PQueue eingefügt!<br />
Das wiederholen wir solange, bis nur noch ein Knoten in der PQueue ist.<br />
• Machen wir das einmal „abstrakt“ für die Liste oben:<br />
Initial: [a/1,b/2,c/2,d/3,e/4,f/9,g/13], a und b entnehmen, (a,b) mit Gewicht<br />
1+2=3 einstellen (kann vor oder auch hinter d/3 landen <strong>–</strong> das ist nicht festgelegt!)<br />
[c/2,d/3,(a,b)/3,e/4,f/9,g/13], c und d entnehmen, (c,d) mit Gewicht 5 einstellen<br />
[(a,b)/3,e/4,(c,d)/5,f/9,g/13], (a,b) und e entnehmen, (a,b,e) mit Gewicht 7<br />
einstellen<br />
[(c,d)/5,(a,b,e)/7,f/9,g/13], (c,d) und (a,b,e) entnehmen, (a,b,c,d,e) mit Gewicht<br />
12 einstellen<br />
[f/9,(a,b,c,d,e)/12,g/13], f und (a,b,c,d,e) entnehmen, (a,b,c,d,e,f) mit Gewicht 21<br />
einstellen<br />
[(a,b,c,d,e,f)/21,g/13], (a,b,c,d,e,f) und g entnehmen, (a,b,c,d,e,f,g) mit Gewicht<br />
34 einstellen<br />
Fertig!<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 33
Datenkompression<br />
• Idee 3 (Huffman):<br />
• Zur Erinnerung hier nochmal die Operationen auf der PQueue:<br />
a,b raus -> (a,b) rein; c,d -> (c,d); (a,b), e -> (a,b,e);<br />
(c,d), (a,b,e) -> (a,b,c,d,e); f , (a,b,c,d,e) -> (a,b,c,d,e,f);<br />
(a,b,c,d,e,f),g -> (a,b,c,d,e,f,g)<br />
• Der Algorithmus baut also folgenden Codebaum „auf dem Kopf“:<br />
a<br />
b<br />
e<br />
a,b c d<br />
a,b,e<br />
c,d<br />
f<br />
a,b,c,d,e<br />
a,b,c,d,e,f<br />
g<br />
a,b,c,d,e,f,g<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 34
Datenkompression<br />
• Idee 3 (Huffman):<br />
Vorteile:<br />
• Das Verfahren ist sehr günstig zu rechnen <strong>–</strong> die Knoten<br />
werden (teil-)sortiert (O(n log n)) und dann werden O(n-1)<br />
innere Knoten gebildet (und dabei wird die Pqueue aufrecht<br />
erhalten zu Kosten von jeweils O(log n) pro Operation bei<br />
einer Heapimplementierung mit n-1 Inserts und 2(n-1)<br />
DeleteMins im Worstcase), also Kosten von O(n log n)<br />
insgesamt<br />
• Das Verfahren ist <strong>–</strong> unglaublich, aber wahr <strong>–</strong> optimal!<br />
<br />
Nachteile:<br />
• Keine<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 35
Huffman-Codierung<br />
• Baum zur Kodierung auf Basis der Zeichenhäufigkeit<br />
optimal aufbauen<br />
• Infos über die gewählte Codierung in der erzeugten Datei<br />
mit der Komprimierung speichern<br />
• Führt je nach Daten zu Komprimierungen ca. zwischen<br />
20%-70% (im worst-case spart man nix...im Gegenteil, die<br />
Codetabelle kostet ja auch etwas)<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 36
Huffman-Codierung: Ablauf<br />
1. Ein Durchlauf zur Bestimmung der vorkommenden<br />
Zeichen und zur Ermittlung ihrer Vorkommenshäufigkeit<br />
2. Aufbau des optimalen Codebaums<br />
3. Ableiten der Codes und Codelängen aus dem Baum<br />
4. Abspeichern der Codeinformationen in der Ausgabedatei<br />
5. Zweiter Durchlauf, um die Zeichen zu codieren, nebst<br />
Ablage in der Ausgabedatei<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 37
Huffman-Codierung: Ablauf<br />
• Häufigkeitsermittlung ist klar<br />
• Aufbau des Codebaums:<br />
<br />
„Gierige“ Suche nach einem optimalen Baum (Gierig,<br />
weil die Zeichen in der Reihenfolge „absteigende<br />
Häufigkeit“ genau einmal angepackt werden)<br />
• Aus dem Baum kann die Codetabelle abgeleitet werden<br />
• Vollständiges Beispiel s. Übungsaufgabe bzw. Mitschrieb<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 38
Huffman-Codierung: Expansion<br />
<br />
<br />
<br />
Die Codetabelle wird in der Ausgabedatei abgespeichert<br />
Aus der Codetabelle kann man direkt den Baum<br />
rekonstruieren<br />
• 0 = links, 1 = rechts, die Tiefe eines Zeichens entspricht<br />
der Länge des Codes für ein Zeichen<br />
Die Expansion läßt dann den binären „Codestring“ durch den<br />
Baum rieseln:<br />
1. Beginn mit dem ersten Zeichen des Codestrings<br />
2. Wegwahl entsprechend der binären Ziffern entlang des<br />
Baumes, beginnend mit der Wurzel, bis ein Blatt erreicht<br />
wird<br />
3. Zum Blatt gehöriges Zeichen ausgeben und auf die Wurzel<br />
zurückgehen, wenn noch nicht alles expandiert ist, zum<br />
Schritt 2 zurückkehren<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 39
Literatur<br />
• Zum generellen Greedy-Ablauf und zur Huffman-Codierung:<br />
B. Owsnicki-Klewe: Algorithmen und Datenstrukturen, 4.<br />
Auflage, Wißner-Verlag, Augsburg, 2002 (gut lesbares<br />
Buch, launig-nett geschrieben, kann beim Verstehen<br />
sicher helfen, kaum/keine Beweise, wenig Aufgaben,<br />
keine Lösungen, aber dafür nicht sehr teuer, ca. 15 Euro,<br />
und berührt viele unserer Themen)<br />
Ansonsten finden sie praktisch in allen genannten<br />
Büchern Informationen zu Greedy-Algorithmen und<br />
Codierungen<br />
• Zu Matroiden: In allen guten Büchern zu Optimierung (z.B.<br />
dem von Korte und Nguyen) oder auch in Cormen, Rivest,<br />
Leierson, Stein oder bei Schöning (s. frühere<br />
Literaturangaben).<br />
11.06.2008 (c) W. <strong>Conen</strong>, FH GE, <strong>ADS</strong> 40