INF2810: Funksjonell Programmering. Huffman-koding

Like dokumenter
INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. Huffman-koding

INF2810: Funksjonell Programmering. Trær og mengder

INF2810: Funksjonell Programmering. Lokale variabler. Og trær.

INF2810: Funksjonell Programmering

INF2810: Funksjonell Programmering

Innlevering 2a i INF2810, vår 2017

INF2810: Funksjonell Programmering

Høyere-ordens prosedyrer

INF2810: Funksjonell Programmering

Rekursjon og lister. Stephan Oepen & Erik Velldal. 1. februar, Universitetet i Oslo

INF2810: Funksjonell Programmering. Kommentarer til prøveeksamen

INF 1040 Løsningsforslag til kapittel

INF2810: Funksjonell Programmering. En metasirkulær evaluator

INF2810: Funksjonell Programmering. En metasirkulær evaluator

INF2810: Funksjonell Programmering. Lister og høyereordens prosedyrer

INF2810: Funksjonell Programmering. Lister og høyereordens prosedyrer

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme

INF2810: Funksjonell Programmering. Mer om verditilordning. Tabeller. Og strømmer.

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme, del 2

INF2810: Funksjonell Programmering. Mer om verditilordning. Tabeller. Og strømmer.

INF2810: Funksjonell Programmering. Muterbare data

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme

INF2810: Funksjonell Programmering. Eksamensforberedelser

INF2810: Funksjonell Programmering. En metasirkulær evaluator, del 2

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme, del 2

INF2810: Funksjonell Programmering. En metasirkulær evaluator, del 2

INF2810: Funksjonell Programmering. En Scheme-evaluator i Scheme, del 2

INF2810: Funksjonell Programmering. Oppsummering og eksamensforberedelser

INF Algoritmer og datastrukturer

Informasjonsteori og entropi

INF2810: Funksjonell Programmering. Strømmer og utsatt evaluering

INF2810: Funksjonell Programmering. Mer om strømmer

INF Algoritmer og datastrukturer

INF2220: Time 4 - Heap, Huffmann

INF2810: Funksjonell Programmering. Oppsummering og eksamensforberedelser

INF2810: Funksjonell Programmering. Oppsummering og eksamensforberedelser

INF2810: Funksjonell Programmering. Oppsummering og eksamensforberedelser

Binære søketrær. En ordnet datastruktur med raske oppslag. Sigmund Hansen

INF2810: Funksjonell programmering: Mer om Scheme. Rekursjon og iterasjon.

Informasjonsteori og entropi

INF2220: Forelesning 1. Praktisk informasjon Analyse av algoritmer (kapittel 2) (Binær)trær (kapittel )

INF1020 Algoritmer og datastrukturer GRAFER

IN Algoritmer og datastrukturer

INF2810: Funksjonell Programmering. Køer, tabeller, og (litt om) parallelitet

INF Algoritmer og datastrukturer. Hva er INF2220? Algoritmer og datastrukturer

INF2810: Funksjonell Programmering. Strømmer

Hva er en algoritme? INF HØSTEN 2006 INF1020. Kursansvarlige Ragnar Normann E-post: Dagens tema

Forelesning 25. MAT1030 Diskret Matematikk. Litt repetisjon. Litt repetisjon. Forelesning 25: Trær. Dag Normann

Dagens plan. INF Algoritmer og datastrukturer. Koding av tegn. Huffman-koding

MAT1030 Diskret Matematikk

UNIVERSITETET I OSLO

MAT1030 Diskret Matematikk

MAT1030 Forelesning 25

UNIVERSITETET I OSLO

Generelle Tips. INF Algoritmer og datastrukturer. Åpen og Lukket Hashing. Hashfunksjoner. Du blir bedømt etter hva du viser at du kan

Løsningsforslag, Ukeoppgaver 9 INF2310, våren kompresjon og koding del I

Notater til INF2220 Eksamen

INF Algoritmer og datastrukturer

INF2810: Funksjonell programmering: Introduksjon

INF110 Algoritmer og datastrukturer TRÆR. Vi skal i denne forelesningen se litt på ulike typer trær:

Trær. En datastruktur (og abstrakt datatype ADT)

Vi skal se på lambda-uttrykk. Følgende er definerte og vil bli brukt gjennom oppgaven

Transkript:

