Das Huffman- Kodierverfahren - next-internet.com
Das Huffman- Kodierverfahren - next-internet.com
Das Huffman- Kodierverfahren - next-internet.com
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
LK Informatik <strong>Huffman</strong>-Code Seite 1<br />
<strong>Das</strong> <strong>Huffman</strong>- <strong>Kodierverfahren</strong><br />
Zusammenfassung<br />
Der <strong>Huffman</strong>-Algorithmus kodiert die Zeichen eines Textes entsprechend ihrer<br />
Häufigkeit und hat damit Ähnlichkeit mit dem Morsecode. Zentraler Bestandteil<br />
des Algorithmus ist der <strong>Huffman</strong>-Kodierbaum, dessen Hierarchie die Häufigkeit<br />
der Zeichen wiederspiegelt.<br />
Die folgende Darstellung zeigt das Verfahren der <strong>Huffman</strong>-Kodierung zunächst<br />
sprachunabhängig an einem Beispiel auf. Dabei werden die einzelnen<br />
Schritte ausführlich aufgezeigt und grafisch veranschaulicht.<br />
Im zweiten Teil wird eine Implementierung mit der funktionalen Sprache<br />
Haskell vorgestellt. Dabei wird der entsprechende <strong>Huffman</strong>-Baum mit Hilfe eines<br />
algebraischen Datentyps HTree realisiert. Die Entwicklung der einzelnen<br />
Funktionen erfolgt sowohl nach der bottom-up-Methode als auch im top-down-<br />
Verfahren. Zum Schluss an weiteren Beispielen erläutert.<br />
Motivation<br />
Der zunehmende Datenaustausch erfordert einen möglichst gut komprimierten Code.<br />
Neben mehreren bekannten Verfahren für Bilder und Videos (JPEG, MPEG) sind das<br />
ZIP-, RAR-, ARJ- und TGZ-Format weit verbreitet.<br />
Einige Verfahren kodieren Folgen sich wiederholender Zeichen (sog. ”runs”). Tritt ein<br />
run auf, dann wird dieser durch den entsprechenden Code mit Angabe der Weiderholungsrate<br />
ersetzt. (Lauflängenkodierung - RLE ).<br />
<strong>Das</strong> <strong>Huffman</strong> Verfahren orientiert sich an der Idee des Morsealphabet: häufig vorkommende<br />
Zeichen sollten einen kurzen Code, seltene Zeichen können einen längeren<br />
Code erhalten. Zur Ermittlung der Zeichenhäufigkeit muss die Datei einmal gelesen<br />
werden.<br />
Die meisten Programme verwenden einen festen Code für die Zeichen. Lange Zeit war<br />
der 8-Bit ASCII-Code üblich, heute verwendet man meist den Unicode (16-Bit).<br />
Zeichen Ascii Unicode<br />
’T’ 0101 0100 0000 0000 0101 0100<br />
’e’ 0110 0101 0000 0000 0110 0101<br />
’x’ 0111 1000 0000 0000 0111 1000<br />
’t’ 0111 0100 0000 0000 0111 0100<br />
<strong>Das</strong> Wort ”Text” wird demnach durch folgende Binärfolge dargestellt:<br />
Ascii: 01010100 01100101 01111000 01110100<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 2<br />
Unicode: 0000000001010100 0000000001100101<br />
0000000001111000 0000000001110100<br />
Im Ascii-Code sind hierfür 4 · 8=32Bit erforderlich, der Unicode benötigt sogar<br />
64 Bit. Eine Buch mit ca. 300 Seiten á 45 Zeilen á 60 Zeichen benötigt demnach im<br />
ASCII-Code ca. 300·45·60 = 810 000 Byte. Dies entspricht etwa 790 KB. Im Unicode<br />
wäre die Datei doppelt so groß und würde nicht mehr auf eine Diskette passen.<br />
<strong>Das</strong> Rückcodieren geht bei beiden Codes aufgrund ihrer festen Länge sehr einfach.<br />
Bei einer variablen Codelänge muss darauf geachtet werden, dass der Text wieder<br />
eindeutig decodiert werden kann. Der folgende Code 1 wäre nicht sinnvoll, da die<br />
Zeichenfolge 111100 sowohl ”Text” als auch ”Teett” bedeuten könnte. Beim zweiten<br />
Code käme dieses Wort überhaupt nicht vor. man erkennt, dass jedes mögliche Wort<br />
wieder eindeutig zurückcodiert werden kann.<br />
Zeichen Code 1 Zeichen Code 2<br />
’t’ 0 ’t’ 0<br />
’e’ 1 ’e’ 10<br />
’x’ 10 ’x’ 110<br />
’T’ 11 ’T’ 111<br />
ungünstiger günstiger<br />
Code Code<br />
<strong>Das</strong> Problem läßt sich allgemein lösen, indem man nur Codes verwendet, bei dem<br />
kein Code ein Anfangsteil eines anderen Codes darstellt (Code 2). Einen solchen Code<br />
nennt man Präfix-Code.<br />
Wenn man es schafft, die durchschnittliche Codelänge zu halbieren, schrumpft das<br />
oben erwähnte Buch auf knapp 400 KB zusammen.<br />
<strong>Das</strong> <strong>Huffman</strong>-Verfahren<br />
<strong>Das</strong> <strong>Huffman</strong> Verfahren ist sehr verbreitet und wird von vielen Komprimierprogrammen<br />
benutzt (z.B. PKZIP/PKUNZIP oder ARJ) oder in Verbindung mit anderen Verfahren<br />
(mit LZ77 bei LHA).<br />
Zur Verdeutlichung wird nur ein kurzer Text mit wenigen Zeichen kodiert (keine Großbuchstaben).<br />
FISCHERS_FRITZ_FISCHT_FRISCHE_FISCHE<br />
1. Zuerst wird die Häufigkeit aller Zeichen ermittelt.<br />
F I S _ C H E R T Z<br />
5 5 5 4 4 4 3 3 2 1<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 3<br />
2. Nun wird der sogenannte <strong>Huffman</strong>-Kodierbaum aufgebaut. Dabei wird jedes<br />
Zeichen als Blatt eines Binärbaumes gespeichert. Die Struktur des Baumes ergibt<br />
sich aus der Häufigkeit der einzelnen Zeichen.<br />
(a) Zunächst werden alle Zeichen in ein-blättrige Baume umgewandelt. Dabei<br />
werden die enzelnen Elemente entsprechend ihrer Häufigkeit (aufsteigend)<br />
sortiert.<br />
[(Blatt ’Z’ 1), (Blatt ’T’ 2), (Blatt ’R’ 3),<br />
(Blatt ’E’ 3), (Blatt ’_’ 4), (Blatt ’H’ 4),<br />
(Blatt ’C’,4), (Blatt ’S’ 5), (Blatt ’I’ 5),<br />
(Blatt ’F’ 5)]<br />
(b) Nun werden die Baume mit der geringsten Häufigkeit verschmolzen (Links<br />
der Baum mit der größeren Häufigkeit). Die Summe der einzelnen Häufigkeit<br />
ergibt dabei die Gesamthäufigkeit. Der neue Baum wird dann wieder<br />
in die Liste entsprechend seiner Häufigkeit eingefügt.<br />
3<br />
T Z<br />
; (r,3) ; (e,3) ; (_,4) ; (h,4) ; (c,4) ; (s,5) ; (i,5) ; (f,5)<br />
(c) <strong>Das</strong> Verfahren wird nun solange wiederholt, bis die Liste nur noch einen<br />
Baum, den <strong>Huffman</strong>-Kodierbaum, enthält.<br />
(e,3) ; (_,4) ; (h,4) ; (c,4) ; (s,5) ; (i,5) ; (f,5) ; 6<br />
(h,4) ; (c,4) ; (s,5) ; (i,5) ; (f,5) ; 6<br />
3 R<br />
T Z<br />
(s,5) ; (i,5) ; (f,5) ; 6<br />
3 R<br />
T Z<br />
(f,5) ; 6<br />
3 R<br />
T Z<br />
7<br />
_ E<br />
; 8<br />
H C<br />
; 7<br />
_ E<br />
; 10<br />
S I<br />
; 8<br />
H C<br />
; 11<br />
6 F<br />
3 R<br />
T Z<br />
; 7<br />
_ E<br />
; 10<br />
S I<br />
; 8<br />
H C<br />
; 7<br />
_ E<br />
3 R<br />
T Z<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 4<br />
10<br />
S I<br />
15<br />
7<br />
_ E<br />
; 11<br />
6 F<br />
3 R<br />
T Z<br />
8<br />
H C<br />
36<br />
21<br />
11 10<br />
6 F S I<br />
3 R<br />
T Z<br />
; 15<br />
7<br />
_ E<br />
8<br />
H C<br />
; 21<br />
11 10<br />
6 F S I<br />
3 R<br />
T Z<br />
15<br />
7<br />
_ E<br />
8<br />
H C<br />
<strong>Huffman</strong>-Kodierbaum<br />
3. Den Code eines Zeichens erhält man jetzt durch die Pfadangabe des Zeichens<br />
im Kodebaum. Dabei kann man ”nach links gehen” etwa mit 1 und ”nach rechts<br />
gehen” mit 0 interpretieren.<br />
1<br />
T<br />
1<br />
1<br />
0<br />
Z<br />
0<br />
R<br />
1<br />
0<br />
F<br />
1<br />
S<br />
0<br />
1<br />
0<br />
I<br />
<strong>Huffman</strong>-Kodierbaum für<br />
”FISCHERS_FRITZ_FISCHT_FRISCHE_FISCHE”<br />
<strong>Das</strong> Zeichen ’F’ besitzt also etwa den Code 110 (Links-Links-Rechts). Notwendigerweise<br />
ist kein Pfad eines Blattes Anfangsteil eines anderen Pfades, es<br />
handelt sich also wunschgemäß um einen Präfixcode.<br />
Paul-Natorp-Oberschule Gussmann<br />
1<br />
H<br />
0<br />
1<br />
0<br />
C<br />
1<br />
_<br />
0<br />
0<br />
E
LK Informatik <strong>Huffman</strong>-Code Seite 5<br />
Zeichencodes gemäß <strong>Huffman</strong>-Baum<br />
I S F E H C _ R Z T<br />
100 101 110 000 001 011 010 1110 1110 1111<br />
4. Der Beispieltext kann nun nach dieser Tabelle kodiert werden.<br />
110 100 101 011 001 000 1110 101 010 110 1110 100 1111 1110<br />
F I S C H E R S _ F R I T Z<br />
010 110 100 101 011 001 1111 010 110 1110 100 101 011 001 000<br />
_ F I S C H T _ F R I S C H E<br />
010 110 100 101 011 001 000<br />
_ F I S C H E<br />
Tatsächlich sind die Bits natürlich nicht getrennt sondern bilden einen Strom von<br />
0 und 1:<br />
110100101011001000111010101011011101001111111001011010010<br />
101100111110101101110100101011001000010110100101011001000<br />
5. Zum Dekodieren benötigt man eine Funktion, die die Bitfolge anhand des Kodierbaumes<br />
zurückübersetzt. Dabei wandert man in dem Baum bis zu einem<br />
Blatt, liest das entsprechende Zeichen aus und setzt diesen Vorgang wieder bei<br />
der Wurzel beginnend fort, bis die Bitfolge leer ist. Bleiben dabei am Ende einige<br />
Bits übrig oder kommt man auf eine Folge von Bits, die zu keinem Blatt<br />
führen, so ist der Code ungültig.<br />
Kurzübersicht über das Vorgehen:<br />
• Erstelle eine Liste der vorkommenden Buchstaben mit ihrer Häufigkeit (aufsteigend<br />
nach den Häufigkeiten sortiert).<br />
• Wandle diese Liste in eine Liste von Bäumen (Blätter) um.<br />
• Verschmelze jeweils die zwei Bäume mit der geringsten Häufigkeit zu einem<br />
Gesamtbaum, bis ein einzelner Baum entstanden ist.<br />
• Die Pfade in diesem Kodierbaum ergeben den Code .<br />
• Eine Codefunktion ermittelt den Pfad eines Zeichens im Kodierbaum.<br />
• Eine Dekodierfunktion wandelt eine Bitfolge mit dem Kodebaum in den Text<br />
zurück.<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 6<br />
Implementierung<br />
In diesem Kapitel werden schrittweise Funktionen zum kodieren und dekodieren von<br />
Texten nach dem <strong>Huffman</strong>-Kodieralgorithmus entworfen.<br />
Zeichenliste erstellen<br />
Zunächst muss der Text eingelesen und eine Zeichentabelle erstellt werden. Für jedes<br />
Zeichen wird dabei die Häufigkeit mitabgespeichert. Diese Liste muss nach der<br />
Häufigkeit der Zeichen aufsteigend sortiert sein.<br />
Im dargestellten Beispiel muss man folgende Liste erhalten:<br />
> makeZeichenliste "fischers_fritz_fischt_frische_fische"<br />
[(z,1),(t,2),(r,3),(e,3),(_,4),(h,4),(c,4),(s,5),(i,5),(f,5)]<br />
Für ein einzelnes Zeichen mit seiner Häufigkeit wird sinnvollerweise ein Datentyp<br />
Zeichen vereinbart.<br />
type Zeichen = (Char,Int)<br />
Die Entwicklung der Funktion makeZeichenliste wird nun im top-down-Verfahren<br />
entwickelt. Dabei geht man von der gewünschten Zielfunktion aus und reduziert das<br />
Problem durch Einführung neuer Funktionen.<br />
Zunächst wird also die Zielfunktion definiert:<br />
-- ----------------------------------------------------<br />
-- (1) Erstellen einer Liste der vorkommenden Zeichen<br />
-- sortiert nach der Häufigkeit (aufsteigend)<br />
-- ---------------------------------------------------makeZeichenliste<br />
:: String -> [Zeichen]<br />
makeZeichenliste xs = qsortlist (mklist xs [])<br />
Die Funktionen qsortlist und mklist müssen nun im nächsten Schritt erstellt<br />
werden.<br />
Die Funktion qsortlist sortiert eine Liste von Elementen der Form (Char,Int)<br />
entsprechend der 2. Komponente aufsteigend. Als Sortieralgorithmus wird Quicksort<br />
verfwendet.<br />
qsortlist :: [Zeichen] -> [Zeichen]<br />
qsortlist [] = []<br />
qsortlist ((x,n):xs) = qsortlist kleiner<br />
++ [(x,n)]<br />
++ qsortlist groesser<br />
where kleiner = [(y,m)| (y,m)
LK Informatik <strong>Huffman</strong>-Code Seite 7<br />
Die zweite Funktion mklist erstellt aus dem String die Liste der Zeichen. Dabei<br />
braucht auf die Sortierung keine Rücksicht genommen werden. Diese Funktion stützt<br />
sich auf eine Hilfsfunktion add2list, die ein Element in eine Liste einfügt. mklist<br />
selbst benutzt die Akkumulatortechnik. Die aufzubauende Liste wird als 2. Parameter<br />
mitübergeben und Schritt für Schritt vervollständigt. Der initiale Aufruf muss hier mit<br />
einer leeren Liste erfolgen (siehe makeZeichenliste).<br />
mklist :: String -> [Zeichen] -> [Zeichen]<br />
mklist [] liste = liste<br />
mklist (x:xs) liste = mklist xs (add2list x liste)<br />
Die Funktion add2list fügt schließlich ein Zeichen in eine übergebene Liste ein.<br />
Dabei können zwei Fälle auftreten: Wenn das Zeichen bereits enthalten ist, dann wird<br />
die Häufigkeit um eins erhöht. Andernfalls wird das neue Zeichen mit der Häufigkeit<br />
eins am Ende eingefügt.<br />
add2list :: Char -> [Zeichen] -> [Zeichen]<br />
add2list c [] = [(c,1)]<br />
add2list c ((l,n):ls)<br />
| c==l = (l,n+1):ls<br />
| otherwise = (l,n):add2list c ls<br />
<strong>Huffman</strong>-Kodierbaum erstellen<br />
Im zweiten Schritt muss nun aus der Liste der vorkommenden Zeichen ein Kodierbaum<br />
erstellt werden. Hierzu muss zunächst eine geeignete Datenstruktur bereitgestellt werden.<br />
Dabei müssen folgende Fälle für einen <strong>Huffman</strong>-Baum berücksichtigt werden:<br />
• Er besitzt Blätter, in denen einzelne Zeichen abgespeichert sind.<br />
• Er besitzt innere Knoten, in denen die Gesamthäufigkeit aller Blätter der beiden<br />
Unteräume abgespeichert sind.<br />
Realisierung:<br />
data HTree = Blatt Char Int | Knoten HTree Int HTree<br />
deriving (Eq, Show)<br />
Die Entwicklung des Kodierbaumes soll nun nach der bottom-up-Methode vorgestellt<br />
werden. Dabei ist das Ziel eine Funktion, die aus einer Liste von Zeichen einen geeigneten<br />
<strong>Huffman</strong>-Baum erzeugt.<br />
-- Ziel:<br />
make<strong>Huffman</strong>ntree :: [Zeichen] -> HTree<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 8<br />
1. Schritt:<br />
Zunächst muss die Liste der Zeichen in eine Liste von Blättern umgewandelt werden.<br />
Dies geht mit Hilfe der map-Funktion ganz einfach, wenn es eine Funktion gibt, die<br />
ein Zeichen in ein Blatt umwandelt<br />
-- aus einem Zeichen ein Blatt machen<br />
makeBlatt :: Zeichen -> HTree<br />
makeBlatt (c,n) = Blatt c n<br />
-- Zeichenliste in Baumliste umwandeln<br />
list2tree :: [Zeichen] -> [HTree]<br />
list2tree xs = map makeBlatt xs<br />
2. Schritt:<br />
Nun müssen die ersten beiden Elemente der Baumliste zu einem gemeinsamen Baum<br />
verschmolzen werden. Dabei bildet das Element mit der größeren Häufigkeit den linken<br />
Unterbaum. Die Summe der beiden Einzelhäufigkeiten wird im neuentstandenen<br />
Knoten abbgespeichert.<br />
Hierfür sind eine Reihe von Unterfunktionen nötig:<br />
• Ermitteln der Häufigkeit eines Baumes<br />
• Verschmelzen zweier Bäume<br />
• Einfügen eines Baumes in eine sortierte Liste<br />
Die Bestimmung der Häufigkeit eines Baumes hängt davon ab, ob es sich um ein Blatt<br />
oder einen Knoten handelt.<br />
gewicht :: HTree -> Int<br />
gewicht (Blatt c n) = n<br />
gewicht (Knoten li n re) = n<br />
Bei der Verschmelzung muss die Häufigkeit der beiden Teilbäume berücksichtigt werden.<br />
-- links den Baum mit dem größeren Gewicht<br />
verschmelzen :: HTree -> HTree -> HTree<br />
verschmelzen b1 b2<br />
| n1 >= n2 = Knoten b1 (n1+n2) b2<br />
| otherwise = Knoten b2 (n1+n2) b1<br />
where n1 = gewicht b1<br />
n2 = gewicht b2<br />
Ein verschmolzener Baum muss nun an die richtige Stelle wieder eingefügt werden.<br />
Da die Liste stets sortiert ist, gelingt dies durch einfache Rekursion.<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 9<br />
-- einfuegen eines Baums in eine Liste von Bäumen<br />
-- entsprechend dem Baumgewicht (aufsteigend)<br />
insTreeInList :: HTree -> [HTree] -> [HTree]<br />
insTreeInList b [] = [b]<br />
insTreeInList b (x:xs)<br />
| gewicht b [HTree]<br />
mkHTree [b] = [b] -- Stoppfall<br />
mkHTree (b1:b2:bs) = mkHTree (insTreeInList b bs)<br />
where b=verschmelzen b1 b2<br />
4. Schritt:<br />
Damit kann die gewünschte Zielfunktion make<strong>Huffman</strong>ntree erstellt werden. Sie<br />
ruft die beschriebenen Funktionen nun nur noch in einer geeigneten Weise auf.<br />
-- ----------------------------------------------------<br />
-- (2) Erstellen des <strong>Huffman</strong>-Kodierbaumes<br />
-- ----------------------------------------------------<br />
make<strong>Huffman</strong>tree :: [Zeichen] -> HTree<br />
make<strong>Huffman</strong>tree xs = head (mkHTree (list2tree xs))<br />
Nachricht kodieren<br />
Endlich können Nachrichten nun kodiert werden. Die Entwicklung einer geeigneten<br />
Funktion kodieren erfolgt wieder per top-down-Verfahren.<br />
1. Schritt:<br />
Gesucht ist eine Kodierfunktion, die einen String in einen Binärcode umwandelt. Da<br />
für das dekodieren aber der hierbei entwickelte <strong>Huffman</strong>-Baum benötigt wird, muss<br />
die Kodierfunktion auch diesen zurückliefern.<br />
-- ----------------------------------------------------<br />
-- (3) Kodieren einer Nachricht<br />
-- ----------------------------------------------------<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 10<br />
kodieren :: String -> (String,HTree)<br />
kodieren xs = (kodieren’ xs htree,htree)<br />
where htree = make<strong>Huffman</strong>tree (makeZeichenliste xs)<br />
2. Schritt:<br />
kodieren stützt sich auf die Hilfsfunktion kodieren’, die einen Text mit Hilfe<br />
eines Baumes kodiert. Dabei wird der Eingabestring Zeichen für Zeichen kodiert<br />
(Rekursion). Die Kodierung eines einzelnen Zeichens erfolgt mit der Funktion code<br />
kodieren’ :: String -> HTree -> String<br />
kodieren’ [] b = []<br />
kodieren’ (x:xs) b = (code b x)++kodieren’ xs b<br />
3. Schritt:<br />
Den Code eines Zeichens kann man durch Suchen im Kodierbaum finden. Diese Methode<br />
ist jedoch sehr Zeitaufwendig, da der Kodierbaum über die Funktion member<br />
mehrfach durchlaufen wird.<br />
code :: HTree -> Char -> String<br />
code (Blatt c n) x<br />
| c==x = []<br />
| otherwise = error "Zeichen nicht in Baum enthalten"<br />
code (Knoten li n re) x<br />
| member x li = ’1’:code li x<br />
| otherwise = ’0’:code re x<br />
where member a (Blatt c n) = (a==c)<br />
member a (Knoten li c re) = (member a li) || (member a re)<br />
Nachrichten dekodieren<br />
Zum dekodieren einer Nachricht wird natürlich der benutzte Kodierbaum benötigt.<br />
Dieser muss also bei einer Datenkomprimierung / Datenübertragung mitgespeichert<br />
werden.<br />
<strong>Das</strong> Dekodieren erfolgt nach folgendem Schema: Aus dem Code werden solange Bits<br />
gelesen, bis man im zugehörigen Kodierbaum auf ein Blatt trifft. <strong>Das</strong> so ermittelte Zeichen<br />
wird als Klartext zurückgegeben. Anschließend wird mit dem Rest der kodierten<br />
Nachricht nach demselben Muster weiterverfahren.<br />
Die Funktionen dekodieren benutzt die Hilfsfunktion dekodieren’, die als zusätlichen<br />
Parameter eine Kopie des Kodierbaumes erhält. Diese wird benötigt, da beim<br />
rekursiven Durchlaufen des Baumes der Originalbaum verloren geht. Sobald ein Zeichen<br />
gefunden wurde, muss aber die erneute Suche wieder mit dem Originalbaum<br />
beginnen.<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 11<br />
Bleiben dabei am Ende einige Bits übrig oder kommt man auf eine Folge von Bits, die<br />
zu keinem Blatt führen, so ist der Code ungültig.<br />
-- ----------------------------------------------------<br />
-- (4) Dekodieren einer Nachricht<br />
-- Es wird der zugehörige HTree benötigt<br />
-- ----------------------------------------------------<br />
dekodieren :: String -> HTree -> String<br />
dekodieren s b = dekodieren’ s b b<br />
dekodieren’ :: String -> HTree -> HTree -> String<br />
dekodieren’ [] b (Blatt c n) = [c]<br />
dekodieren’ [] b (Knoten b1 n b2) = error "Code ungültig"<br />
dekodieren’ xs b (Blatt c n) = c:dekodieren’ xs b b<br />
dekodieren’ (x:xs) b (Knoten li n re)<br />
| x==’1’ = dekodieren’ xs b li<br />
| otherwise = dekodieren’ xs b re<br />
Effizienz<br />
Der Beispieltext ”FISCHERS FRITZ FISCHT FRISCHE FISCHE” besitzt 36 Zeichen<br />
(inklusive Leerzeichen). Bei der Speicherung/Übertragung im 8-Bit-ASCII-Code<br />
werden dafür 8 ∗ 36 = 288 Bit benötigt.<br />
Der <strong>Huffman</strong>-kodierte Text hat eine Länge von 114 Bit. Dies entspricht einer Datenkompression<br />
von ca. 60 %.<br />
Eine noch bessere Reduzierung würde man erhalten, wenn ganze Silben im Baum<br />
abgespeichert würden. Dies würde jedoch den Aufwand erheblich steigern.<br />
Beispiele<br />
Die vorgestellten Funktionen können nun für beliebige Eingaben angewandt werden.<br />
Beispiel 1:<br />
> kodieren "abrakadabra"<br />
("01101001111011100110100",<br />
Knoten (Knoten (Knoten (Knoten (Blatt ’k’ 1) 2 (Blatt ’d’ 1))<br />
4 (Blatt ’b’ 2)) 6 (Blatt ’r’ 2)) 11 (Blatt ’a’ 5))<br />
> dekodieren "01101001111011100110100" _<br />
(snd (kodieren "abrakadabra"))<br />
"abrakadabra"<br />
Paul-Natorp-Oberschule Gussmann
LK Informatik <strong>Huffman</strong>-Code Seite 12<br />
Beispiel 2:<br />
> kodieren "simsalabim"<br />
("00111110001001110010111110",<br />
Knoten (Knoten (Knoten (Blatt ’i’ 2) 4 (Blatt ’m’ 2)) 6<br />
(Blatt ’a’ 2)) 10 (Knoten (Knoten (Blatt ’l’ 1) 2<br />
(Blatt ’b’ 1)) 4 (Blatt ’s’ 2)))<br />
> dekodieren "00111110001001110010111110" _<br />
(snd (kodieren "simsalabim"))<br />
"simsalabim"<br />
Anwendung : Kodieren/Dekodieren einer Textdatei<br />
In der klassischen Haskell-Programmierung kommen interaktive Ein- und Ausgabe<br />
nicht vor; diese lassen sich aber durchaus realisieren. Die Grundidee, dass keine sequentiellen<br />
Fragmente benutzt werden, muss dabei aber aufgegeben werden. Schließlich<br />
soll zunächste eine Textdatei eingelesen, dann kodiert und schließlich wieder dekodiert<br />
werden.<br />
Haskell bietet folgende Konstruktion zur sequentiellen Bearbeitung an:<br />
codefile dateiname =<br />
do s
LK Informatik <strong>Huffman</strong>-Code Seite 13<br />
++ "Codelänge =" ++ (show lc) ++ " Bits\n"<br />
++ "Komprimierung :" ++ (show kompr) ++ " %\n"<br />
++ "-------------------------------------\n"<br />
where (c,b) = kodieren s<br />
ls = length s<br />
lc = length c<br />
kompr = fromInt (ls*8-lc)/(fromInt (ls*8)) *100<br />
Wendet man diese Funktion etwa auf einen Auszug von Shakespeares Julius Caesar an<br />
(Act 2, Scene 1: Rome. BRUTUS’s orchard), so erhält man folgende Übersicht:<br />
Tree> codefile "caesar21.txt"<br />
Statistik :<br />
-----------------------------------------<br />
Textlänge = 12111 Zeichen<br />
Textlänge = 96888 Bits<br />
Codelänge = 55321 Bits<br />
Komprimierung : 42.9021 %<br />
-----------------------------------------<br />
Eine Auswertung der Kurzgeschichte „Auf der Galerie” von Franz Kafka ergibt:<br />
Tree> codefile "kafka.txt"<br />
Statistik :<br />
-----------------------------------------<br />
Textlänge = 2121 Zeichen<br />
Textlänge = 16968 Bits<br />
Codelänge = 9570 Bits<br />
Komprimierung : 43.5997 %<br />
-----------------------------------------<br />
Literatur<br />
[1] Richard Bird, Philip Wadler : Einführung in die funktionale Programmierung<br />
Hanser-Verlag München Wien 1992<br />
Standardwerk zur Einführung, Programmiersprache Miranda<br />
[2] Simon Thompson : MIRANDA - The Craft Of Functional Programming<br />
Addison-Wesley Co. , Inc. 1995<br />
Hierzu gibt es auch eine neuere Ausgabe, das die Sprache Haskell benutzt.<br />
[3] Christoph Oehler, Raimond Reichert : Applet und Lernaufgabe zu ”Kompression”,<br />
ETH Zürich<br />
http://www.educeth.ch/informatik/interaktiv/kompression/<br />
Paul-Natorp-Oberschule Gussmann