02.12.2012 Aufrufe

Das Huffman- Kodierverfahren - next-internet.com

Das Huffman- Kodierverfahren - next-internet.com

Das Huffman- Kodierverfahren - next-internet.com

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!