INF2810: Funksjonell Programmering Huffman-koding Stephan Oepen & Erik Velldal Universitetet i Oslo 22. februar, 2013

Tema 2 Forrige uke Data-abstraksjon Lister av lister Tre-rekursjon Prosedyrer som datastruktur I dag Hierarkisk og symbolsk data Eksempel: Huffman-koding

Repetisjon: Prosedyrer som datastruktur 3 (define (p-cons x y) (lambda (proc) (proc x y))) (define (p-car proc) (proc (lambda (p q) p))) (define (p-cdr proc) (proc (lambda (p q) q)))? (p-cons 2 3) #<procedure>? (p-cdr (p-cons 2 3)) 3? (p-car (p-cdr (p-cons 1 (p-cons 2 3)))) 2 Selv datastrukturer kan implementeres som prosedyrer! Vi definerer p-cons som en rent prosedyrebasert versjon av cons. Mest en kuriositet; eksempel på hvordan ren funksjonsorientering kan strekkes overraskende langt. (Men vi skal senere i kurset se flere eksempler på prosedyrer som datastrukturer.)

Repetisjon: Trær 4 Et tre er en type graf, en mengde noder forbundet med kanter. Interne noder har barn / døtre: nye subtrær. Den øverste noden kalles rot. Løvnoder har ikke barn (såkalt terminalnoder). Kantene kan ikke være sykliske. Binærtrær: nodene har maks to døtre, venstre/høyre. I Scheme kan vi representere trær som lister av lister.

Repetisjon: Rekursjon på lister av lister 5 (define (fringe tree) (cond ((null? tree) ()) ((pair? tree) (append (fringe (car tree)) (fringe (cdr tree)))) (else (list tree))))? (fringe ((1 2) ((3) 4))) (1 2 3 4) ;; ps: append kombinerer to lister, ;; sammenlikning med cons:? (append (1 2) (3)) (1 2 3)? (cons (1 2) (3)) ((1 2) 3) fringe; samler alle løvnodene i én flat liste. Typisk eksempel på rekursjon på lister av lister: Vi ønsker å gjøre noe for hvert atomære element (løvnodene): Må passe på at rekursjonen går ned i hver liste...

Lister av lister, del 2: 6... men i dag skal vi også se eksempler der rekursjonen velger å kun følge én gren (i stedet for å gå ned i alle subtrær). Vi skal også definere abstrakte datatyper for trær der ikke bare løvnodene men også de interne nodene kan ha verdier. Som praktiske eksempler skal vi se på hvordan både mengder og komprimeringskoder kan implementeres effektivt som trær.

Hvor vi er på vei først: Huffmankoding 7 Ved komprimering er målet å kode informasjon med færrest mulig bits (og færre enn den opprinnelig representasjonen). Huffmankoding: En algoritme for generere en optimal komprimeringskode automatisk fra data. Oppdaget i 1952. Brukes for å komprimere forskjellige typer data som lyd (f.eks mp3), bilder (jpeg), video (mpeg-1), og tekst (gzip / bzip2). Men aller først trenger vi litt terminologi...

Litt kompresjons- og kryptografi-sjargong Vi har et alfabet av symboler F.eks {E, T, A, Z} En kodebok tilskriver en kode (kode-ord) til hvert symbol. F.eks E = 00 Vi ønsker å kunne gjøre to ting: å kode / komprimere data (ofte kalt melding) til en kompakt kode. å dekode / dekomprimere en kode tilbake til lesbare data. Koding for kompresjon vs kryptering; forskjellig mål: effektiv lagring / overføring vs effektiv skjuling av informasjon. Tapsfri (lossless): Opprinnelig melding kan rekonstrueres eksakt. Ikke-tapsfri (lossy): Opprinnelig melding kan ikke rekonstrueres eksakt. 8

Koder med fast lengde 9 Eksempel på en binærkode; ASCII. sekvens av 7 bits for hvert tegn Gir mulighet for å representere 2 7 = 128 ulike tegn. Eksempel på såkalt fixed-length code For å skille mellom n symboler trenger vi log 2 n bits per symbol. Enda mer generelt: må bruke log b n kodeposisjoner gitt et kodealfabet av størrelse b (hvor b = 2 for bits / binærkode). Gitt alfabetet {E, T, A, Z } trenger vi log 2 4 = 2 bits. En mulig kodebok: symbol E T A Z kode 00 01 10 11 0001 = ET, 1011 = AZ

