La codifica aritmetica
Come possiamo far meglio di Huffman? - I Come abbiamo visto, il principale svantaggio della codifica di Huffman sono i suoi problemi quando c'è un simbolo che ha probabilità molto alta Ricordare il limite sulla ridondanza della codifica di Huffman statica dove p 1 è la probabilità del simbolo più probabile
Come possiamo far meglio di Huffman? - II L'unico modo per superare questa limitazione è quella di utilizzare, come simboli, gruppi di caratteri. In questo modo l'inefficienza per-simbolo è spalmata sull'intero gruppo Comunque, l'uso dei gruppi è difficile da implementare dato che ci deve essere un gruppo per ogni possibile combinazione di caratteri e quindi il numero dei gruppi cresce esponenzialmente con la lunghezza dei gruppi
Come possiamo far meglio di Huffman? - III Il codice di Huffman è ottimo nelle sue condizioni modello statico un simbolo, una parola Huffman adattivo gruppi codifica aritmetica
L'idea fondamentale La codifica aritmetica supera completamente l'idea di sostituire un simbolo in ingresso con una parola di codice. Al contrario, trasforma un flusso di simboli in ingresso in un singolo numero in virgola mobile in [0,1) Più lungo e complesso è il messaggio e più bit sono necessari per rappresentare il numero in uscita
L'idea fondamentale - II L'uscita dell'algoritmo di codifica aritmetica è, come al solito, un flusso di bit Tuttavia si può pensare come se ci fosse uno 0 iniziale, e il flusso rappresenti un numero frazionale in binario, compreso fra 0 e 1 Per spiegare l'algoritmo, i numeri che mostrerò saranno in base 10, ma ovviamente in realtà sono sempre in base 2
Un esempio - I Stringa bccb dall'alfabeto sorgente {a,b,c} Problema della frequenza 0 risolto inizializzando a 1 tutti i contatori dei caratteri Quando inizio a codificare il primo b tutti i simboli hanno il 33% di probabilità (perché?) La codifica aritmetica mantiene due numeri, low e high, che rappresentano un sottointervallo [low,high) sottoinsieme di [0,1) Inizialmente low=0 e high=1
Un esempio - II L'intervallo tra low e high viene diviso tra i simboli dell'alfabeto, proporzionalmente alle loro probabilità low high a b c (P[c]=1/3) (P[b]=1/3) (P[a]=1/3)
Un esempio - III low high a b c b low = high = P[a]=1/4 P[b]=2/4 P[c]=1/4 new probabilities
Un esempio - IV new probabilities P[a]=1/5 P[b]=2/5 P[c]=2/5 low high a b c c low = high = (P[c]=1/4) (P[b]=2/4) (P[a]=1/4)
Un esempio - V new probabilities P[a]=1/6 P[b]=2/6 P[c]=3/6 low high a b c c low = high = (P[c]=2/5) (P[b]=2/5) (P[a]=1/5)
Un esempio - VI Final interval [0.6390,0.6501) we can send low high a b c low = high = b (P[c]=3/6) (P[b]=2/6) (P[a]=1/6)
Un esempio - riassunto Partendo dall'intervallo tra 0 e 1 ci restringiamo ogni volta al sottointervallo che codifica il simbolo dato Alla fine l'intera sequenza può essere codificata da un numero qualunque nell'intervallo finale (ricordando le parentesi...)
Un esempio - riassunto a b c / /4 2/4 1/4 a b c /5 1/ a b c a b c /6 2/6 1/6 [0.6390, )0.6400
Un altro esempio - I Consideriamo la codifica del nome BILL GATES Abbiamo bisogno delle frequenze delle varie lettere nel testo. caratterefrequenza space0.1 A0.1 B0.1 E0.1 G0.1 I0.1 L0.2 S0.1 T0.1
Un altro esempio - II carattereprobabilitàintervallo space0.1[0.00, 0.10) A0.1[0.10, 0.20) B0.1[0.20, 0.30) E0.1[0.30, 0.40) G0.1[0.40, 0.50) I0.1[0.50, 0.60) L0.2[0.60, 0.80) S0.1[0.80, 0.90) T0.1[0.90, 1.00)
Un altro esempio - III Caratterelowhigh B I L L Space G A T E S Il valore finale di low, codifica univocamente il nome BILL GATES
Decodifica - I Supponiamo di dover decodificare 0.64 nel primo esempio Il decodificatore ha bisogno delle probabilità dei simboli, dal momento che deve ripetere quanto fatto dal codificatore Si comincia con low=0 e high=1 e si divide l'intervallo esattamente come ha fatto il codificatore (a in [0, 1/3), b in [1/3, 2/3), c in [2/3, 1)
Decodifica - II Il numero ricevuto cade nell'intervallo corrispondente a b, perciò b deve essere stato il primo simbolo codificato Quindi il decodificatore calcola i nuovi valori per low (0.3333) per high (0.6667), aggiorna le probabilità dei simboli e divide l'insieme da low a high in accordo con le nuove probabilità La decodifica procede finché l'intera stringa non è stata ricostruita
Decodifica - III 0.64 in [0.3333, ) b 0.64 in [0.5834, ) c... e così via...
Perché funziona? Un numero in un intervallo più piccolo ha bisogno di più bit per essere codificato Eventi ad alta probabilità non diminuiscono di molto l'estensione dell'intervallo, mentre eventi a bassa probabilità conducono ad intervalli molto più ridotti Il numero di bit richiesti per la codifica è proporzionale a meno il logaritmo della dimensione dell'intervallo
Perché funziona? La dimensione dell'intervallo finale è il prodotto delle probabilità dei simboli codificati, perciò il logaritmo di questo prodotto è la somma dei logaritmi di ciascun termine Perciò un simbolo s con probabilità p s genera bit in uscita, che è uguale al contenuto informativo del simbolo (incertezza) !!
Perché funziona? Per questo motivo, la codifica aritmetica è quasi ottima come numero di bit in uscita, ed è capace di codificare eventi molto probabili in appena una frazione di bit! In pratica, l'algoritmo non è esattamente ottimo per via dell'uso di aritmetica a precisione limitata, e perché la codifica finale deve comunque essere fatta da un numero intero di bit
Un trucco - I Per come abbiamo descritto l'algoritmo, l'intero output è disponibile soltanto quanto la codifica è terminata In pratica è possibile emettere dei bit durante la codifica, e questo elimina anche la necessità di avere un'aritmetica di precisione sempre più elevata durante la fase di codifica Il trucco consiste nell'osservare che quando low e high sono vicini fra loro, possono condividere un prefisso comune
Un trucco - II Questo prefisso rimarrà per sempre nei due valori, perciò possiamo emetterlo e rimuoverlo da low e high Per esempio, nella codifica di bccb, succede che dopo la codifica del terzo carattere low=0.6334, high= Si può rimuovere il prefisso comune, emettendo il 6 e trasformando low e high in and 0,667
La fase di codifica Per codificare il simbolo s, se i simboli sono numerati da 1 a n and il simbolo i ha probabilità p i low_bound = high_bound = range = high - low low = low + range * low_bound high = low + range * high_bound
La fase di decodifica I simboli sono numerati da 1 a n e value è il codice aritmetico da decodificare Trovare s tale che Emettere il simbolo s Restringere l'intervallo esattamente come durante la codifica
Implementare la codifica aritmetica Come detto prima, la codifica aritmetica utilizza un numero binario frazionario con precisione illimitata Lavorando con precisione finita (es. 16 or 32 bit) rende la compressione un po' peggiore rispetto al limite fissato dall'entropia E' possibile anche costruire codificatori e decodificatori in aritmetica intera, scontando un'altra piccola degradazione della compressione
Codifica aritmetica vs. codifica di Huffman In un tipico testo in lingua inglese, lo spazio è il simbolo più comune, con una probabilità di circa il 18%, perciò il limite superiore della ridondanza di Huffman è piuttosto piccolo. Al contrario, in immagini in bianco e nero, la codifica aritmetica è molto migliore di quella di Huffman a meno di non usare una tecnica a blocchi (gruppi) A La codifica aritmetica richiede meno memoria dato che la rappresentazione dei simboli è calcolata al volo A La codifica aritmetica è più adatta per modelli ad alte performance, le cui predizioni hanno una probabilità stimata molto alta
Codifica aritmetica vs. codifica di Huffman H La decodifica di Huffman è generalmente più veloce H Nella codifica aritmetica non è facile cominciare a decodificare a metà del flusso di bit. Nella codifica di Huffman invece si può far uso di opportuni “punti di ingresso” In una collezione di testi ed immagini è probabile che Huffman venga usato per i testi e la codifica aritmetica per le immagini