LZW

LZW-Kompression

Lempel-Ziv-Welch — verlustfreies Wörterbuch-Verfahren hinter GIF

LZW (Lempel-Ziv-Welch) ist ein verlustfreies Wörterbuch-Kompressionsverfahren, das 1984 von Terry Welch aus den Verfahren LZ78 von Abraham Lempel und Jacob Ziv weiterentwickelt wurde. Es ist das Kompressionsverfahren hinter GIF (und optional TIFF/PDF).

Die Grundidee: Wiederkehrende Folgen von Zeichen werden durch kurze Zahlencodes ersetzt. Das Besondere an LZW ist, dass das Wörterbuch nicht mitgespeichert werden muss — Encoder und Decoder bauen es während des Lesens nach exakt denselben Regeln Schritt für Schritt selbst auf.

Gestartet wird mit einem Wörterbuch, das bereits alle einzelnen Symbole enthält (bei GIF die Paletten-Indizes). Während der Verarbeitung kommen immer längere Zeichenketten hinzu — je öfter sich Muster wiederholen, desto stärker die Kompression.

Kernidee: Encoder und Decoder bauen dasselbe Wörterbuch nach identischen Regeln auf — das Wörterbuch selbst muss daher nie übertragen werden.

⚙️ So funktioniert der Algorithmus

Der Encoder verlängert ein Muster w so lange, wie es im Wörterbuch existiert. Sobald w + k unbekannt ist, wird der Code für w ausgegeben und w + k neu aufgenommen.

ENCODER (Kodierung):
┌──────────────────────────────────────────────────────────┐
│ w = leer                                                   │
│ für jedes Zeichen k im Eingabestrom:                       │
│     wenn (w + k) im Wörterbuch:                            │
│         w = w + k            ← Muster verlängern            │
│     sonst:                                                 │
│         gib code(w) aus      ← bekannten Teil ausgeben      │
│         füge (w + k) neu ins Wörterbuch ein                │
│         w = k                                              │
│ gib code(w) aus                                            │
└──────────────────────────────────────────────────────────┘

         Eingabe  "ABABABA"
            │
            ▼
   ┌─────────────────┐      Codes      ┌─────────────────┐
   │  Wörterbuch baut │ ───────────────▶│  kompakter      │
   │  sich dynamisch  │   65,66,256,258 │  Code-Strom     │
   │  auf (256, 257…) │                 │                 │
   └─────────────────┘                 └─────────────────┘

🔢 Schritt-für-Schritt-Beispiel

Eingabe "ABABABA": Beobachte, wie das Wörterbuch wächst und die Ausgabe kürzer wird als die Eingabe.

Beispiel: Kodierung von „ABABABA“ (Start-Wörterbuch: A=65, B=66)

Schrittwkw+k bekannt?Ausgabeneuer Eintrag
1ABnein65 (A)AB = 256
2BAnein66 (B)BA = 257
3ABja
4ABAnein256 (AB)ABA = 258
5ABja
6ABAja
7ABAEnde258 (ABA)

🎬 Visueller Ablauf

Dieselbe Eingabe live durchgespielt: Grün = Muster verlängern, Blau = Code ausgeben & neuen Eintrag anlegen.

A
65
B
66
A
65
B
66
A
65
B
66
A
65
1
w = +k = AA“ bekannt → w verlängern
2
w = A+k = BAusgabe 65 (A)·neu: AB = 256
3
w = B+k = AAusgabe 66 (B)·neu: BA = 257
4
w = A+k = BAB“ bekannt → w verlängern
5
w = AB+k = AAusgabe 256 (AB)·neu: ABA = 258
6
w = A+k = BAB“ bekannt → w verlängern
7
w = AB+k = AABA“ bekannt → w verlängern
8
w = ABA+k = Ausgabe 258 (ABA)
Code-Strom: 6566256258
Aufgebautes Wörterbuch: AB=256BA=257ABA=258
7 Zeichen Eingabe → 4 Codes Ausgabe.

Ein zweites Beispiel

Bei stärker wiederholten Mustern wächst die Ersparnis deutlich:

T
84
O
79
B
66
E
69
O
79
R
82
T
84
O
79
B
66
E
69
O
79
R
82
T
84
O
79
B
66
E
69
1
w = +k = TT“ bekannt → w verlängern
2
w = T+k = OAusgabe 84 (T)·neu: TO = 256
3
w = O+k = BAusgabe 79 (O)·neu: OB = 257
4
w = B+k = EAusgabe 66 (B)·neu: BE = 258
5
w = E+k = OAusgabe 69 (E)·neu: EO = 259
6
w = O+k = RAusgabe 79 (O)·neu: OR = 260
7
w = R+k = TAusgabe 82 (R)·neu: RT = 261
8
w = T+k = OTO“ bekannt → w verlängern
9
w = TO+k = BAusgabe 256 (TO)·neu: TOB = 262
10
w = B+k = EBE“ bekannt → w verlängern
11
w = BE+k = OAusgabe 258 (BE)·neu: BEO = 263
12
w = O+k = ROR“ bekannt → w verlängern
13
w = OR+k = TAusgabe 260 (OR)·neu: ORT = 264
14
w = T+k = OTO“ bekannt → w verlängern
15
w = TO+k = BTOB“ bekannt → w verlängern
16
w = TOB+k = EAusgabe 262 (TOB)·neu: TOBE = 265
17
w = E+k = Ausgabe 69 (E)
Code-Strom: 84796669798225625826026269
Aufgebautes Wörterbuch: TO=256OB=257BE=258EO=259OR=260RT=261TOB=262BEO=263ORT=264TOBE=265
16 Zeichen Eingabe → 11 Codes Ausgabe.
Ergebnis (erstes Beispiel): 7 Zeichen Eingabe → 4 Codes Ausgabe 65 · 66 · 256 · 258. Je länger und wiederholter die Eingabe, desto stärker der Effekt.

