Huffmanův kód

Aktuální verze stránky ještě nebyla zkontrolována zkušenými přispěvateli a může se výrazně lišit od verze recenzované 8. ledna 2019; kontroly vyžadují 27 úprav .

Huffmanův algoritmus  je chamtivý algoritmus pro optimální prefixové kódování abecedy s minimální redundancí . Byl vyvinut v roce 1952 postgraduálním studentem MIT Davidem Huffmanem při psaní semestrální práce . V současné době se používá v mnoha programech pro kompresi dat .

Na rozdíl od Shannon-Fano algoritmu zůstává Huffmanův algoritmus vždy optimální pro sekundární abecedy m 2 s více než dvěma znaky.

Tato metoda kódování se skládá ze dvou hlavních kroků:

  1. Konstrukce optimálního kódového stromu.
  2. Vytvoření mapování kód-symbol na základě vytvořeného stromu.

Klasický Huffmanův algoritmus

Myšlenka algoritmu je následující: se znalostí pravděpodobnosti výskytu znaků ve zprávě je možné popsat postup pro konstrukci kódů s proměnnou délkou sestávajících z celého čísla bitů. Znakům je pravděpodobnější přiřadit kratší kódy. Huffmanovy kódy mají vlastnost prefix (to znamená, že žádné kódové slovo není prefixem jiného), což umožňuje jejich jednoznačné dekódování.

Klasický Huffmanův algoritmus přijímá tabulku četností znaků ve zprávě jako vstup. Dále je na základě této tabulky zkonstruován Huffmanův kódovací strom (H-strom). [jeden]

  1. Znaky vstupní abecedy tvoří seznam volných uzlů. Každý list má váhu, která se může rovnat buď pravděpodobnosti, nebo počtu výskytů znaku ve zprávě, která má být komprimována.
  2. Jsou vybrány dva volné uzly stromu s nejmenší váhou.
  3. Jejich rodič je vytvořen s váhou rovnou jejich celkové hmotnosti.
  4. Rodič je přidán do seznamu volných uzlů a jeho dva potomci jsou z tohoto seznamu odstraněni.
  5. Jeden oblouk vycházející z rodiče je nastaven na bit 1, druhý na bit 0. Bitové hodnoty větví pocházejících z kořene nezávisí na vahách potomků.
  6. Kroky počínaje druhým se opakují, dokud v seznamu volných uzlů nezůstane pouze jeden volný uzel. Bude považován za kořen stromu.

Řekněme, že máme následující tabulku absolutních frekvencí:

Symbol ALE B V G D
Absolutní frekvence patnáct 7 6 6 5

Tento proces si lze představit jako vytvoření stromu , jehož kořenem je symbol se součtem pravděpodobností kombinovaných symbolů získaných kombinací symbolů z posledního kroku, jeho n 0 potomky jsou symboly z předchozího kroku atd. .

Abychom určili kód pro každý ze znaků obsažených ve zprávě, musíme přejít od listu stromu odpovídajícího aktuálnímu znaku k jeho kořenu, přičemž při pohybu po větvích stromu shromažďujeme bity (první větev v cestě odpovídá nejméně významnému bitu). Takto získaná sekvence bitů je kód daného znaku, zapsaný v obráceném pořadí.

Pro danou tabulku znaků budou Huffmanovy kódy vypadat takto.

Symbol ALE B V G D
Kód 0 100 101 110 111

Vzhledem k tomu, že žádný z přijatých kódů není prefixem druhého, lze je při čtení ze streamu jednoznačně dekódovat. Kromě toho je nejčastější symbol zprávy A kódován nejmenším počtem bitů a nejvzácnější symbol D je kódován největším.

V tomto případě bude celková délka zprávy sestávající ze symbolů uvedených v tabulce 87 bitů (průměrně 2,2308 bitů na symbol). Při použití jednotného kódování by celková délka zprávy byla 117 bitů (přesně 3 bity na znak). Všimněte si, že entropie zdroje, který nezávisle generuje symboly se zadanými frekvencemi, je ~2,1858 bitů na symbol, což je redundance Huffmanova kódu konstruovaného pro takový zdroj, chápaná jako rozdíl mezi průměrným počtem bitů na symbol a entropie, je menší než 0,05 bitu na symbolu.

