Schauen wir uns eine etwas andere Sichtweise der Huffman-Codierung an.
Angenommen, Sie haben ein Alphabet mit drei Symbolen, A, B und C, mit Wahrscheinlichkeiten von 0,5, 0,25 und 0,25. Da die Wahrscheinlichkeiten alle inverse Zweierpotenzen sind, hat dies einen optimalen Huffman-Code (dh er ist identisch mit der arithmetischen Codierung). Wir werden für dieses Beispiel den kanonischen Code 0, 10, 11 verwenden.
s
encode(s,A)encode(s,B)encode(s,C)=2s=4s+2=4s+3
Beginnen wir also mit dem Zustand 11 (der binär 1011 ist) und codieren Sie das Symbol B. Der neue Zustand ist 46, der binär 101110 ist. Wie Sie sehen können, ist dies der "alte" Zustand mit der am Ende hinzugefügten Sequenz 10. Wir haben im Wesentlichen die Bitfolge 10 "ausgegeben".
So weit, ist es gut.
[04,24)[24,34)[34,44)
Grundsätzlich multiplizieren wir hier alles mit dem gemeinsamen Nenner. Stellen Sie sich vor, der Zustand befand sich tatsächlich in Basis 4. Das Codieren eines Symbols B gibt tatsächlich die Ziffer 2 in dieser Basis aus, und das Codieren eines Symbols C gibt die Ziffer 3 in dieser Basis aus.
Symbol A ist jedoch etwas anders, da es in Basis 4 keine ganze Ziffer ist.
Stattdessen können wir uns das Alphabet als die Menge der Symbole A_0, A_1, B, C mit gleicher Wahrscheinlichkeit vorstellen. Dies hat wiederum einen optimalen Huffman-Code 00, 01, 10, 11. Oder wir können uns dies in Basis 4 vorstellen. Um ein Symbol zu codieren, tun wir einfach:
encode(s,A0)encode(s,A1)encode(s,B)encode(s,C)=4s+0=4s+1=4s+2=4s+3
A0A1
s
s′=⌊s2⌋
i=smod2
und dann .encode(s′,Ai)
Anhand unseres vorherigen Beispiels, , stellen wir fest, dass und und dann . Der neue Status ist 10101 in binär.s=11s′=5i=1encode(5,A1)=4×5+1=21
Dies erzeugt nicht genau die gleiche Bitausgabe wie die Huffman-Codierung, sondern eine Ausgabe mit der gleichen Länge. Und ich hoffe, Sie können sehen, dass dies auch einzigartig dekodierbar ist. Um ein Symbol zu dekodieren, nehmen wir den Rest, wenn er durch 4 geteilt wird. Wenn der Wert 2 oder 3 ist, ist das Symbol B bzw. C. Wenn es 0 oder 1 ist, ist das Symbol A, und dann können wir das Informationsbit zurücksetzen, indem wir den Zustand mit 2 multiplizieren und entweder 0 oder 1 addieren.
Das Schöne an diesem Ansatz ist, dass er sich natürlich auf die Bruchbitcodierung erstreckt, wenn der Zähler und / oder der Nenner der Wahrscheinlichkeiten keine Zweierpotenzen sind. Angenommen, wir haben zwei Symbole, A und B, wobei die Wahrscheinlichkeit von A und die Wahrscheinlichkeit von B . Dann können wir ein Symbol codieren mit:3525
encode(s,A0)encode(s,A1)encode(s,A2)encode(s,B0)encode(s,B1)=5s+0=5s+1=5s+2=5s+3=5s+4
Um das Symbol A zu codieren, nehmen wir und und dann .i=smod3codieren(s',Ai)s′=⌊s3⌋i=smod3encode(s′,Ai)
Dies entspricht einer arithmetischen Codierung. Es ist eigentlich eine Familie von Methoden, die als asymmetrische Zahlensysteme bekannt sind und in den letzten Jahren von Jarek Duda entwickelt wurden. Die Bedeutung des Namens sollte offensichtlich sein: Um ein Symbol mit der Wahrscheinlichkeit zu codieren , stehlen Sie konzeptionell eine Basis-p-Ziffer aus dem Status und fügen dann eine Basis-q-Ziffer hinzu. Die Asymmetrie ergibt sich aus der Interpretation des Zustands als Zahl in zwei verschiedenen Grundlagen.pq
Der Grund, warum es sich um eine Familie von Codierungsmethoden handelt, ist, dass das, was wir hier gesehen haben, für sich genommen unpraktisch ist. Es sind einige Änderungen erforderlich, um die Tatsache zu berücksichtigen, dass Sie wahrscheinlich keine Ganzzahlen mit unendlicher Genauigkeit haben, um die Statusvariable effizient zu manipulieren, und es gibt verschiedene Möglichkeiten, wie Sie dies erreichen können. Die arithmetische Codierung hat natürlich ein ähnliches Problem mit der Genauigkeit ihres Zustands.
Praktische Varianten sind rANS ("r" bedeutet "Verhältnis") und tANS ("tabellengesteuert").
ANS hat einige interessante Vorteile gegenüber der arithmetischen Codierung, sowohl in der Praxis als auch in der Theorie:
- Im Gegensatz zur arithmetischen Codierung ist der "Zustand" eher ein einzelnes Wort als ein Wortpaar.
- Darüber hinaus haben ein ANS-Codierer und sein entsprechender Decodierer identische Zustände und ihre Operationen sind vollständig symmetrisch. Dies eröffnet einige interessante Möglichkeiten, z. B. dass Sie verschiedene Ströme codierter Symbole verschachteln können und alles perfekt synchronisiert ist.
- Praktische Implementierungen müssen natürlich Informationen "ausgeben", während Sie fortfahren, und sie nicht nur in einer großen Ganzzahl sammeln, die am Ende geschrieben werden soll. Die Größe der "Ausgabe" kann jedoch als Gegenleistung für (normalerweise bescheidene) Komprimierungsverluste konfiguriert werden. Wenn also arithmetische Codierer jeweils ein Bit ausgeben müssen, kann ANS jeweils ein Byte oder ein Nybble ausgeben. Dies gibt Ihnen einen direkten Kompromiss zwischen Geschwindigkeit und Komprimierung.
- Es scheint auf Hardware der aktuellen Generation ungefähr so schnell zu sein wie die binäre arithmetische Codierung und daher mit der Huffman-Codierung konkurrenzfähig zu sein. Dies macht es viel schneller als die arithmetische Codierung mit großem Alphabet und ihre Varianten (z. B. Bereichscodierung).
- Es scheint patentfrei zu sein.
Ich glaube nicht, dass ich jemals wieder arithmetisch codieren werde.