Übrigens: um den Algorithmus im Unterricht einzuführen, sind keine Formeln notwendig! Warum reicht die normale ASCII-Codierung nicht aus?

Ähnliche Dokumente
Wie muss der Eingabetext beschaffen sein, damit er sich gut komprimieren lässt?

Kapitel 7: Optimalcodierung und Huffman Coding

Dynamisches Huffman-Verfahren

2. Woche Eindeutige Entschlüsselbarleit, Sätze von Kraft und McMillan, Huffmancodierung

21. Greedy Algorithmen. Aktivitätenauswahl, Fractional Knapsack Problem, Huffman Coding Cormen et al, Kap. 16.1, 16.3

Aufgabe: Platz-effiziente Kompression von Textdaten

Übersicht. Aktivitäten-Auswahl-Problem. Greedy Algorithmen. Aktivitäten-Auswahl-Problem. Aktivitäten-Auswahl-Problem. Datenstrukturen & Algorithmen

21. Dynamic Programming III

Algorithmus zur komprimierten Übertragung von Textdaten an mobile Endgeräte

Einleitung. Kapitel 1

ADS: Algorithmen und Datenstrukturen 2

ADS: Algorithmen und Datenstrukturen 2

Praktikum BKSPP: Blatt 2

Seminar Kompressionsalgorithmen Huffman-Codierung, arithmetische Codierung

Algorithmen und Datenstrukturen 2

Computer Science Unplugged Algorithmen zur Textkompression

Stud.-Nummer: Datenstrukturen & Algorithmen Seite 1

Strings. Stringsuche, Boyer-Moore, Textkompression, Huffman Codes.

Referat zum Thema Huffman-Codes

Datenstrukturen und Algorithmen (SS 2013)

3.3 Optimale binäre Suchbäume

3 Codierung diskreter Quellen. Quelle Quellcodierer Kanalcodierer reduziert die benötigte Datenmenge. fügt Daten zur Fehlerkorrektur ein.

Algorithmen und Komplexität Lösungsvorschlag zu Übungsblatt 8

Seminar über Algorithmen, SS2004. Textkompression. von Christian Grümme und Robert Hartmann

Abstrakter Datentyp (ADT): Besteht aus einer Menge von Objekten, sowie Operationen, die auf diesen Objekten wirken.

Informatik II, SS 2016

9 Minimum Spanning Trees

Wann sind Codes eindeutig entschlüsselbar?

Mathematik für Information und Kommunikation

Datenkompression. 1 Allgemeines. 2 Verlustlose Kompression. Holger Rauhut

Copyright, Page 1 of 7 Heapsort

Isomorphie von Bäumen

Einführung in die Informatik II Aus der Informationstheorie: Datenkompression

(a, b)-bäume / 1. Datenmenge ist so groß, dass sie auf der Festplatte abgespeichert werden muss.

2.7 Der Shannon-Fano-Elias Code

15 Optimales Kodieren

Technische Informatik - Eine Einführung

Datenstrukturen Kurzanleitung

6. Komprimierung. (Text)komprimierung ist ein Wechsel der Repräsentation von Daten, so daß sie weniger

Praktikum BKSPP. Aufgabenblatt Nr. 2

Theoretische Grundlagen der Informatik

Klausur Informatik-Propädeutikum (Niedermeier/Hartung/Nichterlein, Wintersemester 2012/13)

Suchen und Sortieren Sortieren. Heaps

Übung zur Vorlesung. Vorlesung: Heinrich Hußmann Übung: Renate Häuslschmid, Hanna Schneider

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 15/16. Kapitel 13. Bäume. Bäume 1

Einführung in die Programmierung (EPR)

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 16/17. Kapitel 14. Bäume. Bäume 1

Suchbäume. Suchbäume. Einfügen in Binären Suchbäumen. Suchen in Binären Suchbäumen. Prinzip Suchbaum. Algorithmen und Datenstrukturen

11.1 Grundlagen - Denitionen

Theoretische Grundlagen der Informatik

8. A & D - Heapsort. Werden sehen, wie wir durch geschicktes Organsieren von Daten effiziente Algorithmen entwerfen können.

Theoretische Grundlagen der Informatik. Vorlesung am 8. Januar INSTITUT FÜR THEORETISCHE INFORMATIK

Informatik II, SS 2018

Heapsort / 1 A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8]

Grundlagen der Informatik II Übungsblatt: 5, WS 17/18 mit Lösungen

13. Bäume: effektives Suchen und Sortieren

Vorlesung Informatik 2 Algorithmen und Datenstrukturen