Klasický Huffmanův algoritmus má řadu významných nevýhod. Za prvé, aby bylo možné obnovit obsah komprimované zprávy, musí dekodér znát frekvenční tabulku používanou kodérem. Proto je délka komprimované zprávy zvýšena o délku frekvenční tabulky, která má být odeslána před daty, což může popřít jakoukoli snahu o komprimaci zprávy. Kromě toho potřeba mít kompletní statistiku frekvence před zahájením skutečného kódování vyžaduje dva průchody zprávou: jeden pro vytvoření modelu zprávy (tabulka frekvencí a H-strom), druhý pro skutečné kódování. Za druhé, redundance kódování zmizí pouze v těch případech, kdy pravděpodobnosti zakódovaných symbolů jsou převrácené mocniny 2. Za třetí, pro zdroj s entropií nepřesahující 1 (například pro binární zdroj), přímá aplikace Huffmanova kódu je zbytečné.

Adaptivní komprese

Adaptivní komprese vám umožňuje nepřenášet model zprávy spolu s ním a omezit se na jeden průchod zprávou jak při kódování, tak při dekódování.

Při vytváření adaptivního Huffmanova kódovacího algoritmu vznikají největší potíže při vývoji postupu pro aktualizaci modelu o další znak. Teoreticky by bylo možné do této procedury jednoduše vložit kompletní konstrukci Huffmanova kódovacího stromu, nicméně takový kompresní algoritmus by měl nepřijatelně nízký výkon, protože sestavení H-stromu je příliš pracné a je nerozumné to dělat při zpracování každá postava. Naštěstí existuje způsob, jak upravit již existující H-strom, aby odrážel zpracování nové postavy. Nejznámější přestavbové algoritmy jsou Voller-Gallagher-Knuth (FGK) algoritmus a Witterův algoritmus.

Všechny algoritmy pro přestavbu stromu při čtení dalšího znaku zahrnují dvě operace:

Prvním je zvýšení hmotnosti uzlů stromu. Nejprve zvýšíme váhu listu odpovídající čtenému znaku o jednu. Poté zvýšíme váhu rodiče, aby byla v souladu s novými váhami dětí. Tento proces pokračuje, dokud se nedostaneme ke kořeni stromu. Průměrný počet přírůstků váhy se rovná průměrnému počtu bitů potřebných k zakódování znaku.

Druhá operace - permutace uzlů stromu - je vyžadována, když zvýšení hmotnosti uzlu vede k porušení vlastnosti uspořádání, to znamená, že zvýšená hmotnost uzlu je větší než váha následujícího uzlu v objednat. Pokud budeme pokračovat ve zpracování přírůstku hmotnosti a budeme se pohybovat směrem ke kořeni stromu, pak strom již nebude Huffmanovým stromem.

Aby se zachovalo pořadí kódovacího stromu, algoritmus funguje následovně. Nechť je nová zvýšená hmotnost uzlu W+1. Poté se začneme po seznamu pohybovat ve směru rostoucí váhy, dokud nenajdeme poslední uzel s váhou W. Přeuspořádejme mezi sebou v seznamu aktuální a nalezené uzly, čímž obnovíme pořadí ve stromu (v tomto případě rodiče každého z uzlů se také změní). Tím je operace swap dokončena.

Po permutaci pokračuje operace zvyšování hmotnosti uzlů dále. Další uzel, kterému algoritmus zvýší váhu, je nový rodič uzlu, jehož zvýšení hmotnosti způsobilo permutaci.

Kanonické Huffmanovy kódy

Problémem konvenčního Huffmanova kompresního algoritmu je nedeterminismus. Pro podobné sekvence mohou vzniknout různé stromy a jeden strom bez řádné serializace může odpovídat různým sekvencím. Aby se zabránilo použití kanonických Huffmanových kódů.

Tento algoritmus nevytváří Huffmanův strom [2] .

Skládá se ze dvou fází:

  1. Výpočet délky kódu pro nějaký znak
  2. Skládání kódu.

Výpočet délky

  1. Vypočítejte frekvenci pro každý znak
  2. Seřaďte je v lexikografickém pořadí.
  3. Zapišme frekvenci každého písmene do pole.
  4. Vlevo přiřadíme pole stejné délky, ale se sériovými čísly z pole vpravo. Levé pole se získá jako seznam ukazatelů na prvky pravé strany.
  5. Na levé straně vytvoříme nerostoucí pyramidu . Ale halda nebude podle hodnoty prvků pole, ale podle hodnoty odkazovaného prvku pole.
  6. Prvek nejvíce vlevo ukazuje na znak z pravého pole s nejnižší frekvencí. Dá se odstranit takto:
    1. Nedotýkejte se pravé poloviny
    2. Nahradíme první prvek pole prvkem zcela vpravo levého pole, čímž se údajně posune hranice separace.
    3. Zkontrolujeme podmínky pro správnost pyramidy, pokud je něco špatně, musíme opakovat „hipizaci“.
    4. První prvek z levé strany pole je odstraněn a dříve odstraněný prvek je zkombinován. Součet jejich frekvencí je zapsán na hranici mezi levým a pravým polem.
    5. Místo smazaného prvku na levé straně se zapíše index pole tam, kde byl v posledním kroku přidán součet frekvencí.
    6. Vzhledem k tomu, že byly zkombinovány dva prvky, je nutné změnit hodnoty těchto prvků pole s odkazem na rodiče, kam byly vloženy.
  7. Opakujeme, v hromadě nezůstane 1 prvek.
  8. Na pravé straně pole máme odkazy na prvky, které kombinují 2 znaky. Proto procházíme polem po odkazech a zvyšujeme úroveň ponoření.
  9. Počet kliknutí na odkazy bude odpovídat délce Huffmanova kódu.

