Metodi Lempel-Ziv
Modelli basati su dizionari - I I metodi di compressione basati su dizionari fanno uso del principio di sostituire sottostringhe del messaggio con una parola di codice che identifica tale sottostringa in un dizionario (codebook) Il dizionario contiene una lista di sottostringhe e di parole di codice associate A differenza dei metodi basati sui simboli, quelli basati su un dizionario spesso utilizzano parole di codice prefissate piuttosto che distribuzioni di probabilità esplicite
Modelli basati su dizionari - II Ad esempio, possiamo inserire nel dizionario l'insieme dei caratteri ASCII a 8 bit (Quanti?) e le 256 coppie di caratteri più comuni Se le parole di codice hanno lunghezza fissa, di quanti bit abbiamo bisogno per indicizzare gli elementi del dizionario? SOL. 9 bit Quali sono le performance in bits/carattere nel caso migliore ed in quello peggiore? SOL. migliore:4.5b/car peggiore:9b/car!!
Modelli basati su dizionari - III Un'altra possibilità è quella di utilizzare parole più lunghe nel dizionario, per esempio parole comuni come articoli o preposizioni. Queste stringhe sono le frasi del dizionario Un dizionario con un insieme di parole predefinito non permette normalmente di ottenere una compressione elevata Le performances sono migliori se il dizionario è adattato alla sorgente, ossia se perdiamo l'indipendenza dalla sorgente
Modelli basati su dizionari - IV Per esempio frasi comuni in un quotidiano sportivo sono rare in un libro che si occupa di business management Per evitare che il dizionario sia inadatto per la sorgente potremo costruirne uno per ogni messaggio da comprimere ma c'è un notevole overhead per trasmetterlo e memorizzarlo Decidere la dimensione del dizionario al fine di massimizzare la compressione è un problema molto arduo
I metodi Lempel-Ziv L'unica soluzione efficiente al problema è utilizzare un meccanismo adattivo per costruire il dizionario Praticamente tutti i metodi adattivi basati su un dizionario sono basati su uno dei due metodi sviluppati da due ricercatori israeliani Abraham Lempel e Jacob Ziv nel 1977 e nel 1978, e noti come LZ77 e LZ78 "A Universal Algorithm for Sequential Data Compression" in IEEE Transactions on Information Theory, Maggio 1977
L'idea fondamentale - I L'idea più interessante è che è possibile costruire automaticamente un dizionario che comprenda le stringhe viste fino ad adesso nel messaggio da comprimere Il testo precedente costituisce un ottimo dizionario dato che normalmente condivide lo stesso linguaggio e stile del testo ancora da vedere
L'idea fondamentale - II Non c'è bisogno di trasmettere il dizionario dato che il decodificatore lo può costruire nello stesso modo in cui ha fatto il codificatore Le molte varianti dei metodi Lempel-Ziv differiscono nel modo in cui sono rappresentati i puntatori e nelle limitazioni sulle stringhe di testo a cui tali puntatori possono riferirsi Inoltre lo straordinario proliferare di varianti è anche dovuto ad alcuni brevetti (patents) e alle dispute su di essi
La famiglia LZ77 + Semplice da implementare + Decodifica veloce ed efficiente (serve poca memoria) Il messaggio compresso consiste in una serie di triplette - la prima componente indica quanto indietro guardare nel testo già decodificato - la seconda componente è la lunghezza della frase - la terza è il carattere successivo nel messaggio di input
Un esempio - codifica alfabeto {a,b} aaaabbb aabb
Un esempio - decodifica SOL. x y xz xx yxzz xxyz zxz
Un esempio ricorsivo Nonostante il riferimento ricorsivo, ogni carattere è disponibile quando se ne ha bisogno acaaacb??bbbbbbbbbba
Ulteriori dettagli su LZ77 L'algoritmo LZ77 impone delle limitazioni su quanto indietro i puntatori possano puntare (e questo influenza direttamente la lunghezza della prima componente della tripletta) e sulla dimensione massima della frase riferita (cioè sulla lunghezza della seconda componente) Ad esempio in un tipico testo inglese non c'è guadagno nell'utilizzare una finestra scorrevole di più di qualche migliaio di caratteri Potremo usare una finestra di caratteri, cioè 13 bit
Ulteriori dettagli su LZ77 Allo stesso tempo, la lunghezza del match è raramente più lunga di 16 caratteri, dato che il costo extra per consentire match più lunghi non è normalmente giustificato Esercizio: codificare la sequenza $0110$$0111 con una finestra scorrevole di 7 simboli e massima lunghezza del match di 3 caratteri. Calcolare il rapporto di compressione ottenuto SOL., C=(17*2)/7*8=0.607 << 1!!!
LZ77 - codifica Codificare il testo S[1..N] utilizzando LZ77, con una finestra di W caratteri p=1 WHILE p<N { Supponiamo che tale match occorra in posizione p-m, e sia di lunghezza l produrre la tripletta p = p+l+1 }
LZ77 - decodifica Decodificare il testo S[1..N] utilizzando LZ77, con una finestra di W caratteri p=1 FOREACH triple { S[p... p+l-1] = S[p-f... p–f+l-1] S[p+l ] = c p = p+l+1 }
LZ77 - miglioramenti LZ77 è stato gradualmente affinato prima componente della tripletta: è utile avere una codifica con lunghezza variabile, con codici più corte ai match più recenti (che sono più comuni) seconda componente della tripletta: codici a lunghezza variabile per rappresentare con meno i bit i numeri più piccoli terza componente della tripletta: in alcune varianti è aggiunta solo se serve (e quando serve?), con un flag da 1 bit che indichi la presenza o l'assenza di questa terza componente
L'algoritmo gzip - I gzip è una delle più efficaci varianti di LZ77 E' distribuito dalla Free Software Foundation come parte del progetto GNU (gzip = GNU zip) home page del progetto gzip:
L'algoritmo gzip - II gzip utilizza una tabella hash: i prossimi tre caratteri da codificare sono passati attraverso una funzione hash, ed il valore è usato come indice in una lookup table Questo elemento della tabella è la testa di una lista dei luoghi nei quali occorrono questi tre caratteri all'interno della finestra Il match più lungo viene cercato all'interno della lista Se non ci sono match, la lista è codificata semplicemente come singoli caratteri
L'algoritmo gzip - III Se invece il match esiste, otteniamo una distanza, una lunghezza ed un carattere supplementare La finestra scorrevole ha dimensione W=32KB, e le lunghezze sono limitate a 258 byte La lunghezza delle liste viene limitata per evitare di fare ricerche costose tradeoff accuratezza/tempo: la scelta è lasciata all'utente
L'algoritmo gzip - IV Le lunghezze, le distanze e i caratteri supplementari sono codificati con due alberi di Huffman, uno per le distanze e l'altro per per le lunghezze ed i caratteri aggiuntivi I codici di Huffman sono generati analizzando blocchi fino a 64KB (con Huffman canonico) per questo motivo, l'algoritmo gzip non è a rigore a singolo passaggio (one-pass). Da un punto di vista pratico, tuttavia, è a singolo passaggio dal momento che i blocchi sono piccoli e perciò sono letti soltanto una volta e poi mantenuti in memoria centrale
gzip - esempio abacbcaab
gzip – migliore compressione abacbcaab..... abcaab due soluzioni ab + caab a + bcaab La prima soluzione è greedy, cioè utilizza il match più lungo possibile. Ma talvolta la seconda soluzione è migliore. Se si sceglie di avere la migliore compressione possibile, gzip impiega più tempo ma sceglie la migliore soluzione fra le due, eventualmente codificando caratteri singoli anche se sono disponibili match più lunghi, se questo serve ad avere alla lunga una compressione migliore
La famiglia di algoritmi LZ78 - Ha delle restrizioni su quali sottostringhe possono essere riferite (ma questo elimina alcune inefficienze) - La decodifica è più lenta e richiede più memoria rispetto a LZ77 + non ha una finestra che limiti quanto indietro possiamo riferire le sottostringhe + una delle sue varianti, LZW, è usata in molti comuni sistemi di compressione
Stringhe riferibili Il testo antecedente la posizione corrente è scansito in sottostringhe, e solo loro possono essere riferite Le frasi precedenti sono numerate, e l'uscita è una lista di coppie Questa combinazione, che ancora non era stata mai incontrata, viene memorizzata come nuova frase
Un esempio aaaaabbb FrasiUscita 0 1a1a 2b2b 3aa 4ba 5baa Solo queste frasi possono essere riferite Questo elimina l'inefficienza di avere più di una possibile rappresentazione per la stessa stringa, come è comune in LZ77 a
Come sono memorizzate le frasi? E' cruciale per l'efficienza dell'algoritmo che le frasi siano memorizzate in modo furbo Si può ottenere utilizzando un trie Frasi 0 1a1a 2b2b 3aa 4ba 5baa a a a ab b
Come sono memorizzate le frasi? I caratteri di ogni frase specificano un percorso dalla radice ad una foglia I caratteri da codificare sono usati per attraversare il trie fino a che il percorso non termina L'ultimo nodo contiene il numero della frase da emettere Un nuovo nodo è aggiunto, a formare una nuova frase con il prossimo carattere di input a a a ab b baab 7 b
Un problema La struttura dati del trie continua a crescere durante la codifica e la sua crescita deve eventualmente essere arrestata per evitare di occupare troppa memoria Ci sono varie strategie Il trie può essere reinizializzato da zero Il trie può essere utilizzato così com'è, senza ulteriori aggiornamenti Il trie può essere parzialmente ricostruito utilizzando l'ultima parte di testo (e questo evita la penalità di dover ricominciare da zero)
LZ78 contro LZ77 La codifica LZ78 può essere più veloce La decodifica LZ78 è più lenta perché occorre memorizzare anche le frasi già analizzate
LZ78 - esercizio Si codifichi la sequenza con LZ78 e si mostri il trie che memorizza le frasi SOL. Frasi
La variante LZW - I Una delle più famose varianti degli algoritmi di codifica Lempel-Ziv (Welch 1984) E' alla base dell'utility compress e di altri compressori molto diffusi La differenza principale tra LZW e LZ78 è che LZW codifica solo i numeri delle frasi senza nessun carattere supplementare E il meccanismo funziona perché il dizionario viene inizializzato con una frase per ogni carattere dell'alfabeto sorgente, cioè normalmente i 256 caratteri del codice ASCII a 8 bit
La variante LZW - II Si costruisce una nuova frase appendendo il primo carattere della prossima frase Supponiamo di utilizzare il codice ASCII a 7-bit: il dizionario è inizializzato con 128 frasi (dalla 0 all 127)
LZW - codifica a baab baabaabaainput: output: nuove frasi 128 ab 129 ba 130 aa 131 aba 132 abb 133 baa 134 abaa
LZW - decodifica abaab baabaaba? input: output: nuove frasi a?abb?a?ab? ba?aba?abaabbbaaabaabaaa ? non è pronto! abaa abaa? Il ritardo nella creazione delle frasi non è un problema a meno che il codificatore utilizzi una frase immediatamente dopo la sua creazione. In questo caso, se il decodificatore inserisce le frasi solo quando sono complete, non riesce a decodificare perché non ha la frase 134
LZW - esercizio Codificare con LZW la sequenza 0102$00$10111$02$ utilizzando il codice ASCII a 8 bit Suggerimento: 0 => 48, 1 => 49, 2 => 50, $ => 36 SOL FRASI $ 260$ $ 263$ $ 267$ $?
Metodi Lempel-Ziv: riassunto LZ77 LZ78 gzip LZW
Metodi Lempel-Ziv: esercizio Codificare il messaggio con tutti i metodi studiati abbb010cac0bb0abb10111b1a utilizzando una finestra scorrevole di 7 bit e una lunghezza massima del match di 7 bit. La dimensione del dizionario, invece, si assume senza limiti Si faccia uso dei seguenti codici ASCII a 8-bit a => 97 b => 98 c => 99 0 => 48 1 => 49