⌨️ Implementierung in C

Bewusst vereinfacht (feste Codebreite, lineares Wörterbuch) — für die Lesbarkeit.

LZW-Encoder (vereinfacht, in C) · c
#include <stdint.h>
#include <string.h>

/* Vereinfachtes Wörterbuch: Schlüssel = String, Wert = Code.
   In der Praxis nutzt man eine Hash-Tabelle oder einen Trie. */
typedef struct {
    char    key[64];
    int     code;
} dict_entry_t;

/* Gibt den Code für die Zeichenkette w aus dem Wörterbuch zurück,
   oder -1 falls (noch) nicht vorhanden. */
int dict_lookup(dict_entry_t *dict, int size, const char *w);

/* Kodiert 'input' und schreibt die Codes über emit(). */
void lzw_encode(const char *input, void (*emit)(int code)) {
    dict_entry_t dict[4096];
    int dict_size = 256;                 /* 0..255 = alle Bytes */
    for (int i = 0; i < 256; i++) {      /* Start-Wörterbuch */
        dict[i].key[0] = (char)i;
        dict[i].key[1] = '\0';
        dict[i].code   = i;
    }

    char w[64] = "";
    for (const char *p = input; *p; p++) {
        char wk[64];
        snprintf(wk, sizeof wk, "%s%c", w, *p);   /* w + k */

        if (dict_lookup(dict, dict_size, wk) != -1) {
            strcpy(w, wk);                /* Muster verlängern  */
        } else {
            emit(dict_lookup(dict, dict_size, w));        /* code(w) */
            strcpy(dict[dict_size].key, wk);              /* neuer Eintrag */
            dict[dict_size].code = dict_size;
            dict_size++;
            w[0] = *p; w[1] = '\0';      /* w = k */
        }
    }
    if (w[0]) emit(dict_lookup(dict, dict_size, w));      /* Rest ausgeben */
}

ℹ️ Zur Verständlichkeit mit fester Codebreite und ohne Clear-Code. Echte GIF-Encoder nutzen variable Bitbreiten und Sub-Blocks.

LZW-Decoder (Kerngedanke) · c
/* Pseudocode-nahe C-Skizze des Decoders */
void lzw_decode(const int *codes, int n, void (*emit_str)(const char *)) {
    char dict[4096][64];
    int dict_size = 256;
    for (int i = 0; i < 256; i++) { dict[i][0] = (char)i; dict[i][1] = 0; }

    char prev[64];
    strcpy(prev, dict[codes[0]]);
    emit_str(prev);

    for (int i = 1; i < n; i++) {
        char entry[64];
        if (codes[i] < dict_size) {
            strcpy(entry, dict[codes[i]]);          /* Code bekannt */
        } else {
            /* Sonderfall KwKwK: Code = prev + erstes Zeichen von prev */
            snprintf(entry, sizeof entry, "%s%c", prev, prev[0]);
        }
        emit_str(entry);

        /* neuer Eintrag = prev + erstes Zeichen von entry */
        snprintf(dict[dict_size++], 64, "%s%c", prev, entry[0]);
        strcpy(prev, entry);
    }
}

ℹ️ Der Decoder baut dasselbe Wörterbuch auf — ein Sonderfall (KwKwK) tritt auf, wenn ein Code gelesen wird, der gerade erst entstehen würde.

🖼️ LZW in GIF

GIF erweitert das Grundverfahren um Steuercodes und variable Bitbreiten.

LZW-Besonderheiten in GIF

AspektBedeutung
LZW Minimum Code Size1 Byte vor den Daten — Bittiefe der Palette (min. 2)
Clear-CodeSetzt das Wörterbuch zurück (= 2^minCodeSize)
End-of-InformationMarkiert das Ende des Bildes (= Clear-Code + 1)
Variable CodelängeStartet bei minCodeSize+1 Bit, wächst bis 12 Bit
Sub-BlocksCode-Strom in Blöcke à max. 255 Byte, mit Längen-Präfix

Mehr zum Dateiaufbau auf der GIF-Seite.

💡 Wissenswert

LZW war bis 2003/2004 durch Unisys-Patente geschützt — der Streit darum war der Auslöser für die Entwicklung von PNG.
Der Decoder braucht das Wörterbuch nicht übertragen zu bekommen: Er rekonstruiert es deterministisch aus dem Code-Strom.
Der berüchtigte „KwKwK“-Sonderfall: Manchmal verweist ein Code auf einen Eintrag, der erst in genau diesem Schritt entsteht — der Decoder muss ihn vorausberechnen.
LZW steckt auch in TIFF, PDF und im Unix-Tool `compress` (.Z-Dateien).
← Zurück zur Übersicht