Med fast lengde ignoreres frekvens 10 Svakhet: koden over tar ikke hensyn til symbolenes relative frekvens. Koden er kun optimal dersom alle symbolene er like sannsynlige. Vi kan få en mer kompakt kode dersom symboler som forekommer ofte gis en kortere representasjon. På Engelsk er f.eks. bokstaven E mye vanligere enn Z. Gjenspeiles f.eks i morsekode; E = én prikk Z = to streker og to prikker. Morsekode er eksempel på såkalt variable-length code.

Kode med variabel lengde 11 La oss lage en ny kodebok på bakgrunn av frekvensen for hvert symbol (som vi tenker oss at vi har observert fra data): symbol E T A Z frekvens 10 7 5 3 kode 0 1 00 01 Kodeordene har variabel lengde, men er ikke unikt dekodbare: 01 = ET eller Z? 0001 = EEET, AZ, EAT, eller AET? Mulig løsning: bruk en dedikert utvetydig kode som separator. F.eks kort pause i morsekode. Bedre: sørg for at ingen kode er prefiks for en annen kode!

Prefikskode 12 Nytt forslag til kodebok: symbol E T A Z frekvens 10 7 5 3 kode 0 10 110 111 Her har vi en prefikskode med variabel lengde som er unikt dekodbar. Kodeordet som representerer et gitt symbol er aldri et prefiks for kodeordet for et annet symbol. Oppnår kompresjon kun dersom symbolene er ikke-uniformt distribuert. Forekommer alle like ofte kunne vi like gjerne brukt fast lengde. Koden 010 står utvetydig for ET.

David A. Huffman (1925 99) 13 Huffman var en pioner i informasjonsteori og matematisk origami (!) Definerte en algoritme for å automatisk generere optimale prefikskoder fra data. Basert på at vi kjenner de relative frekvensene til symbolene. Beskrevet i artikkelen A Method for the Construction of Minimum-Redundancy Codes fra 1952. Såkalt Huffman-koding. Kodeboken representeres ved et Huffmantre.

Huffman-trær 14 Kodeboken gitt ved et binærtre. En venstregren representerer 0 En høyregren representerer 1 Løvnoder, lagrer: et symbol, vekt (frekvens) Interne noder, lagrer: mengden av symbolene i løvnodene under det i treet, summen av vektene, to barn Gitt et Huffmantre kan vi kode en gitt datasekvens dekode en gitt bitsekvens

Huffman-trær: koding 15 For å kode en melding: For hvert symbol: Vi begynner ved roten og klatrer nedover treet til løvnoden som representerer symbolet. Velger gren ved å sjekke hvilken node symbolet tilhører. Legger til 0 for hver venstregren. Legger til 1 for hver høyregren. F.eks: D = 1011, DB = 1011100

Huffman-trær: dekoding 16 For å dekode en bitsekvens: Begyn ved roten og les av bits; Ta til venstre for hver 0. Ta til høyre for hver 1. Ved en løvnode: les av symbolet og begynn fra roten igjen. F.eks: 10001010 = BAC

Å generere et Huffmantre 17 Gitt et alfabet og de relative frekevensene til symbolene, hvordan kan vi generere kodeboken som vil kode meldinger med færrest mulig bits? 1: Start med en liste av løvnodene. 2: Slå sammen de to nodene med lavest frekvens til en ny node (med summen av frekvensene og unionen av symbolene). - La den ene noden tilsvare venstregrenen og den andre høyre. 3: Oppdater lista av noder og gjenta fra 2. - Legg til den nye noden og fjern nodene vi slo sammen. 4: Stopp når vi står igjen med kun én node (roten).

Å generere et Huffmantre (forts.) Start: {(A 8) (B 3) (C 1) (D 1) (E 1) (F 1) (G 1) (H 1)} Sammenslåing: {(A 8) (B 3) ({C D} 2) (E 1) (F 1) (G 1) (H 1)} Sammenslåing: {(A 8) (B 3) ({C D} 2) ({E F} 2) (G 1) (H 1)} Sammenslåing: {(A 8) (B 3) ({C D} 2) ({E F} 2) ({G H} 2)} Sammenslåing: {(A 8) (B 3) ({C D} 2) ({E F G H} 4)} Sammenslåing: {(A 8) ({B C D} 5) ({E F G H} 4)} Sammenslåing: {(A 8) ({B C D E F G H} 9)} Stopp: {({A B C D E F G H} 17)} 18