Kompilace kódu

  1. Uspořádejte prvky v lexikografickém pořadí.
  2. Udělejme tabulku skládající se z bloků, počínaje největší délkou kódu. Každý blok bude obsahovat prvky se stejnou délkou kódu.
  3. Úplně první znak tabulky je zakódován nulami.
  4. V každém bloku budou znaky v lexikografickém pořadí.
  5. Kódy v bloku budou binární a budou se lišit o 1.
  6. Při přechodu na další blok jsou kódové bity nejnovějšího znaku odříznuty a přidána 1.

Bigramový model

Existuje varianta Huffmanova algoritmu, který používá kontext. V tomto případě je velikost kontextu rovna jedné (bigram - dva znaky, trigram - tři atd.). Toto je metoda konstrukce předponového kódu pro modely vyššího řádu, který již není zdrojem bez paměti. Použije výsledek operace (předchozí operace) na předchozí písmeno spolu s aktuálním písmenem. Je postaven na bázi Markovova řetězce s hloubkou závislosti . [3]

Algoritmus

  1. Tabulka je konstruována ve formě čtverce - rozdělení pravděpodobnosti na bigramech. Okamžitě se vypočítá počáteční schéma, s jehož pomocí bude zakódováno pouze první písmeno. Řádky v tabulce jsou například předchozí písmena a sloupce jsou aktuální.
  2. Pravděpodobnosti se počítají pro kódové stromy pro kontexty.
  3. Délkové kontexty slouží k sestavení zbývajících kódových stromů, s jejichž pomocí budou zakódovány všechny ostatní znaky (kromě prvního).
  4. Provede se kódování, první znak se zakóduje podle výchozího schématu, všechny následující znaky se zakódují na základě kódových stromů pro kontexty (předchozí znak).

Dekódování se provádí podobným způsobem: ze schématu počátečního kódu získáme první kontext a poté přejdeme do odpovídajícího stromu kódu. Navíc dekodér potřebuje tabulku rozdělení pravděpodobnosti.

Příklad

Řekněme, že zpráva, která má být zakódována, je „abcabcabc“ . Tabulku frekvence symbolů známe předem (na základě jiných dat, jako jsou statistiky slovníku).

A b C Součet
A
b
C

Máme výchozí schéma: . Seřaďte sestupně: a vytvořte strom Huffmanových kódů.

Pro kontext " a " máme:

Pro kontext " b " máme:

Pro kontext " c " máme:

Poznámka: zde p(x, y) není rovno p(y, x) .

Vytváříme kódové stromy pro každý kontext. Provádíme kódování a máme zakódovanou zprávu: (00, 10, 01, 11, 10, 01, 11, 10, 01).

Přetečení

Jak kompresní algoritmus běží, váha uzlů v Huffmanově kódovacím stromě neustále roste. První problém nastává, když váha kořene stromu začne převyšovat kapacitu buňky, ve které je uložen. Obvykle se jedná o 16bitovou hodnotu, a proto nemůže být větší než 65535. Druhý problém, který si zaslouží více pozornosti, může nastat mnohem dříve, když velikost nejdelšího Huffmanova kódu překročí kapacitu buňky, která se používá k jeho přenosu do výstupní proud. Dekodér se nestará o to, jak dlouho kód dekóduje, protože se pohybuje shora dolů po stromě kódování a vybírá jeden bit po druhém ze vstupního toku. Kodér na druhé straně musí začínat na listu stromu a pohybovat se nahoru ke kořenu a sbírat bity, které mají být přenášeny. To se obvykle děje s proměnnou typu integer, a když délka Huffmanova kódu překročí velikost typu integer v bitech, dojde k přetečení.