Algorithmen und Datenstrukturen

Vorlesung Datenstrukturen

13. Bäume: effektives Suchen und Sortieren

12 (2-4)-Bäume Implementierbare Funktionen. (2-4)-Bäume sind durch folgende Eigenschaften deniert: 1. Alle Äste sind gleich lang

Informatik II, SS 2014

Greedy Algorithms - Gierige Algorithmen

Übungsblatt 5 - Musterlösung

7. Sortieren Lernziele. 7. Sortieren

Natürliche Bäume. (Algorithmen und Datenstrukturen I) Prof. Dr. Oliver Braun. Letzte Änderung: :16. Natürliche Bäume 1/16

13 (2-4)-Bäume Implementierbare Funktionen. (2-4)-Bäume sind durch folgende Eigenschaften deniert: 1. Alle Äste sind gleich lang

Proseminar WS 2002/2003

Gierige Algorithmen Interval Scheduling

Algorithmen und Datenstrukturen Heapsort

Algorithmen und Datenstrukturen

Algorithmen und Datenstrukturen

Anwendungsbeispiel MinHeap

Programm heute. Algorithmen und Datenstrukturen (für ET/IT) Suchen. Lineare Suche. Such-Algorithmen. Sommersemester Dr.

Logik. Gabriele Kern-Isberner LS 1 Information Engineering. TU Dortmund Wintersemester 2014/15 WS 2014/15

18. Natürliche Suchbäume

Problem: Finde für Alphabet mit n Zeichen einen Binärcode, der die Gesamtlänge eines Textes (über diesem Alphabet) minimiert.

Übung Datenstrukturen. Bäume

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 17/18. Kapitel 14. Bäume. Bäume 1

Beispiellösung zu den Übungen Datenstrukturen und Algorithmen SS 2008 Blatt 5

Klausur Informationstheorie und Codierung

Abzählen und Konstruktion der Strukturisomere von Alkanen, Alkenen und Alkinen

Datenstrukturen und Algorithmen. Christian Sohler FG Algorithmen & Komplexität

(Digital) Sorting. October 25, Algorithms & Datastructures 2 Exercises WS 2016

Arrays. Theorieteil. Inhaltsverzeichnis. Begriffe. Programmieren mit Java Modul 3. 1 Modulübersicht 3

Datenstrukturen & Algorithmen

Algorithmische Graphentheorie

Bäume 1. Thomas Röfer

Vorlesung 15a. Quellencodieren und Entropie

elementare Datenstrukturen

KAPITEL 5. Damit wird F n (B) selbst zu einer Booleschen Algebra und es gilt f(x) := f(x) 0(x) := 0 B 1(x) := 1 B

(Prüfungs-)Aufgaben zur Codierungstheorie

Aufgabe 2 Konstruktion von Binärbäumen Tafelübung

Proseminar Datenkompression Universelles Codieren ganzer Zahlen

Textsuche mit Indexstrukturen. Anfragen. Inzidenz Vektoren. Term-Dokument Inzidenz Matrix

Häufige Mengen ohne Kandidatengenerierung. FP-Tree: Transaktionen. Konstruktion eines FP-Trees. FP-Tree: Items

Transkript:

Huffman-Code Dieser Text ist als Hintergrundinformation ausschliesslich für die Lehrperson gedacht. Der Text ist deshalb eher technisch gehalten. Er lehnt sich an das entsprechende Kapitel in "Turing Omnibus" von Dewdney A.K. (1989, 320ff) an. Allerdings ist es auch für die Lehrperson nicht notwendig, den Algorithmus bis ins letzte Detail zu verstehen. Insbesondere die Abschnitte "Was wäre ein optimaler Code?" und "Der Huffman-Code ist ein optimaler Code!" können von weniger versierten LeserInnen übersprungen werden. Übrigens: um den Algorithmus im Unterricht einzuführen, sind keine Formeln notwendig! Warum reicht die normale ASCII-Codierung nicht aus? Wenn wir einen "reinen" Text als ASCII-Datei speichern, so belegt jedes gespeicherte Zeichen 8 Bit. Wir könnten auch sagen, es wird mit 8 Bit codiert. Ob dieses Zeichen nun 1000 Mal oder nur ein einziges Mal im Text vorkommt, spielt dabei keine Rolle. Das ist im Hinblick auf den Speicherbedarf einer Datei sicherlich keine effektive Codierungsmethode! Warum nicht? Nun, schöner wäre eine Codierung, welche die Häufigkeiten der Zeichen im Text berücksichtigt. Wenn ein Zeichen häufig im Text auftritt, wählen wir einen möglichst kurzen Code dafür. Wenn ein Zeichen hingegen nur ganz selten vorkommt, macht es nichts, wenn sein Code etwas länger ist. Das Ziel ist also ein Code mit folgender Eigenschaft: je grösser die Wahrscheinlichkeit, dass ein Zeichen im Text auftritt, desto kürzer soll sein Code sein (im Vergleich zu den Codes der anderen Zeichen). Wir könnten auch sagen: je häufiger ein Zeichen im Text vorkommt, desto kürzer soll sein Code sein. Die Wahrscheinlichkeiten bzw. die Häufigkeiten der Zeichen spielen also eine zentrale Rolle. Die "Elemente" der Huffman-Codierung Die Huffman-Codierung besitzt genau die oben gewünschte Eigenschaft! Betrachten wir nun, wie die Huffman-Codierung abläuft. Etwas formal ausgedrückt, hantieren wir dabei mit folgenden Elementen: Wir möchten einen Text komprimieren. Die Länge diese Textes sei M Zeichen. Die Zeichen, aus denen der Text besteht, nennen wir "Symbole" und bezeichnen sie mit s 1, s 2, s 3,..., s n. Diese Symbole sind üblicherweise eine Untermenge der ASCII- Zeichen. Wesentlich ist, dass wir nur diejenigen ASCII-Zeichen betrachten, die im Text überhaupt auftreten. Alle anderen Zeichen spielen keine Rolle! Die Wahrscheinlichkeit des Symbols s i (seine relative Häufigkeit im Text) nennen wir p i. Berechnet wird sie wie folgt: Von der Wahrscheinlichkeitsrechnung wissen wir, dass die Summe aller relativen Häufigkeiten Eins beträgt.

Was wäre ein optimaler Code? Bei der Codierung ersetzen wir jedes Zeichen im Originaltext durch einen Code für dieses Zeichen. Formaler ausgedrückt: das Symbol s i im Originaltext wird durch ein binäres Codewort der Länge l i ersetzt. Gesucht ist ein optimaler Code. Optimal bedeutet, dass der Code die mittlere (durchschnittliche) Codewortlänge minimiert. Berechnet wird die mittlere Codewortlänge L wie folgt: Der Huffman-Code ist ein optimaler Code! Wir können annehmen, die Symbole s 1, s 2, s 3,..., s n seien bereits so nummeriert, dass die Wahrscheinlichkeiten der Symbole absteigend sortiert sind: p 1 >= p 2 >= p 3 >=... >= p n. Wir hatten schon zu Beginn gefordert, dass die wahrscheinlicheren Symbole kürzere Codes haben sollten. Das heisst, es soll gelten: l 1 <= l 2 <= l 3 <=... <= l n. Genau das erreicht die Huffman-Codierung; wie, das zeigt der Algorithmus weiter unten. Warum ist das so wichtig? Nun, es ist anhand der Formel zur Berechnung von L relativ leicht einzusehen, dass die mittlere Codewortlänge L nur dann minimal sein kann, wenn eben die Codewortlängen aufsteigend sortiert sind. Ergo ist der Huffman-Code optimal! Anmerkung: Der Algorithmus ist natürlich kein Beweis für die Optimalität des Huffman-Codes! Der Beweis baut aber auf den Eigenschaften des Algorithmus auf. Daher kann der Algorithmus als "Plausibilitätsbetrachtung" gelten. Grafische Repräsentation des Huffman-Codes: der Huffman-Baum Der Huffman-Algorithmus baut einen sogenannten "Code-Baum", den Huffman-Baum, auf. Mit Hilfe dieses Baumes werden die Codewörter für die einzelnen Symbole erzeugt. Ausserdem dient der Baum als praktische visuelle Repräsentation der Symbole, ihrer Wahrscheinlichkeiten und ihrer Codes. Beispiel. Nehmen wir an, wir hätten einen Text, in dem nur die Symbole A, B, C, D, E, F, G vorkommen. Was der Text genau ist, spielt für die Konstruktion der Huffman-Codes keine Rolle. Wichtig sind einzig und allein die Wahrscheinlichkeiten der Symbole. Diese seien 0.25, 0.21, 0.18, 0.14, 0.09, 0.07 respektive 0.06. Die Huffman-Codes für diese Symbole können wir im Huffman-Baum grafisch darstellen (siehe folgende Seite). Die Blätter dieses Baumes repräsentieren die Symbole des Originaltextes; in ihnen steht die Wahrscheinlichkeit des jeweiligen Symbols. Jeder Knoten des Baumes hat auch eine gewisse Wahrscheinlichkeit. Diese ist einfach die Summe der Wahrscheinlichkeiten seiner Kinder. Das bedeutet, dass die Wahrscheinlichkeit der Wurzel Eins sein muss - es ist einfach die Summe der Wahrscheinlichkeiten aller Symbole! Der Huffman-Baum grafisch dargestellt:

Codewörter - einfach ablesen aus dem Huffman-Baum Aus diesem Huffman-Baum können die eigentlichen Code-Wörter abgelesen werden. Dazu sucht man sich den (eindeutigen) Weg von der Wurzel des Baumes zu einem Symbol-Blatt, und macht folgendes: Wir starten mit einem leeren String. Bei jedem Knoten können wir uns entscheiden, ob wir nach links oder rechts weitergehen. Steigen wir in den linken Teilbaum ab, so fügen dem String hinten eine "0" an. Steigen wir in den rechten Teilbaum ab, so fügen dem String hinten eine "1" an. Sind wir in dem Blatt angelangt, so nehmen wir den String als Codewort für das entsprechende Symbol. Beispiel (Fortsetzung). Der obige Huffman-Baum liefert uns folgende Codewörter: A B C D E F G 00 10 010 011 111 1100 1101 Die mittlere Codelänge L beträgt hier 2.67 Bit. Besser geht es nicht!

Decodierung - einfach dank Huffman-Baum Warum verwendet der Huffman-Algorithmus einen Huffman-Baum? Der Grund dafür ist die effiziente Decodierung. Will man einen Binärstring anhand eines Huffman-Baumes decodieren, geht man analog zur oben beschriebenen Codewort-Erzeugung vor. Man startet bei der Wurzel. Ist das erste Zeichen im Binärstring eine "0", steigt man in den linken Teilbaum ab, sonst in den rechten. Dieses Prozedere wiederholt man, bis man in einem Blatt ankommt. Und schon hat man das Symbol gefunden! Etwas technischer ausgedrückt, garantiert der Huffman-Baum, dass der daraus resultierende Code präfixfrei ist. Das heisst, kein Codewort ist der Anfang eines anderen Codewortes. Wäre dem nicht so, könnte man nicht so einfach wie oben beschrieben decodieren: man wüsste nicht, ob man in einem Knoten oder schon in einem Blatt ist! Konstruktion des Huffman Huffman-Baums - der Huffman-Algorithmus Der Huffman-Algorithmus verfolgt das Ziel, weniger häufigen Symbolen längere Codewörter zuzuweisen. Daher geht er wie folgt vor. 1. Er startet mit einer Liste von n Blättern. Jedes Blatt repräsentiert ein Symbol. 2. Dann entfernt er die zwei Blätter mit den kleinsten Wahrscheinlichkeiten aus der Liste. 3. Diese beiden Blätter fasst er zu einem Knoten zusammen. Der Knoten wird in die Liste eingefügt. Und jetzt macht der Algorithmus bei 2. weiter: er entfernt die zwei Blätter oder Knoten mit den kleinsten Wahrscheinlichkeiten aus der Liste, fasst sie zusammen zu einem neuen Knoten und fügt diesen in die Liste ein. Und so weiter, bis die Liste nur noch einen Knoten enthält. Dieser Knoten ist die Wurzel des Huffman-Baums - und daraus können die Codewörter ganz einfach, wie oben beschrieben, erzeugt werden! Die Datenstruktur Den Huffman-Algorithmus zur Erzeugung der Codewörter zeigen wir im folgenden als Pascalähnliches Programm. Der Huffman-Baum spielt dabei eine zentrale Rolle. Deshalb brauchen wir folgende Baum-Datenstruktur: TYPE Subtrees = ARRAY n+1 of Node; (* Array mit Indizes 0 bis n *) Node = POINTER TO NodeDesc; NodeDesc = RECCORD prob: REAL; (* Wahrscheinlichkeit *) symbol: CHAR; (* Symbol *) left: Node; (* linker Teilbaum *) right: Node; (* rechter Teilbaum *) END;

