6.4 Huffman-koding
Huffman-koding som grådig algoritme — bygg treet via prioritetskø, og les av kodelengder fra en frekvenstabell (håndkjøring).
Huffman-koding dukker opp i 4 til 5 av 7 sett (rundt 64 %), og den kommer
nesten alltid i én form:
- Sjanger E — håndkjøring, altså at du utfører algoritmen steg for steg og
oppgir sluttilstanden. Du får en frekvenstabell og skal oppgi kodelengden per
symbol, eller det totale antallet bits for en tekst.
Du blir svært sjelden bedt om å skrive Huffman i pseudokode. Du blir bedt om å
bygge treet på papir og lese av tallene — og det er en mekanisk ferdighet du
kan trene deg feilfri på i løpet av en halvtime.
Prioritet: kjenne (nivå 3 av tre — «må sitte» er nivå 1, «kunne» er nivå 2).
Det betyr ikke at temaet er uviktig, men at det gir få poeng per side lest. Til
gjengjeld er de poengene svært lette å ta: én korrekt håndkjøring er typisk 2
poeng, og det er ingen skjønn i vurderingen.
Slik er kapitlet lagt opp (45 min): tre løkker med teori, gjennomkjørt
eksempel og oppgaver.
Forkunnskaper
- kap. 4.4 — min-heapen som prioritetskø. Huffman bygger
treet ved å ta ut de to minste elementene gjentatte ganger, og det er nøyaktig
RemoveMin to ganger.
- kap. 6.3 — begrepet grådig algoritme. Huffman er den
tredje grådige algoritmen i denne delen, etter Prim og Kruskal.
- kap. 4.1 — binære trær, og hva det vil si at en node er et
blad.
Løkke 1 — hvorfor korte koder til vanlige tegn (ca. 12 min)
Tenk deg at du skal sende en lang tekst over en treg linje, og at du selv får
bestemme hvordan hvert tegn skal kodes som bits.
Den enkle løsningen er å gi alle tegn like lange koder. Har du seks forskjellige
tegn, holder det med 3 bits hver (), og en tekst på 50 tegn koster
150 bits.
Men tegnene er ikke like vanlige. I en norsk tekst er E langt hyppigere enn K.
Gir du E en kode på 1 bit og K en på 5, betaler du mer for de sjeldne tegnene
og mindre for de vanlige — og siden de vanlige forekommer oftest, går regnestykket
i din favør.
Det er hele ideen i Huffman-koding. Og det er også der den vanligste feilen ligger:
kort kode til hyppig symbol, ikke omvendt.
En koding der ingen kodeord er begynnelsen på et annet kodeord.
Det er egenskapen som gjør at en bitstreng kan leses entydig uten skilletegn: når
du har lest et gyldig kodeord, vet du at det er ferdig. Har E koden 0 og R
koden 01, er koden ikke en prefikskode — og bitstrengen 01 kunne betydd
både «R» og «E fulgt av noe som begynner på 1».
Et binærtre der alle symbolene ligger i bladene, gir automatisk en prefikskode:
ingen vei til et blad er begynnelsen på en annen vei til et blad.
Et binærtre der hvert symbol ligger i et blad, og kodeordet leses av veien fra
rota: venstre gren er 0, høyre gren er 1.
Treet bygges grådig nedenfra: slå gjentatte ganger sammen de to minste
frekvensene til én ny node. Resultatet er en optimal prefikskode — ingen annen
prefikskode gir færre bits totalt.
(symbol, frekvens) med oppføringer. Prioritetskøen PQ er en min-heapordnet på frekvens, med
RemoveMin og Insert i — sekap. 4.4. En trenode har feltene
v.frekvens, v.venstreog
v.hoyre; et blad har i tillegg v.symbol.Prebetingelse: og alle frekvenser er positive.
Postbetingelse: returverdien er rota i et binærtre der hvert symbol er et blad,
og kodene lest av treet danner en optimal prefikskode.
Procedure Huffman(frekvenser)
Input: liste med n par (symbol, frekvens), alle frekvenser > 0
Output: rota i et Huffman-tre
PQ = tom min-heap ordnet paa frekvens
for hvert par (s, f) i frekvenser:
PQ.Insert(nytt blad med symbol s og frekvens f)
while PQ inneholder mer enn én node:
a = PQ.RemoveMin()
b = PQ.RemoveMin()
ny = ny node med frekvens a.frekvens + b.frekvens
ny.venstre = a
ny.hoyre = b
PQ.Insert(ny)
return PQ.RemoveMin()Grunnideen i én setning: de to sjeldneste symbolene må ligge dypest i treet,
så de kan trygt slås sammen først — og etterpå er det samme problem med ett symbol
mindre.
Kjøretid: . Tell operasjonene: Insert i starten à
gir . while-løkka kjøres ganger (hver runde
reduserer køens størrelse med én, fra til 1), og hver runde gjør toRemoveMin og én Insert, altså . Summen er .
Å lese av kodene koster en traversering av treet, , og endrer ikke
orden.
Et tekstutdrag på 50 tegn bruker bare seks bokstaver, med disse frekvensene:
20, 12, 8, 5, 3, 2.
Bygg Huffman-treet, oppgi kodelengden til hvert symbol, og finn hvor mange bits
teksten trenger til sammen.
Køen vises som heap-array med indeks fra 0. Etikettene på de sammenslåtte
nodene, som KV og KTV, viser hvilke symboler som ligger under noden — de er
bare navn, ikke en del av algoritmen. Ved lik frekvens tas den alfabetisk minste
etiketten først, slik at sporingen kan gjenskapes nøyaktig.
Steg 2 — slå sammen de to minste, gjentatte ganger:
| Steg | To minste tatt ut | Ny node lagt inn | Kø etter (heap-array) |
|---|---|---|---|
| 1 | (2, K) og (3, V) | (5, KV) | (5, KV), (5, T), (12, R), (20, E), (8, S) |
| 2 | (5, KV) og (5, T) | (10, KTV) | (8, S), (10, KTV), (12, R), (20, E) |
| 3 | (8, S) og (10, KTV) | (18, KSTV) | (12, R), (20, E), (18, KSTV) |
| 4 | (12, R) og (18, KSTV) | (30, KRSTV) | (20, E), (30, KRSTV) |
| 5 | (20, E) og (30, KRSTV) | (50, EKRSTV) | (50, EKRSTV) |
Fem sammenslåinger for seks symboler. Kontrollregning: .
Stemmer.
Steg 3 — les av kodene ved å gå ned fra rota: venstre er
0, høyre er 1.I hver sammenslåing over er den først uttatte noden venstre barn.
| Symbol | Frekvens | Kodeord | Kodelengde | Bits (frekvens × lengde) |
|---|---|---|---|---|
E | 20 | 0 | 1 | 20 |
K | 2 | 11100 | 5 | 10 |
R | 12 | 10 | 2 | 24 |
S | 8 | 110 | 3 | 24 |
T | 5 | 1111 | 4 | 20 |
V | 3 | 11101 | 5 | 15 |
| Sum | 50 | 113 |
Sluttilstand — dette er svaret du leverer:
E: 1 bit R: 2 bits S: 3 bits
T: 4 bits V: 5 bits K: 5 bits
Totalt: 113 bitsSammenligningen som viser poenget. Med fast kodelengde trengs 3 bits per tegn(seks symboler krever minst 3, siden ), altså
bits. Huffman bruker 113. Det er en besparelse på 37 bits,
eller nesten 25 %.
Se på rekkefølgen på kodelengdene: 1, 2, 3, 4, 5, 5 — de følger frekvensene
nøyaktig omvendt. Det er ikke tilfeldig, det er hele algoritmen. Får du en tabell
der et sjeldent symbol har kortere kode enn et hyppig, har du gjort en feil.
Fellenote. Fella her er å bytte om: kort kode til det sjeldneste symbolet.
Kontrollen tar fem sekunder — sorter symbolene etter frekvens og sjekk at
kodelengdene går motsatt vei.
(Innstegsoppgave, sjanger E — håndkjøring, altså at du utfører algoritmen steg for
steg og oppgir sluttilstanden.) Bruk kodetabellen fra eksempel 1.
a) Hvor mange bits trengs for å kode strengen RESE?
b) Bitstrengen 100110 skal dekodes. Hvilken tekst er det?
c) Hvorfor trenger du ingen skilletegn mellom kodeordene?
Løkke 2 — håndkjøringen, steg for steg (ca. 15 min)
Dette er den delen som gir poeng på eksamen, så den er verdt å gjøre mekanisk.
Oppskriften er fire steg, og den er den samme hver gang.
to som skal slås sammen først.
2. Slå sammen de to minste. Summen blir en ny node, og den settes inn på riktig
plass i den sorterte lista. Gjenta til det er én node igjen.
3. Tell sammenslåingene: det skal være nøyaktig av dem.
4. Les av kodelengden per symbol som antall sammenslåinger symbolet var med i,
altså dybden i treet.
Snarveien for punkt 4: du trenger ofte ikke tegne treet i det hele tatt.
Kodelengden til et symbol er antallet ganger frekvensen dets inngikk i en
sammenslåing. Fulgte med i sammenslåing 1, 2, 3, 4 og 5, er kodelengden 5.
Totalt antall bits:
Merk at dette tallet er entydig selv når treet ikke er det. Er to frekvenser like,
kan sammenslåingene gjøres i forskjellig rekkefølge, og du kan få forskjellige
kodeord — men den totale bitkostnaden er alltid den samme, og det er den sensor
regner på.
Fem symboler har frekvensene 12, 9, 4, 3, 2. Oppgi
kodelengden til hvert symbol og totalt antall bits, uten å tegne treet.
| Steg | To minste tatt ut | Ny node lagt inn | Kø etter (heap-array) |
|---|---|---|---|
| 1 | (2, U) og (3, T) | (5, TU) | (4, S), (5, TU), (9, O), (12, L) |
| 2 | (4, S) og (5, TU) | (9, STU) | (9, O), (12, L), (9, STU) |
| 3 | (9, O) og (9, STU) | (18, OSTU) | (12, L), (18, OSTU) |
| 4 | (12, L) og (18, OSTU) | (30, LOSTU) | (30, LOSTU) |
Kodelengdene, lest som antall sammenslåinger hvert symbol var med i:
- var med i sammenslåing 1, 2, 3 og 4: lengde 4.
- var med i 1, 2, 3 og 4: lengde 4.
- var med i 2, 3 og 4: lengde 3.
- var med i 3 og 4: lengde 2.
- var med i 4: lengde 1.
| Symbol | Frekvens | Kodeord | Kodelengde | Bits (frekvens × lengde) |
|---|---|---|---|---|
L | 12 | 0 | 1 | 12 |
O | 9 | 10 | 2 | 18 |
S | 4 | 110 | 3 | 12 |
T | 3 | 1111 | 4 | 12 |
U | 2 | 1110 | 4 | 8 |
| Sum | 30 | 62 |
Sluttilstand — dette er svaret du leverer:
L: 1 bit O: 2 bits S: 3 bits T: 4 bits U: 4 bits
Totalt: 62 bitsKontrollregningene, begge verdt å gjøre:1. Antall sammenslåinger: 4, og . Stemmer.
2. Kodelengdene mot frekvensene: frekvensene stiger , og
lengdene synker . Motsatt vei hele veien, som de skal.
Sammenligning med fast kodelengde: fem symboler krever 3 bits hver, altså
bits mot Huffmans 62.
Merk hvorfor snarveien virker. Hver gang en node inngår i en sammenslåing,
kommer den ett nivå lenger ned fra rota, og hvert nivå er ett bit. Å telle
sammenslåinger er derfor nøyaktig det samme som å telle dybde i treet — og det er
mye raskere å gjøre riktig under tidspress.
Fem symboler har frekvensene 15, 7, 6, 6, 5.
a) Kjør Huffmans algoritme og oppgi hvilke noder som slås sammen i hvert steg.
b) Oppgi kodelengden til hvert symbol.
c) Hvor mange bits trengs totalt, og hvor mye sparer du mot fast
kodelengde?
- Å gi kortest kode til det sjeldneste symbolet. Dette er den klassiske
ombyttingen, og den ødelegger hele svaret. Kontrollen: sorter symbolene etter
frekvens og sjekk at kodelengdene går motsatt vei.
- Å slå sammen andre enn de to minste. Algoritmen er grådig og har ingen
unntak: alltid de to laveste frekvensene i køen, også når den ene er en node du
nettopp laget.
- Å blande kodelengde og kodeord. Spør oppgaven om kodelengden, er svaret et
tall (4). Spør den om kodeordet, er svaret en bitstreng (1110). Les
oppgaveteksten nøye — det er sensors mest gjentatte råd.
- Å regne totalbits feil. Formelen er — frekvens
ganger lengde, summert. Ikke summen av kodelengdene alene, og ikke summen av
frekvensene.
- Å tro at treet er entydig. Er to frekvenser like, finnes flere gyldige
Huffman-trær. Kodeordene kan variere; totalen kan ikke. Oppgir du en gyldig
tabell med riktig total, er svaret riktig selv om det ikke ser ut som fasiten.
- Å glemme kontrollen . Med symboler skal det være nøyaktig
sammenslåinger. Har du flere eller færre, har du mistet eller duplisert en node
underveis.
Og den gjennomgående: å oppgi kjøretiden . Huffman er —
logaritmefaktoren kommer fra prioritetskøen, og uten den kunne du ikke funnet de
to minste billig.
Løkke 3 — hvorfor grådighet virker her (ca. 10 min)
Huffman er den tredje grådige algoritmen i Del 6. Prim tar alltid den letteste
kanten ut av treet; Kruskal tar alltid den letteste kanten som ikke lager sykel;
Huffman slår alltid sammen de to minste frekvensene.
Alle tre har det til felles at det lokale valget aldri må gjøres om. Det er ikke
selvsagt — for de fleste optimeringsproblemer er grådighet feil. At det virker
her, er et resultat du skal kjenne til, ikke bevise.
Bør kjenne til. Ingen prefikskode gir færre bits totalt enn den Huffmans algoritme
finner.
Intuisjonen bak, i to setninger. De to sjeldneste symbolene må ligge dypest i
et optimalt tre — ellers kunne du byttet et sjeldent symbol med et hyppigere som
lå dypere, og spart bits. Og siden de er dypest, kan de like gjerne være søsken,
for da er treet over dem det samme problemet med ett symbol færre.
Det du skal kunne på eksamen er påstanden og at algoritmen er grådig — ikke
beviset. Selve beviset er ikke IN2010-pensum.
Merk grensen for hva «optimal» betyr: optimal blant prefikskoder med ett fast
kodeord per symbol. Andre komprimeringsmetoder kan gjøre det bedre ved å utnytte
mønstre over flere tegn — men det er utenfor pensum, og utenfor det Huffman lover.
En sensor sender en av seks statuskoder hvert minutt. Over en time ble kodene talt
opp slik: 25, 14, 9, 6, 4, 2.
a) Bygg Huffman-treet og oppgi kodelengden per statuskode.
b) Hvor mange bits trengs for hele timen?
c) Hvor mye spares mot en fast koding, og hva ville skjedd hvis alle seks
kodene forekom like ofte?
Sum: 60 — én kode per minutt i en time. Stemmer.
| Steg | To minste tatt ut | Ny node lagt inn | Kø etter (heap-array) |
|---|---|---|---|
| 1 | (2, Y) og (4, J) | (6, JY) | (6, H), (6, JY), (14, I), (25, N), (9, G) |
| 2 | (6, H) og (6, JY) | (12, HJY) | (9, G), (12, HJY), (14, I), (25, N) |
| 3 | (9, G) og (12, HJY) | (21, GHJY) | (14, I), (25, N), (21, GHJY) |
| 4 | (14, I) og (21, GHJY) | (35, GHIJY) | (25, N), (35, GHIJY) |
| 5 | (25, N) og (35, GHIJY) | (60, GHIJNY) | (60, GHIJNY) |
Fem sammenslåinger, og . Stemmer.
| Symbol | Frekvens | Kodeord | Kodelengde | Bits (frekvens × lengde) |
|---|---|---|---|---|
G | 9 | 110 | 3 | 27 |
H | 6 | 1110 | 4 | 24 |
I | 14 | 10 | 2 | 28 |
J | 4 | 11111 | 5 | 20 |
N | 25 | 0 | 1 | 25 |
Y | 2 | 11110 | 5 | 10 |
| Sum | 60 | 134 |
Kodelengder: 1, 2, 3, 4, 5, 5.
b) 134 bits for hele timen.
c) Fast koding trenger 3 bits per melding (seks koder krever minst 3, siden
), altså bits. Huffman bruker
134, en besparelse på 46 bits,
eller rundt 26 %.
Hadde alle seks kodene forekommet like ofte — 10 hver — ville Huffman gitt
kodelengder på 2, 2, 3, 3, 3, 3 (treet den bygger på seks like frekvenser), og
totalt bits mot fast kodings 180. En liten
gevinst, men langt mindre enn de 46 bitene her.
Momentet, og det som gir det siste poenget: Huffman tjener på skjevhet.
Jo skjevere frekvensfordelingen er, jo mer sparer du. Er alle symbolene like
vanlige, er det nesten ingenting å hente — og det er verdt å si i en besvarelse,
fordi det viser at du forstår hva algoritmen faktisk utnytter.
Kjøretid: , der er antall forskjellige statuskoder — ikke
antall meldinger. Å blande de to er en fast forveksling: alfabetets størrelse er
det som styrer kostnaden ved å bygge treet.
Marker sant eller usant, og begrunn hvert svar med én setning.
a) I et Huffman-tre har det hyppigste symbolet alltid den korteste koden.
b) Huffman-treet er entydig bestemt av frekvenstabellen.
c) Huffmans kjøretid er , der er antall forskjellige symboler.
d) En Huffman-kode er alltid en prefikskode.
e) Huffman lønner seg mest når alle symbolene forekommer like ofte.
En melding består av sju symboler med frekvensene 1, 1, 2,
3, 5, 8, 13.
a) Hvor mange sammenslåinger gjør algoritmen?
b) Bygg treet og oppgi kodelengden til hvert symbol.
c) Hva er totalt antall bits, og hva blir gevinsten mot fast kodelengde?
d) Hva er spesielt med denne frekvensfordelingen, og hva gjør den med treets
form?
Begrepsbank
Begrepsbanken er flashcard-/repetisjonsstoff — den gjentar det du nettopp har
lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder
kjernestoffet.
Bygger en optimal prefikskode: legg alle symbolene i en min-heap ordnet på
frekvens, slå gjentatte ganger sammen de to minste til én ny node, og legg
noden tilbake.
Kjøretid , der er antall forskjellige symboler — ikke tekstens
lengde. Grådig algoritme, som Prim og Kruskal.
Dybden til symbolets blad i Huffman-treet, altså antall bits i kodeordet.
Snarveien i håndkjøring: kodelengden er antall sammenslåinger symbolet var med
i. Du trenger ikke tegne treet for å svare på eksamen.
Frekvens ganger kodelengde, summert over alle symboler. Dette tallet er entydig
selv når treet ikke er det — er to frekvenser like, kan kodeordene variere, men
totalen er alltid den samme.
Med symboler gjør Huffmans algoritme nøyaktig sammenslåinger, og treet
får interne noder.
Hver runde tar ut to noder og setter inn én, så køen krymper med én per runde: fra
til 1. Bruk tallet som kontrollregning på håndkjøringen.
Er to frekvenser like, kan sammenslåingene gjøres i forskjellig rekkefølge, og
kodeordene kan bli forskjellige.
Kodelengdene og den totale bitkostnaden er derimot alltid de samme. Leverer du en
gyldig tabell med riktig total, er svaret riktig selv om det ikke ligner
fasiten.
Når frekvensfordelingen er skjev. Jo mer noen symboler dominerer, jo mer
sparer du mot en fast koding.
Er alle symbolene like vanlige, blir kodelengdene nesten like, og gevinsten er
liten. Det er ofte det siste delpoenget i en oppgave: å si hva algoritmen faktisk
utnytter.
Med forskjellige symboler trengs bits per symbol i
en fast koding — 3 bits for 5 til 8 symboler, 4 bits for 9 til 16.
Det er tallet du sammenligner Huffman med når oppgaven spør «hvor mye spares?».
Prim, Kruskal og Huffman er alle grådige: de tar det beste lokale valget i hvert
steg og angrer aldri.
For alle tre er grådighet beviselig optimalt. Det er ikke selvsagt — for de fleste
optimeringsproblemer er grådighet feil — og at det virker her, er et resultat du
skal kjenne, ikke bevise.
Med symboler kan et kodeord bli opptil bits langt.
Det skjer når frekvensene vokser slik at hver ny sammenslåing er like stor som det
neste symbolet — for eksempel Fibonacci-tallene 1, 1, 2, 3, 5, 8, 13. Treet blir da
en kjede. Huffman garanterer lavest totalkostnad, ikke lav høyde.
Les bitstrengen fra venstre, ett bit av gangen, og følg treet ned fra rota. Når du
når et blad, skriv ut symbolet og start på rota igjen.
Kostnaden er ett steg per bit, altså . Prefiks-egenskapen er
det som gjør at du aldri trenger å lese fram og tilbake.
En koding der ingen kodeord er begynnelsen på et annet. Det gjør bitstrengen
entydig lesbar uten skilletegn.
Et binærtre med alle symbolene i bladene gir automatisk en prefikskode. Ligger
et symbol i en intern node, brytes egenskapen, og koden blir tvetydig.
Å kode en tekst på en million tegn med seks forskjellige symboler koster
for å bygge treet, pluss ett oppslag per tegn. Å blande de to
størrelsene er en fast forveksling — og felle #10, kjøretid med udefinerte
størrelser.
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.
Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.