Lze dokázat, že Huffmanův kód pro zprávy se stejnou vstupní abecedou bude mít maximální délku, pokud frekvence symbolů tvoří Fibonacciho posloupnost . Zpráva s frekvencemi symbolů rovnými Fibonacciho číslům až Fib(18) je skvělý způsob, jak otestovat, jak funguje Huffmanův kompresní program.

Měřítko vah uzlů Huffmanova stromu

Vezmeme-li v úvahu výše uvedené, algoritmus aktualizace Huffmanova stromu by měl být změněn následujícím způsobem: jak se váha zvyšuje, je třeba zkontrolovat, zda dosáhl povoleného maxima. Pokud jsme dosáhli maxima, pak je třeba váhu „rozměřit“, obvykle tak, že váhu listů vydělíme celým číslem, například 2, a pak přepočítáme váhu všech ostatních uzlů.

Při dělení hmotnosti na polovinu však nastává problém spojený s tím, že po provedení této operace může strom změnit svůj tvar. To se vysvětluje tím, že při dělení celých čísel se zlomková část zahodí.

Správně organizovaný Huffmanův strom po změně měřítka může mít tvar, který se výrazně liší od původního. Je to proto, že změna měřítka vede ke ztrátě přesnosti ve statistikách. Se sběrem nových statistik však důsledky těchto „chyb“ prakticky mizí. Váhové škálování je poměrně nákladná operace, protože vede k nutnosti přestavět celý kódovací strom. Ale protože potřeba toho vzniká poměrně zřídka, můžete se s tím smířit.

Výhody škálování

Měřítko váhy uzlů stromu v určitých intervalech dává neočekávaný výsledek. I když při škálování dochází ke ztrátě statistické přesnosti, testy ukazují, že škálování vede k lepšímu výkonu komprese, než kdyby bylo škálování zpožděno. To lze vysvětlit tím, že současné symboly stlačitelného proudu jsou více „podobné“ svým blízkým předchůdcům než těm, které se vyskytovaly mnohem dříve. Změna měřítka má za následek snížení vlivu „starých“ symbolů na statistiku a zvýšení vlivu „nedávných“ symbolů na ni. To je velmi obtížné kvantifikovat, ale v zásadě má škálování pozitivní vliv na stupeň komprese informací. Experimenty se změnou měřítka v různých bodech procesu komprese ukazují, že stupeň komprese silně závisí na okamžiku změny měřítka hmotnosti, ale neexistuje žádné pravidlo pro výběr optimálního momentu změny měřítka pro program zaměřený na kompresi jakéhokoli typu informací.

Aplikace

Huffmanovo kódování je široce používáno v kompresi dat, včetně komprese fotografií a videa ( JPEG , MPEG ), v oblíbených archivátorech ( PKZIP , LZH atd.), v protokolech přenosu dat HTTP ( Deflate ), MNP5 a MNP7 a dalších.

Úpravy

V roce 2013 byla navržena modifikace Huffmanova algoritmu, která umožňuje kódování znaků se zlomkovým počtem bitů - ANS [4] [5] . Na základě této úpravy vznikly kompresní algoritmy Zstandard (Zstd, Facebook, 2015—2016) [6] a LZFSE (Apple, OS X 10.11, iOS 9, 2016) [7] [8] [9] [10] jsou implementovány .

Poznámky

  1. D. Mastryukov. Monitor 7-8.93 Archivováno 17. května 2018 na Wayback Machine
  2. Algoritmus je podrobně popsán s příklady v Managing Gigabytes od Witten, Moffat, Bell na straně 68.
  3. Shmuel T. Klein a Dana Shapira. Huffmanovo kódování s netříděnými frekvencemi (2008). Datum přístupu: 2. ledna 2016. Archivováno z originálu 4. března 2016.
  4. Archivovaná kopie . Získáno 2. ledna 2016. Archivováno z originálu 5. března 2016.
  5. Archivovaná kopie . Staženo 2. ledna 2016. Archivováno z originálu 11. září 2016.
  6. Facebook zveřejnil implementaci kompresního algoritmu Zstandard 1.0 Archivováno 2. září 2016 na Wayback Machine / Opennet.ru, 09/01/2016
  7. Apple zahájil implementaci algoritmu bezeztrátové komprese LZFSE
  8. Apple Open-Sourses svůj nový kompresní algoritmus LZFSE . Získáno 1. září 2016. Archivováno z originálu 11. září 2016.
  9. Komprese dat
  10. GitHub - lzfse/lzfse: Knihovna pro kompresi LZFSE a nástroj příkazového řádku . Získáno 1. září 2016. Archivováno z originálu 28. listopadu 2020.

Literatura

Odkazy