In einem Baum unterscheiden wir zwischen Knoten und Blättern. Graphisch dargestellt: In einem Blatt sind left und right nicht definiert. Daher setzen wir sie auf NIL. In einem Knoten hingegen gibt es kein symbol - dieses Feld wird bei Knoten einfach ignoriert. Initialisierung Der Algorithmus startet mit einer Liste (Array) von Blättern, für jedes Symbol ein Blatt. Entsprechend initialisieren wir eine Variable array vom Typ SubTrees: FOR i := 1 TO n DO array[i] := NEW (Node); array[i].symbol := s i ; array[i].prob := p i ; array[i].left := NIL; array[i].right := NIL; END; Anmerkung: das Element array[0] benutzen wir als Hilfselement. Daher wird es nicht initialisiert. In array sind nach dieser Initialisierung n "Teilbäume" gespeichert, die nur aus je einem Blatt bestehen. Der eigentliche Algorithmus Nach der Initialisierung kann der eigentliche Algorithmus beginnen. Wie oben beschrieben, fassen wir solange Teilbäume in der Liste array zusammen, bis die Liste nur noch ein Element enthält. Wieviele "Zusammenfassungen" gibt es? Wir starten mit n Elementen und fassen in jedem Schritt 2 Elemente zusammen. Nach dem ersten Schritt sind es nur noch n-1 Elemente, nach dem zweiten n-2... Nach dem i-ten Schritt sind es noch n-i Elemente. Also fassen wir insgesamt n-1 Mal je zwei Elemente zusammen. Vor der i-ten Zusammenfassung hat es noch n-i+1 Teilbäume in der Liste. Diese sortieren wir in absteigender Reihenfolge. Als Sortierkriterium verwenden wir die Wahrscheinlichkeit der Wurzeln der Teilbäume (array[i].prob). Die beiden Teilbäume mit den kleinsten Schlüsseln werden verschmolzen. Dank der absteigenden Sortierung wissen wir, dass es sich bei den gesuchten Teilbäumen um die beiden letzten in der Liste handelt (array[n-i].prob und array[ni+1].prob). Diese werden mit Hilfe des Arrayelements array[0] verschmolzen. Übrig bleiben noch n-i Elemente.

FOR i := 1 TO (n-1) DO sortiere Teilbäume im Teilarray von 1 bis (n-i+1) nach absteigender Reihenfolge der Wahrscheinlichkeiten; array[0] := NEW (Node); array[0].prob := array[n-i].prob + array[n-i+1].prob; array[0].left := array[n-i]; array[0].right := array[n-i+1]; array[n-i] := array[0]; END; Beispiel (Fortsetzung): Initialisierung Nach der ersten Iteration, und neu sortiert Nach der zweiten Iteration, und neu sortiert Bei dieser grafischen Darstellung lässt sich schön verfolgen, wie bei jeder Iteration die beiden Teilbäume mit der kleinsten Wahrscheinlichkeit verschmolzen werden. Nach dem Verschmelzen sind alle Blätter dieser beiden Teilbäume im neuen Baum eine Etage tiefer angesiedelt als vorher. Das heisst, ihr Code verlängert sich ebenfalls um ein Bit. So stellt der Huffman-Algorithmus sicher, dass die seltensten Codewörter die längsten werden! Anmerkung: In der Realität wird der Algorithmus nicht wie beschrieben implementiert - das wäre viel zu ineffizient. Es ging uns hier nur um das Prinzip des Algorithmus.

Zusammenfassung Komprimierung eines ASCII-Textes mit Huffman Die Komprimierung läuft in 5 Schritten ab: 1. Schritt: Lege eine Tabelle mit allen im Originaltext vorhandenen Symbolen und deren relativen Häufigkeiten an. 2. Schritt: Konstruiere den Huffman-Baum und erzeuge daraus eine Code-Tabelle. 3. Schritt: Durchlaufe den Text und ersetzte jedes Symbol mit dem entsprechenden Code. Das Resultat ist ein grosser binärer String. 4. Schritt: Damit dieser String platzsparend gespeichert werden kann, zerlegen wir ihn. Die meisten Computer arbeiten heute mit Ganzzahlen (Integers), die 32 Bit lang sind. Daher zerlegen wir den String in Blöcke der Länge 32. 5. Schritt: Jeder dieser Blöcke kann nun einfach in einen Integer umgewandelt werden. Diese speichern wir. Zusätzlich speichern wir für die Decodierung den Huffman-Baum ab. Dekomprimieren 1. Schritt: Lade den Huffman-Baum. 2. Schritt: Lies die Integers und interpretiere sie wiederum als einen grossen binären String. Starte bei der Wurzel im Huffman-Baum. Ist das erste Zeichen im Binärstring eine "0", steige in den linken Teilbaum ab, sonst in den rechten. Wiederhole, bis in einem Blatt angekommen. Das gefundene Symbol wird ausgegeben. Wiederhole dieses Prozedere, bis der ganze Binärstring abgearbeitet ist.