Huffman-codering. Huffman-code
Huffman-codering is een eenvoudig algoritme voor het construeren van codes variabele lengte met een minimale gemiddelde lengte. Dit zeer populaire algoritme dient als basis voor veel computertekst- en tekstcompressieprogramma's. grafische informatie. Sommigen van hen gebruiken het Huffman-algoritme rechtstreeks, terwijl anderen het beschouwen als een stap in een compressieproces op meerdere niveaus. De Huffman-methode produceert perfecte compressie (dat wil zeggen, comprimeert de gegevens tot de entropie ervan) als de kansen van de karakters precies gelijk zijn aan negatieve machten van 2. Het algoritme begint de codeboom van onder naar boven op te bouwen en glijdt vervolgens door de boom naar beneden. bouw elke individuele code van rechts naar links (van het minst significante bit tot het meest significante bit). Sinds het werk van D. Huffman in 1952 is dit algoritme het onderwerp geweest van vele onderzoeken. (De laatste uitspraak uit §3.8.1 laat dat zien beste code variabele lengte kan soms worden verkregen zonder dit algoritme.)
Het algoritme begint met het samenstellen van een lijst met alfabetische tekens in aflopende volgorde van hun waarschijnlijkheid. Vervolgens wordt vanaf de wortel een boom gebouwd, waarvan de bladeren deze symbolen zijn. Dit gebeurt in stappen, waarbij elke stap de twee symbolen met de laagste waarschijnlijkheid selecteert, deze bovenaan de gedeeltelijke boom toevoegt, ze uit de lijst verwijdert en ze vervangt door een hulpsymbool dat deze twee symbolen vertegenwoordigt. Aan het hulpsymbool wordt een waarschijnlijkheid toegewezen die gelijk is aan de som van de kansen die bij deze symboolstap zijn geselecteerd. Wanneer de lijst wordt teruggebracht tot één enkel hulpsymbool dat het hele alfabet vertegenwoordigt, wordt de boom compleet verklaard. Het algoritme eindigt door de boom af te dalen en de codes van alle symbolen te construeren.
Dit algoritme wordt het beste geïllustreerd door eenvoudig voorbeeld. Er zijn vijf symbolen met de kansen gegeven in Fig. 1.3a.
Rijst. 1.3. Huffman-codes.
De karakters zijn gekoppeld in de volgende volgorde:
1. wordt gecombineerd met , en beide worden vervangen door het gecombineerde symbool met waarschijnlijkheid 0,2;
2. Er zijn nog vier symbolen over, met een waarschijnlijkheid van 0,4, en ook met een waarschijnlijkheid van 0,2. We selecteren willekeurig en , combineren ze en vervangen ze door een hulpsymbool met waarschijnlijkheid 0,4;
3. Nu zijn er drie symbolen met respectievelijk kansen 0,4, 0,2 en 0,4. We selecteren en combineren symbolen en tot een hulpsymbool met waarschijnlijkheid 0,6;
4. Ten slotte combineren we de twee overgebleven symbolen en vervangen ze door kans 1.
De boom is gebouwd. Het wordt getoond in Afb. 1.3a, “liggend op zijn kant”, met de wortel aan de rechterkant en vijf bladeren aan de linkerkant. Om codes toe te wijzen, wijzen we voor elk paar willekeurig bit 1 toe aan de bovenste tak en bit 0 aan de onderste tak van de boom. Als resultaat krijgen we de volgende codes: 0, 10, 111, 1101 en 1100. De verdeling van bits langs de randen is willekeurig.
De gemiddelde lengte van deze code is bits/symbool. Het is heel belangrijk dat er veel Huffman-codes zijn. Sommige stappen van het algoritme zijn willekeurig gekozen, omdat dat zo was meer karakters met minimale waarschijnlijkheid. In afb. Figuur 1.3b laat zien hoe de karakters op verschillende manieren kunnen worden gecombineerd om een andere Huffman-code te produceren (11, 01, 00, 101 en 100). De gemiddelde lengte is gelijk aan bits/symbool zoals in de vorige code.
Voorbeeld: Gegeven 8 symbolen A, B, C, D, E, F, G en H met kansen 1/30, 1/30, 1/30, 2/30, 3/30, 5/30, 5/30 en 12/ dertig. In afb. 1.4a,b,c tonen drie Huffman-codebomen van hoogte 5 en 6 voor dit alfabet.
Rijst. 1.4. Drie Huffman-bomen voor acht karakters.
De gemiddelde lengte van deze codes (in bits per symbool) is
Voorbeeld: Op afb. Figuur 1.4d toont nog een boom met hoogte 4 voor de acht karakters uit het vorige voorbeeld. Uit de volgende analyse blijkt dat de bijbehorende code met variabele lengte slecht is, ook al is de lengte minder dan 4.
(Analyse) Na het combineren van de symbolen A, B, C, D, E, F en G zijn de resterende symbolen ABEF (met een waarschijnlijkheid van 10/30), CDG (met een waarschijnlijkheid van 8/30) en H (met een waarschijnlijkheid van een waarschijnlijkheid van 12/30). De karakters ABEF en CDG hebben de laagste waarschijnlijkheid, dus moesten ze worden samengevoegd, maar in plaats daarvan werden de karakters CDG en H samengevoegd. De resulterende boom is geen Huffman-boom.
Door enige willekeur in de constructie van de boom kunnen we dus verschillende Huffman-codes met dezelfde gemiddelde lengte verkrijgen. Dit roept de vraag op: “Wat is de beste Huffman-code die voor een bepaald alfabet is samengesteld?” Het antwoord zal eenvoudig zijn, maar niet voor de hand liggend: de beste code is degene met de minste variantie.
Dispersie laat zien hoeveel de lengtes van individuele codes afwijken van hun gemiddelde waarde (dit concept wordt uitgelegd in elk statistiekhandboek). De variantie van code 1.3a is gelijk aan , en voor code 1.3b .
Code 1.3b heeft de voorkeur (dit wordt hieronder toegelicht). Een zorgvuldige blik op de bomen laat zien hoe we degene kunnen kiezen die we nodig hebben. Vijg op de boom 1.3a gaat het symbool samen met het symbool, terwijl in Fig. 1.3b fuseert met . De regel zou zijn: als er meer dan twee knooppunten met de laagste waarschijnlijkheid in de boom zijn, moeten de karakters met de hoogste en laagste waarschijnlijkheid worden gecombineerd; dit vermindert de algehele codevariantie.
Als de encoder gewoon schrijft gecomprimeerd bestand naar schijf, dan doet codevariantie er niet toe. Huffman-codes met lage variantie hebben alleen de voorkeur als de encoder dit gecomprimeerde bestand via communicatielijnen verzendt. In dit geval zorgt een code met een hoge variantie ervoor dat de encoder bits met een variabele snelheid genereert. Normaal gesproken worden gegevens verzonden via communicatiekanalen met constante snelheid, dus de encoder gebruikt een buffer. De bits van het gecomprimeerde bestand worden tijdens het genereren gebufferd en met een constante snelheid in het kanaal ingevoerd voor verzending. Het is gemakkelijk in te zien dat code zonder variantie met een constante snelheid in de buffer wordt ingevoerd, dus er zal een korte buffer nodig zijn, en voor grote codevariantie zal het gebruik van een lange buffer nodig zijn.
In de informatiecompressieliteratuur is soms de volgende uitspraak te vinden: de Huffman-codelengte van een symbool is altijd maximaal . Hoewel deze bewering in veel voorbeelden waar is, is ze in het algemeen niet waar. Ik ben Guy Blalock zeer dankbaar, die mij op dit feit heeft gewezen en mij een voorbeeld heeft gegeven van de code uit de tabel. 1.5. De tweede regel van deze tabel bevat een teken met een codelengte van 3 bits, terwijl .
De lengte van de code van een symbool hangt uiteraard af van de waarschijnlijkheid ervan. Het hangt echter impliciet ook af van de alfabetgrootte. In een groot alfabet zijn de symboolkansen klein, dus Huffman-codes zijn lang. In een klein alfabet wordt het tegenovergestelde beeld waargenomen. Intuïtief is dit logisch omdat voor een klein alfabet slechts een paar codes nodig zijn, dus ze zijn allemaal kort, maar een groot alfabet vereist veel codes, en sommige daarvan moeten lang zijn.
Rijst. 1.6. Huffman-code voor het Engelse alfabet.
In afb. Figuur 1.6 toont de Huffman-code voor alle 26 letters van het Engelse alfabet.
Het geval van een alfabet waarin de symbolen even waarschijnlijk zijn, is bijzonder interessant. In afb. Figuur 1.7 toont Huffman-codes voor een alfabet met 5, 6, 7 en 8 even waarschijnlijke tekens. Als de alfabetgrootte een macht van 2 is, zijn de resultaten eenvoudigweg codes met een vaste lengte. In andere gevallen liggen de codes vrij dicht bij codes met een vaste lengte. Dit betekent dat er geen voordeel is aan het gebruik van codes met variabele lengte. In tafel 1.8 toont de codes, hun gemiddelde lengte en varianties.
Rijst. 1.7. Huffman codeert met gelijke kansen.
Het feit dat gegevens met even waarschijnlijke tekens niet worden gecomprimeerd door de Huffman-methode kan betekenen dat de reeksen van dergelijke tekens volledig willekeurig zijn. Er zijn echter voorbeelden van tekenreeksen waarin alle tekens even waarschijnlijk zijn, maar niet willekeurig, en kunnen worden gecomprimeerd. Een goed voorbeeld is een reeks waarin elk teken in lange reeksen voorkomt. Zo'n string kan worden gecomprimeerd met behulp van de RLE-methode, maar niet met de Huffman-methode. (De lettercombinatie RLE staat voor ‘run-length encoding’, d.w.z. ‘run-length encoding’. Deze eenvoudige methode is op zichzelf niet erg effectief, maar kan worden gebruikt in compressie-algoritmen met veel fasen, zie
Met andere woorden, we zullen beetje bij beetje vanuit de invoerstroom in de codevariabele van links naar de code duwen< base. При этом на каждой итерации будем увеличивать переменную length на 1 (т.е. продвигаться вниз по дереву). Цикл в (2) остановится когда мы определим длину кода (уровень в дереве, на котором находится искомый символ). Остается лишь определить какой именно символ на этом уровне нам нужен.
Stel dat de lus in (2) na verschillende iteraties stopt. In dit geval is de uitdrukking (code - basis) het serienummer van het gewenste knooppunt (symbool) op lengteniveau. Het eerste knooppunt (symbool) dat zich op lengteniveau in de boom bevindt, bevindt zich in de symb-array op index offs. Maar we zijn niet geïnteresseerd in het eerste teken, maar in het genummerde teken (code - basis). Daarom is de index van het gezochte symbool in de symb-array gelijk aan (offs + (code - base)). Met andere woorden: het gewenste symbool symbol=symb + code - base].
Laten we een specifiek voorbeeld geven. Met behulp van het beschreven algoritme decoderen we het bericht Z /.
Z / = "0001 1 00001 00000 1 010 011 1 011 1 010 011 0001 1 0010 010 011 011 1 1 1 010 1 1 1 0010 011 0011 1 0011 0011 011 1 0 1 1"
- code = 0, lengte = 1
- code = 0< base = 1
code = 0<< 1 = 0
code = 0 + 0 = 0
lengte = 1 + 1 = 2
code = 0< base = 2
code = 0<< 1 = 0
code = 0 + 0 = 0
lengte = 2 + 1 = 3
code = 0< base = 2
code = 0<< 1 = 0
code = 0 + 1 = 1
lengte = 3 + 1 = 4
code = 1 = basis = 1 - symbool = symb = 2 + code = 1 - basis = 1] = symb = A
- code = 1, lengte = 1
- code = 1 = basis = 1
- symbool = symb = 7 + code = 1 - basis = 1] = symb = H
- code = 0, lengte = 1
- code = 0< base = 1
code = 0<< 1 = 0
code = 0 + 0 = 0
lengte = 1 + 1 = 2
code = 0< base = 2
code = 0<< 1 = 0
code = 0 + 0 = 0
lengte = 2 + 1 = 3
code = 0< base = 2
code = 0<< 1 = 0
code = 0 + 0 = 0
lengte = 3 + 1 = 4
code = 0< base = 1
code = 0<< 1 = 0
code = 0 + 1 = 1
lengte = 4 + 1 = 5
code = 1 > basis = 0 - symbool = symb = 0 + code = 1 - basis = 0] = symb = F
Dus hebben we de eerste 3 karakters gedecodeerd: A, H, F. Het is duidelijk dat we met dit algoritme precies het bericht S zullen ontvangen.
Berekening van codelengtes
Om een bericht te coderen, moeten we de tekencodes en hun lengte kennen. Zoals opgemerkt in de vorige sectie, worden canonieke codes volledig bepaald door hun lengte. Onze hoofdtaak is dus het berekenen van de lengte van de codes.
Het blijkt dat voor deze taak in de overgrote meerderheid van de gevallen niet expliciet een Huffman-boom hoeft te worden geconstrueerd. Bovendien zijn algoritmen die een interne (niet expliciete) representatie van de Huffman-boom gebruiken veel efficiënter in termen van snelheid en geheugenkosten.
Tegenwoordig zijn er veel effectieve algoritmen voor het berekenen van codelengtes (,). We zullen ons beperken tot het beschouwen van slechts één ervan. Dit algoritme is vrij eenvoudig, maar desondanks is het erg populair. Het wordt gebruikt in programma's zoals zip, gzip, pkzip, bzip2 en vele andere.
Laten we terugkeren naar het algoritme voor het construeren van de Huffman-boom. Bij elke iteratie voerden we een lineaire zoektocht uit naar de twee knooppunten met het laagste gewicht. Het is duidelijk dat een prioriteitswachtrij zoals een piramide (minimaal) hiervoor geschikter is. Het knooppunt met het minste gewicht heeft de hoogste prioriteit en staat bovenaan de piramide. Laten we dit algoritme presenteren.
Laten we alle gecodeerde symbolen in de piramide opnemen.
We zullen achtereenvolgens 2 knooppunten uit de piramide halen (dit zijn de twee knooppunten met het minste gewicht).
Laten we een nieuw knooppunt vormen en daaraan, als kinderen, twee knooppunten uit de piramide koppelen. In dit geval stellen we het gewicht van het gevormde knooppunt gelijk aan de som van de gewichten van de onderliggende knooppunten.
Laten we het gevormde knooppunt in de piramide opnemen.
Als er meer dan één knooppunt in de piramide is, herhaal dan 2-5.
We gaan ervan uit dat voor elk knooppunt een verwijzing naar zijn ouder is opgeslagen. Aan de wortel van de boom stellen we deze pointer gelijk aan NULL. Laten we nu een bladknooppunt (symbool) selecteren en, door de opgeslagen wijzers te volgen, klimmen we in de boom totdat de volgende wijzer NULL wordt. De laatste voorwaarde betekent dat we de wortel van de boom hebben bereikt. Het is duidelijk dat het aantal overgangen van niveau naar niveau gelijk is aan de diepte van het bladknooppunt (symbool), en dus de lengte van zijn code. Door op deze manier alle knooppunten (symbolen) te doorlopen, krijgen we de lengte van hun codes.
Maximale codelengte
In de regel wordt bij het coderen de zogenaamde codeboek (codeboek), een eenvoudige datastructuur, in wezen twee arrays: één met lengtes, de andere met codes. Met andere woorden, de code (als een bitstring) wordt opgeslagen in een geheugenlocatie of register met een vaste grootte (meestal 16, 32 of 64). Om te voorkomen dat er een overflow ontstaat, moeten we er zeker van zijn dat de code in het register past.
Het blijkt dat op een alfabet met N-tekens de maximale codegrootte maximaal (N-1) bits lang kan zijn. Met andere woorden, met N=256 (een gebruikelijke optie) kunnen we een code krijgen van 255 bits lang (hoewel het bestand hiervoor erg groot moet zijn: 2.292654130570773*10^53~=2^177.259)! Het is duidelijk dat een dergelijke code niet in het register past en dat er iets mee gedaan moet worden.
Laten we eerst eens kijken onder welke omstandigheden overstroming optreedt. Laat de frequentie van het i-de symbool gelijk zijn aan het i-de Fibonacci-getal. Bijvoorbeeld: A-1, B-1, C-2, D-3, E-5, F-8, G-13, H-21. Laten we de overeenkomstige Huffman-boom construeren.
WORTEL /\ / \ / \ /\ H / \ / \ /\ G / \ / \ /\ F / \ / \ /\ E / \ / \ /\ D / \ / \ /\ C / \ / \ A B
Zo'n boom heet ontaarden. Om het te kunnen ontvangen, moeten de frequenties van de symbolen minstens evenveel groeien als de Fibonacci-getallen of zelfs sneller. Hoewel het in de praktijk met echte gegevens vrijwel onmogelijk is om zo'n boom te verkrijgen, is het heel eenvoudig om deze kunstmatig te genereren. Met dit gevaar moet in ieder geval rekening worden gehouden.
Dit probleem kan op twee aanvaardbare manieren worden opgelost. De eerste is gebaseerd op een van de eigenschappen van canonieke codes. Feit is dat in een canonieke code (bitstring) geen significante bits anders dan nul kunnen zijn. Met andere woorden: alle andere bits worden mogelijk helemaal niet opgeslagen, omdat ze zijn altijd nul. In het geval van N=256 hoeven we alleen de minst significante 8 bits van elke code op te slaan, ervan uitgaande dat alle andere bits gelijk zijn aan nul. Dit lost het probleem op, maar slechts gedeeltelijk. Dit zal zowel het coderen als het decoderen aanzienlijk compliceren en vertragen. Daarom wordt deze methode in de praktijk zelden gebruikt.
De tweede methode is het kunstmatig beperken van de lengte van de codes (tijdens de constructie of daarna). Deze methode is algemeen aanvaard, dus we zullen er dieper op ingaan.
Er zijn twee soorten algoritmen voor het beperken van de codelengte. Heuristisch (bij benadering) en optimaal. Algoritmen van het tweede type zijn behoorlijk complex om te implementeren en vereisen in de regel meer tijd en geheugen dan de eerste. De effectiviteit van een heuristisch beperkte code wordt bepaald door de afwijking ervan van de optimaal beperkte code. Hoe kleiner dit verschil, hoe beter. Het is vermeldenswaard dat dit verschil voor sommige heuristische algoritmen erg klein is ( , , ), en dat ze heel vaak optimale code genereren (hoewel ze niet garanderen dat dit altijd het geval zal zijn). Bovendien, omdat in de praktijk komt overflow uiterst zelden voor (tenzij er een zeer strikte beperking is op de maximale codelengte); bij een kleine alfabetgrootte is het beter om eenvoudige en snelle heuristische methoden te gebruiken.
We zullen kijken naar een vrij eenvoudig en zeer populair heuristisch algoritme. Het heeft zijn toepassing gevonden in programma's zoals zip, gzip, pkzip, bzip2 en vele andere.
Het probleem van het beperken van de maximale codelengte is gelijk aan het probleem van het beperken van de hoogte van de Huffman-boom. Merk op dat door de constructie elk niet-bladig knooppunt in een Huffman-boom precies twee kinderen heeft. Bij elke iteratie van ons algoritme zullen we de hoogte van de boom met 1 verminderen. Laat L dus de maximale codelengte (boomhoogte) zijn en we willen deze beperken tot L / < L. Laat RN i verder de meest rechtse bladknooppunt op niveau i, en LN i is het meest linkse.
Laten we beginnen te werken vanaf niveau L. Laten we knooppunt RNL verplaatsen naar de plaats van zijn ouder. Omdat knooppunten gaan in paren; we moeten een plaats vinden voor het knooppunt grenzend aan RN L. Om dit te doen, vinden we het niveau j dat het dichtst bij L ligt en dat bladknooppunten bevat, zodat j < (L-1). In plaats van LN j zullen we een niet-bladig knooppunt vormen en hieraan als kinderen het knooppunt LN j en het resterende ongepaarde knooppunt van niveau L koppelen. Pas dezelfde bewerking toe op alle resterende paren knooppunten op niveau L. Het is duidelijk dat we, door de knooppunten op deze manier te herverdelen, de hoogte van onze boom met 1 hebben verkleind. Nu is deze gelijk aan (L-1). Als nu L / < (L-1), dan zullen we hetzelfde doen met niveau (L-1), enz. totdat de vereiste limiet is bereikt.
Laten we terugkeren naar ons voorbeeld, waar L=5. Laten we de maximale codelengte beperken tot L / =4.
WORTEL /\ / \ / \ /\ H C E / \ / \ / \ / \ /\ A D G / \ / \ B F
Het is te zien dat in ons geval RN L = F, j=3, LNj = C. Eerst verplaatsen we het knooppunt RN L = F in de plaats van zijn ouders.
WORTEL /\ / \ / \ /\ H / \ / \ / \ / \ / \ / \ /\ /\ / \ / \ / \ / \ / \ / \ / \ / \ /\ /\ C E / \ / \ / \ / \ F A D G B(ongepaarde knoop)
Nu op plaats LN j = C Laten we een niet-bladknooppunt vormen.
WORTEL /\ / \ / \ /\ H E / \ / \ / \ / \ / \ / \ F A D G ? ? B(ongepaarde knoop) C(ongepaarde knoop)
Laten we twee ongepaarde aan het gevormde knooppunt koppelen: B En C.
WORTEL /\ / \ / \ /\ H / \ / \ / \ / \ / \ / \ / \ / \ / \ /\ /\ / \ / \ / \ / \ / \ / \ / \ / \ /\ /\ /\ E / \ / \ / \ / \ / \ / \ F A D G B C
Daarom hebben we de maximale codelengte beperkt tot 4. Het is duidelijk dat we door het wijzigen van de codelengtes een beetje aan efficiëntie hebben verloren. Het bericht S, gecodeerd met behulp van een dergelijke code, zal dus een grootte hebben van 92 bits, d.w.z. 3 bits meer vergeleken met minimaal redundante code.
Het is duidelijk dat hoe meer we de maximale codelengte beperken, hoe minder efficiënt de code zal zijn. Laten we eens kijken in hoeverre we de maximale codelengte kunnen beperken. Uiteraard niet korter.
Het berekenen van canonieke codes
Zoals we herhaaldelijk hebben opgemerkt, zijn de lengtes van de codes voldoende om de codes zelf te genereren. Laten we laten zien hoe dit kan worden gedaan. Laten we aannemen dat we de codelengtes al hebben berekend en hebben geteld hoeveel codes van elke lengte we hebben. Laat L de maximale codelengte zijn en T i het aantal codes met lengte i.
Laten we Si berekenen - de initiële waarde van de code met lengte i, voor alle i
SL = 0 (altijd)
S L-1 = (S L + T L) >> 1
S L-2 = (S L-1 + T L-1) >> 1
...
S 1 = 1 (altijd)
Voor ons voorbeeld: L = 5, T 1 .. 5 = (1, 0, 2,3, 2).
S 5 = 00000 bak = 0 dec
S 4 = (S 5 =0 + T 5 =2) >> 1 = (00010 bak >> 1) = 0001 bak = 1 dec
S 3 = (S 4 =1 + T 4 =3) >> 1 = (0100 bak >> 1) = 010 bak = 2 dec
S 2 = (S 3 =2 + T 3 =2) >> 1 = (100 bak >> 1) = 10 bak = 2 dec
S 1 = (S 2 =2 + T 2 =0) >> 1 = (10 bak >> 1) = 1 bak = 1 dec
Het is duidelijk dat S 5, S 4, S 3, S 1 precies karaktercodes zijn B, A, C, H. Wat deze symbolen gemeen hebben is dat ze allemaal op de eerste plaats komen, elk op hun eigen niveau. Met andere woorden: we hebben de initiële codewaarde voor elke lengte (of niveau) gevonden.
Laten we nu codes toewijzen aan de resterende symbolen. De code van het eerste teken op niveau i is Si, het tweede is S i + 1, het derde is S i + 2, enz.
Laten we de resterende codes voor ons voorbeeld opschrijven:
B= S 5 = 00000 bak | A= S 4 = 0001 bak | C= S 3 = 010 bak | H= S 1 = 1 bak |
F= S 5 + 1 = 00001 bak | D= S 4 + 1 = 0010 bak | E= S 3 + 1 = 011 bak | |
G= S 4 + 2 = 0011 bak |
Het is duidelijk dat we precies dezelfde codes hebben verkregen alsof we expliciet de canonieke Huffman-boom hadden gebouwd.
Overdracht van codeboom
Om een gecodeerd bericht te kunnen decoderen, moet de decoder dezelfde codeboom hebben (in een of andere vorm) die werd gebruikt tijdens het coderen. Daarom zijn we gedwongen om, samen met de gecodeerde gegevens, de bijbehorende codeboom op te slaan. Het is duidelijk dat hoe compacter het is, hoe beter.
Er zijn verschillende manieren om dit probleem op te lossen. De meest voor de hand liggende oplossing is om de boom expliciet op te slaan (dat wil zeggen, als een geordende reeks knooppunten en aanwijzers van een of andere soort). Dit is misschien wel de meest verkwistende en ineffectieve manier. In de praktijk wordt er geen gebruik van gemaakt.
U kunt een lijst met symboolfrequenties opslaan (dwz een frequentiewoordenboek). Met zijn hulp kan de decoder de codeboom eenvoudig reconstrueren. Hoewel deze methode minder verspillend is dan de vorige, is deze niet de beste.
Ten slotte kunt u een van de eigenschappen van canonieke codes gebruiken. Zoals eerder opgemerkt, worden canonieke codes volledig gedefinieerd door hun lengte. Met andere woorden: het enige wat de decoder nodig heeft is een lijst met tekencodelengtes. Gezien het feit dat gemiddeld de lengte van één code voor een alfabet van N karakters kan worden gecodeerd in [(log 2 (log 2 N))] bits, verkrijgen we een zeer efficiënt algoritme. We zullen er dieper op ingaan.
Laten we aannemen dat de alfabetgrootte N=256 is en we comprimeren het gewone tekstbestand(ASCII). Hoogstwaarschijnlijk zullen we niet alle N-tekens van ons alfabet in zo'n bestand vinden. Laten we vervolgens de lengte van de code van ontbrekende tekens gelijk stellen aan nul. In dit geval zal de opgeslagen lijst met codelengtes voldoende bevatten groot aantal nullen (codelengtes van ontbrekende tekens) gegroepeerd. Elke groep kan worden gecomprimeerd met behulp van de zogenaamde groepscodering - RLE (Run - Length - Encoding). Dit algoritme is uiterst eenvoudig. In plaats van een reeks van M identieke elementen op een rij, zullen we het eerste element van deze reeks en het aantal herhalingen ervan opslaan, d.w.z. (M-1). Voorbeeld: RLE("AAABBBCDDDDDD")=A3 B2 C0 D6.
Bovendien kan deze werkwijze enigszins worden uitgebreid. Wij kunnen solliciteren RLE-algoritme niet alleen voor groepen met een lengte van nul, maar ook voor alle andere. Deze methode voor het verzenden van een codeboom is algemeen aanvaard en wordt in de meeste moderne implementaties gebruikt.
Implementatie: SHCODEC
Bijlage: biografie van D. Huffman
David Huffman werd in 1925 geboren in Ohio, VS. Huffman behaalde een bachelordiploma in elektrotechniek van Staatsuniversiteit Ohio (Ohio State University) op 18-jarige leeftijd. Vervolgens diende hij in het leger als radarondersteuningsofficier op een torpedobootjager die na de Tweede Wereldoorlog hielp bij het opruimen van mijnen in Japanse en Chinese wateren. Vervolgens behaalde hij een masterdiploma aan de Ohio University en een doctoraat aan het Massachusetts Institute of Technology (MIT). Hoewel Huffman vooral bekend is vanwege het ontwikkelen van een methode voor het construeren van minimaal redundante codes, heeft hij ook belangrijke bijdragen geleverd op vele andere gebieden (vooral elektronica). Hij voor een lange tijd Hoofd van de afdeling Computerwetenschappen aan het MIT. In 1974 nam hij, reeds emeritus hoogleraar, ontslag. Huffman ontving een aantal waardevolle onderscheidingen. 1999 - Richard W. Hamming-medaille van het Institute of Electrical and Electronics Engineers (IEEE) voor uitzonderlijke bijdragen aan de informatietheorie, Louis E. Levy-medaille van het Franklin Institute voor zijn proefschrift over sequentiële schakelcircuits, de W. Wallace McDowell Award, de IEEE Computer Society Award en de IEEE Golden Anniversary Technology Innovation Award in 1998. In oktober 1999 stierf David Huffman op 74-jarige leeftijd aan kanker. R.L. Milidiu, A.A. Pessoa, ES Laber, "Efficiënte implementatie van het opwarmalgoritme voor de constructie van in lengte beperkte prefixcodes", Proc. van ALENEX (Internationale workshop over algoritme-engineering en experimenten), pp. 1-17, Springer, januari. 1999. |
Op dit moment Weinig mensen denken na over hoe bestandscompressie werkt. Vergeleken met eerder gebruik persoonlijke computer het werd veel gemakkelijker. En bijna iedereen die ermee werkt bestandssysteem, maakt gebruik van archieven. Maar weinig mensen denken na over hoe ze werken en volgens welk principe bestandscompressie plaatsvindt. De allereerste versie van dit proces waren Huffman-codes, en deze worden nog steeds gebruikt in verschillende populaire archiveringsprogramma's. Veel gebruikers denken er niet eens over na hoe gemakkelijk het is om een bestand te comprimeren en hoe het werkt. In dit artikel zullen we bekijken hoe compressie plaatsvindt, welke nuances het coderingsproces helpen versnellen en vereenvoudigen, en we zullen ook het principe van het construeren van een coderingsboom begrijpen.
Geschiedenis van het algoritme
Het allereerste algoritme voor efficiënt coderen elektronische informatie werd de code die Huffman halverwege de twintigste eeuw voorstelde, namelijk in 1952. Hij is momenteel de belangrijkste basis element de meeste programma's zijn ontworpen om informatie te comprimeren. Op dit moment zijn enkele van de meest populaire bronnen die deze code gebruiken ZIP-archieven, ARJ, RAR en vele anderen.
Ook dit algoritme Huffman geldt voor anderen grafische objecten. Welnu, alle moderne faxen gebruiken ook codering, uitgevonden in 1952. Ondanks het feit dat er zoveel tijd is verstreken sinds de code is gemaakt, wordt deze nog steeds gebruikt in de nieuwste shells en op apparatuur van oude en moderne typen.
Efficiënt codeerprincipe
Het Huffman-algoritme is gebaseerd op een schema waarmee u de meest waarschijnlijke, meest voorkomende symbolen van het systeem kunt vervangen. En degenen die minder vaak voorkomen, worden vervangen door langere codes. De overgang naar lange Huffman-codes vindt pas plaats nadat het systeem alles heeft gebruikt minimumwaarden. Met deze techniek kunt u de codelengte per teken minimaliseren Originele bericht over het algemeen.
Het belangrijke punt is dat aan het begin van het coderen de kansen van de letters al bekend moeten zijn. Op basis hiervan zal het uiteindelijke bericht worden samengesteld. Op basis van deze gegevens wordt een Huffman-codeboom opgebouwd, op basis waarvan het proces van het coderen van brieven in het archief zal worden uitgevoerd.
Huffman-codevoorbeeld
Laten we, om het algoritme te illustreren, nemen grafische versie het bouwen van een codeboom. Om deze methode effectief te gebruiken, is het de moeite waard om de definitie van enkele van de waarden die nodig zijn voor het concept te verduidelijken deze methode. Een reeks bogen en knooppunten die van knooppunt naar knooppunt zijn gericht, wordt gewoonlijk een grafiek genoemd. De boom zelf is een grafiek met een aantal bepaalde eigenschappen:
- elk knooppunt kan niet meer dan één van de bogen bevatten;
- een van de knooppunten moet de wortel van de boom zijn, dat wil zeggen dat deze helemaal geen bogen mag bevatten;
- Als je vanaf de wortel langs bogen begint te bewegen, zou dit proces je in staat moeten stellen om absoluut elk van de knooppunten te bereiken.
Er is ook zo'n concept opgenomen in de Huffman-codes als een blad van een boom. Het vertegenwoordigt een knooppunt waaruit geen boog mag voortkomen. Als twee knooppunten met elkaar zijn verbonden door een boog, dan is een van hen een ouder en de andere een kind, afhankelijk van uit welk knooppunt de boog komt en waar deze binnenkomt. Als twee knooppunten hetzelfde ouderknooppunt hebben, worden ze gewoonlijk zusterknooppunten genoemd. Als de knooppunten naast de bladeren meerdere bogen hebben, wordt deze boom binair genoemd. Dit is precies wat een Huffman-boom is. Kenmerken van knooppunten van deze constructie is dat het gewicht van elke ouder gelijk is aan de som van de gewichten van al zijn knooppuntkinderen.
Huffman-boomconstructie-algoritme
De constructie van de Huffman-code bestaat uit de letters van het invoeralfabet. Er wordt een lijst gevormd met de knooppunten die vrij zijn in de toekomstige codeboom. Het gewicht van elk knooppunt in deze lijst moet hetzelfde zijn als de waarschijnlijkheid van het voorkomen van de berichtletter die overeenkomt met dat knooppunt. In dit geval wordt uit verschillende vrije knooppunten van de toekomstige boom degene geselecteerd die het minst weegt. Bovendien, als de minimale indicatoren in verschillende knooppunten worden waargenomen, kunt u vrijelijk een van de paren kiezen.
Hierna wordt een ouderknooppunt gemaakt, dat hetzelfde zou moeten wegen als de som van dit paar knooppunten. Hierna wordt de ouder naar de lijst met vrije knooppunten gestuurd en worden de kinderen verwijderd. In dit geval ontvangen de bogen de overeenkomstige indicatoren, enen en nullen. Dit proces wordt net lang genoeg herhaald om slechts één knooppunt over te laten. Daarna worden ze van boven naar beneden uitgeschreven.
Verbetering van de compressie-efficiëntie
Om de compressie-efficiëntie te verbeteren, moet u bij het bouwen van een codeboom alle gegevens gebruiken met betrekking tot de waarschijnlijkheid dat letters verschijnen specifiek bestand, vastgemaakt aan de boom, en zorg ervoor dat ze niet verspreid raken een groot aantal tekstdocumenten. Als u dit bestand eerst doorneemt, kunt u meteen statistieken berekenen over hoe vaak letters van het te comprimeren object voorkomen.
Versnelt het compressieproces
Om het werk van letters te versnellen, is het noodzakelijk om niet uit te voeren op basis van indicatoren van de waarschijnlijkheid van het verschijnen van een bepaalde letter, maar op basis van de frequentie van voorkomen ervan. Hierdoor wordt het algoritme eenvoudiger en gaat het werken ermee aanzienlijk sneller. Het vermijdt ook drijvende-komma- en delingsoperaties.
Bovendien werkt het werken in deze modus dynamische code Huffman, of beter gezegd het algoritme zelf, is niet aan veranderingen onderhevig. Dit komt voornamelijk door het feit dat kansen recht evenredig zijn met frequenties. Het is de moeite waard om speciale aandacht te besteden aan het feit dat het uiteindelijke gewicht van het bestand of het zogenaamde rootknooppunt gelijk zal zijn aan de som van het aantal letters in het te verwerken object.
Conclusie
Huffman-codes zijn een eenvoudig en al lang bestaand algoritme dat nog steeds door velen wordt gebruikt bekende programma's en bedrijven. Dankzij de eenvoud en duidelijkheid kunt u effectieve compressieresultaten bereiken voor bestanden van elke grootte en de ruimte die ze op de opslagschijf innemen aanzienlijk verminderen. Met andere woorden: het Huffman-algoritme is een lang bestudeerd en ontwikkeld schema waarvan de relevantie tot op de dag van vandaag niet is afgenomen.
En dankzij de mogelijkheid om de grootte van bestanden te verkleinen, wordt het overbrengen ervan via het netwerk of andere middelen eenvoudiger, sneller en handiger. Als u met het algoritme werkt, kunt u absoluut alle informatie comprimeren zonder de structuur en kwaliteit ervan te schaden, maar met maximaal effect het verminderen van het bestandsgewicht. Met andere woorden: Huffman-codering is en blijft de meest populaire en huidige methode compressie van bestandsgrootte.