Noen kommentarer 19 En Huffman-kode er optimal i den forstand at den gir minst gjennomsnittlig kodestørrelse. Premisser: Faktiske symbolfrekvenser i meldingen vi skal kode stemmer overens med frekvensene som ble brukt for å generere kodetreet. Kodeboken må også overføres eller allerede være kjent. Vi koder ett symbol av gangen. Det siste punktet kan i praksis omgås ved at et separat trinn først definerer symbolene i alfabetet. Symbolene kan f.eks være lengre sekvenser av tegn eller ord. Bruk av to trinn er også grunnen til at Huffman-koding, som gir en tapsfri kode, kan brukes i ikke-tapsfrie formater: F.eks; ved kvantisering mappes flere opprinnelige symboler til et mindre sett av symboler (ikke-tapsfritt), som så Huffman-kodes (tapsfritt).

Enda flere kommentarer 20 Definerer ikke alltid et unikt tre Avhenger av hvordan vi velger hvilken node som blir venstre-/høyregren, og hvordan vi velger hvilke noder for sammenslåing når flere enn to har samme verdi. Det kan altså finnes flere optimale koder. Effektivisering Hold nodelista frekvenssortert; kan da alltid slå sammen de to første. Vi skal se på en abstraksjonsbarriere for å jobbe mot Huffmantrær.

Litt repetisjon: Abstraksjonsbarrierer 21 Hva mener vi med abstraksjonsbarriere? Ikke en barriere mot abstraksjon. Men en abstraksjon som fungerer som en barriere. Når vi introduserer en ny abstrakt datatype definerer vi også et grensesnitt mot datatypen: F.eks konstruktorer, aksessorer og predikat. Danner en abstraksjonsbarriere. Andre deler av programmet har kun kontakt med datatypen gjennom dette grensesnittet. Skjuler detaljene om nøyaktig hvordan datatypen er implementert.

Huffmantrær: Abstraksjonsbarriere 22 Løvnoder; representert av en liste med tre elementer: - leaf (typetagg) - symbol - vekt (frekvens) Konstruktor: - make-leaf Predikat - leaf? Aksessorer: - symbol-leaf - weight-leaf (define (make-leaf symbol weight) (list leaf symbol weight)) (define (leaf? object) (eq? (car object) leaf)) (define (symbol-leaf x) (cadr x)) (define (weight-leaf x) (caddr x))

Huffmantrær: Abstraksjonsbarriere (forts.) 23 Interne noder; liste med fire elementer: - venstre-gren, - høyre-gren - liste av symboler - vekt (define (make-code-tree left right) (list left right (append (symbols left) (symbols right)) (+ (weight left) (weight right)))) (define (left-branch tree) (car tree)) Konstruktor: - make-code-tree Aksessorer: - left-branch - right-branch - symbols - weight (define (right-branch tree) (cadr tree)) (define (symbols tree) (if (leaf? tree) (list (symbol-leaf tree)) (caddr tree))) (define (weight tree) (if (leaf? tree) (weight-leaf tree) (cadddr tree)))

Dekoding (define (decode bits tree) (define (decode-1 bits current-branch) (if (null? bits) () (let ((next-branch (choose-branch (car bits) current-branch))) (if (leaf? next-branch) (cons (symbol-leaf next-branch) (decode-1 (cdr bits) tree)) (decode-1 (cdr bits) next-branch))))) (decode-1 bits tree)) (define (choose-branch bit branch) (cond ((= bit 0) (left-branch branch)) ((= bit 1) (right-branch branch)) (else (error "bad bit CHOOSE-BRANCH" bit)))) Hvorfor bruker vi decode-1? Kalles jo med de samme argumentene, hvorfor ikke bare kalle decode? Kan vi skrive en halerekursiv versjon av decode? 24

Andre ting som gjenstår 25 Flere prosedyrer dere kommer til å skrive i neste innlevering: encode Input: et Huffmantre og en melding (liste av symboler). Output: en bitsekvens (liste av 0 og 1). generate-codebook Input: en liste av symboler og frekvenser. Output: et Huffmantre.