Kompresja S艂ownikowa
LZ77, LZ78 i nowoczesne warianty jak DEFLATE i LZ4.
Nowa Strategia Znajdowania Redundancji: Poza Pojedyncze Symbole
W naszej eksploracji kompresji bezstratnej przeanalizowali艣my metody statystyczne, takie jak kodowanie Huffmana, kt贸re osi膮gaj膮 kompresj臋 poprzez skupienie si臋 na cz臋stotliwo艣ci wyst臋powania poszczeg贸lnych symboli. To podej艣cie jest bardzo skuteczne, ale pomija bardziej ustrukturyzowany, a cz臋sto i bardziej znacz膮cy rodzaj nadmiarowo艣ci w rzeczywistych danych: powtarzanie si臋 ca艂ych sekwencji, fraz i wzorc贸w bajt贸w.
Rozwa偶my prosty plik tekstowy zawieraj膮cy zdanie: "W Szczebrzeszynie chrz膮szcz brzmi w trzcinie". Kompresor statystyczny zauwa偶y艂by wysok膮 cz臋stotliwo艣膰 znak贸w 'z', 's' i 'r', przypisuj膮c im kr贸tkie kody. Jednak偶e nie wykorzysta艂by on faktu, 偶e ca艂a sekwencja liter "rz" pojawia si臋 wielokrotnie. Intuicyjnie wydaje si臋 to nieefektywne, aby ponownie kodowa膰 t臋 powszechn膮 zbitk臋 znak po znaku przy jej kolejnym wyst膮pieniu. Dlaczego nie powiedzie膰 po prostu: "powt贸rz sekwencj臋, kt贸r膮 widzieli艣my wcze艣niej"? Ta prosta idea jest kamieniem w臋gielnym , prawdopodobnie najbardziej wszechstronnej i szeroko stosowanej rodziny algorytm贸w kompresji bezstratnej obecnie. Te algorytmy, kt贸rych pionierami byli Jacob Ziv i Abraham Lempel w latach 70., sta艂y si臋 silnikami nap臋dzaj膮cymi uniwersalne standardy, takie jak ZIP, GZIP i PNG.
Algorytm LZ77: Kompresja z Przesuwnym Oknem
Opublikowany w 1977 roku algorytm LZ77 jest pierwsz膮 z dw贸ch fundamentalnych prac Lempela i Ziva. Jego geniusz tkwi w prostocie i wydajno艣ci. Zamiast budowa膰 ogromny, jawny s艂ownik ka偶dej napotkanej frazy, u偶ywa on sprytnej, stale aktualizowanej "pami臋ci" danych, kt贸re w艂a艣nie przetworzy艂.
Mechanizm Dzia艂ania: Przesuwne Okno
Algorytm dzia艂a przy u偶yciu koncepcji zwanej przesuwnym oknem, kt贸re jest symboliczn膮 ramk膮 poruszaj膮c膮 si臋 nad danymi wej艣ciowymi. Okno to jest podzielone na dwie cz臋艣ci:
- Bufor Wyszukiwania: To "historia" lub "niejawny s艂ownik". Jest to bufor o sta艂ym rozmiarze (np. kilka kilobajt贸w), zawieraj膮cy naj艣wie偶ej zakodowane dane. Algorytm b臋dzie przeszukiwa艂 ten bufor w poszukiwaniu powtarzaj膮cych si臋 sekwencji.
- Bufor Wej艣ciowy: Jest to mniejszy bufor zawieraj膮cy nast臋pny blok danych, kt贸ry ma zosta膰 zakodowany. Algorytm pobiera znaki z tego bufora i pr贸buje znale藕膰 dopasowanie w buforze wyszukiwania.
Proces Kodowania LZ77 w Szczeg贸艂ach
Kodowanie przebiega w ci膮g艂ej p臋tli:
- Algorytm patrzy na znaki na pocz膮tku bufora wej艣ciowego.
- Nast臋pnie starannie przeszukuje ca艂y bufor wyszukiwania, od pocz膮tku do ko艅ca, aby znale藕膰 najd艂u偶sze mo偶liwe dopasowanie dla sekwencji rozpoczynaj膮cej si臋 w buforze wej艣ciowym.
- Na podstawie wyniku wyszukiwania podejmuje decyzj臋 i wypisuje jeden z dw贸ch rodzaj贸w token贸w:
- Je艣li NIE znaleziono dopasowania: (lub dopasowanie jest kr贸tsze ni偶 minimalny pr贸g, np. 3 znaki), algorytm wypisuje token litera艂u. Jest to zazwyczaj nieskompresowany znak z bufora wej艣ciowego, cz臋sto poprzedzony flag膮 bitow膮 w celu odr贸偶nienia go od wska藕nika.
- Je艣li dopasowanie ZOSTANIE znalezione: Algorytm wypisuje token wska藕nika. To nie s膮 same dane, ale zwarty odno艣nik, kt贸ry m贸wi "wr贸膰 i skopiuj z przesz艂o艣ci". Wska藕nik ten zazwyczaj sk艂ada si臋 z trzech cz臋艣ci: flagi bitowej, przesuni臋cia (lub odleg艂o艣ci) i d艂ugo艣ci. Na przyk艂ad: .
- Odleg艂o艣膰 wskazuje, ile bajt贸w nale偶y cofn膮膰 si臋 w buforze wyszukiwania, aby znale藕膰 pocz膮tek dopasowania.
- D艂ugo艣膰 wskazuje, ile bajt贸w nale偶y skopiowa膰 od tego miejsca.
- Na koniec algorytm przesuwa okno. Przesuwa przetworzone dane (pojedynczy znak litera艂u lub ca艂膮 dopasowan膮 sekwencj臋 o d艂ugo艣ci ) z bufora wej艣ciowego do bufora wyszukiwania, a nowe dane z pliku wej艣ciowego s膮 odczytywane, aby uzupe艂ni膰 bufor wej艣ciowy. Proces powtarza si臋.
Magia Samonak艂adaj膮cych si臋 Dopasowa艅
Prawdziwie pot臋偶n膮 cech膮 LZ77 jest jego zdolno艣膰 do niezwykle wydajnego kodowania serii powtarzaj膮cych si臋 wzorc贸w. Rozwa偶my ci膮g "abababababab". Po przetworzeniu pocz膮tkowego "ab", kt贸re znajdzie si臋 w buforze wyszukiwania, bufor wej艣ciowy widzi "ababababab". Algorytm mo偶e znale藕膰 sekwencj臋 "ab" w buforze wyszukiwania, ale mo偶e wyda膰 wska藕nik, kt贸ry m贸wi "wr贸膰 o 2 bajty i skopiuj 10 bajt贸w".
Naiwny dekompresor m贸g艂by si臋 pogubi膰, ale dekompresor LZ77 dzia艂a bajt po bajcie. Wraca o 2 bajty, kopiuje 'a', potem 'b'. Zanim b臋dzie musia艂 skopiowa膰 trzeci bajt, pierwsze 'a', kt贸re w艂a艣nie zapisa艂, jest ju偶 w historii i dost臋pne do skopiowania. To pozwala dekompresorowi na replikacj臋 d艂ugich serii przy u偶yciu pojedynczego, kr贸tkiego wska藕nika, co czyni LZ77 niezwykle skutecznym dla danych z prostymi powt贸rzeniami.
Dekompresja: Prosta i Szybka
Pi臋kno LZ77 tkwi w jego szybkiej i prostej dekompresji. Dekompresor r贸wnie偶 utrzymuje bufor wyszukiwania (kt贸ry jest po prostu histori膮 tego, co ju偶 zapisa艂 na wyj艣ciu). Kiedy otrzymuje token:
- Je艣li to token litera艂u, po prostu do艂膮cza znak litera艂u do swojego wyj艣cia.
- Je艣li to token wska藕nika (odleg艂o艣膰, d艂ugo艣膰), cofa si臋 o 'odleg艂o艣膰' znak贸w w swoim w艂asnym wyj艣ciu i kopiuje 'd艂ugo艣膰' znak贸w na koniec.
Ten proces jest niezwykle szybki, poniewa偶 obejmuje tylko proste kopiowanie pami臋ci do pami臋ci, co jest g艂贸wnym powodem popularno艣ci schemat贸w opartych na LZ77.
Rodzina LZ78 i LZW: U偶ycie Jawnego S艂ownika
Drugi fundamentalny algorytm, LZ78 (opublikowany w 1978 r.), oraz jego niezwykle udany nast臋pca, LZW (Lempel-Ziv-Welch, opracowany w 1984 r.), przyjmuj膮 inne podej艣cie. Zamiast u偶ywa膰 ostatnich wynik贸w jako niejawnego s艂ownika, buduj膮 one jawn膮 tabel臋 fraz napotkanych do tej pory.
Mechanizm Dzia艂ania: Budowanie i Odwo艂ywanie si臋 do Ksi臋gi Fraz
Om贸wili艣my algorytm LZW szczeg贸艂owo w jego w艂asnej, dedykowanej sekcji. Podsumujmy jego g艂贸wn膮 filozofi臋 i por贸wnajmy j膮 z LZ77:
- Inicjalizacja: Algorytm zaczyna od s艂ownika wst臋pnie wype艂nionego wszystkimi mo偶liwymi pojedynczymi symbolami.
- Budowanie Fraz: Odczytuje dane wej艣ciowe i znajduje najd艂u偶sz膮 sekwencj臋 znak贸w, kt贸ra aktualnie znajduje si臋 w s艂owniku.
- Wyj艣cie i Aktualizacja: Wypisuje na wyj艣cie kod s艂ownikowy dla tej najd艂u偶szej znalezionej sekwencji. Nast臋pnie tworzy nowy wpis w s艂owniku sk艂adaj膮cy si臋 z tej sekwencji plus kolejnego znaku z wej艣cia.
LZ77 a LZW: Kluczowe R贸偶nice w Podej艣ciu
Chocia偶 oba s膮 koderami s艂ownikowymi, ich strategie s膮 odmienne. LZ77 zast臋puje powt贸rzenia par膮 `(odleg艂o艣膰, d艂ugo艣膰)` wskazuj膮c膮 wstecz w surowym strumieniu danych. LZW zast臋puje powt贸rzenia pojedynczym kodem ca艂kowitoliczbowym, kt贸ry odnosi si臋 do wpisu w oddzielnie zarz膮dzanym s艂owniku fraz. Oznacza to, 偶e LZW mo偶e by膰 bardziej zwi臋z艂y, je艣li dok艂adnie te same wieloznakowe "s艂owa" pojawiaj膮 si臋 cz臋sto w ca艂ym pliku, podczas gdy LZ77 mo偶e by膰 skuteczniejszy w wychwytywaniu przypadkowych powt贸rze艅, kt贸re niekoniecznie tworz膮 sp贸jne "s艂owa".
Nowoczesne Warianty i Hybrydy: Najlepsze z Obu 艢wiat贸w
Dzisiejsze najskuteczniejsze i najcz臋艣ciej u偶ywane uniwersalne kompresory bezstratne rzadko s膮 czystymi implementacjami tych wczesnych algorytm贸w. Zamiast tego s膮 to zaawansowane hybrydy, kt贸re 艂膮cz膮 si艂臋 dopasowywania fraz koder贸w s艂ownikowych z finezj膮 statystyczn膮 koder贸w entropijnych.
DEFLATE: Ko艅 Roboczy Internetu (ZIP, GZIP, PNG)
Algorytm DEFLATE jest prawdopodobnie najbardziej udanym algorytmem kompresji w historii. Jest to silnik nap臋dzaj膮cy wszechobecne formaty plik贸w ZIP i GZIP oraz u偶ywany do kompresji bezstratnej w formacie obraz贸w PNG. DEFLATE to genialne po艂膮czenie LZ77 i kodowania Huffmana.
- Etap 1: Eliminacja Duplikat贸w LZ77: Pierwszym etapem DEFLATE jest standardowy kompresor LZ77. Skanuje on dane za pomoc膮 przesuwnego okna (zazwyczaj 32KB) i zast臋puje powtarzaj膮ce si臋 ci膮gi wska藕nikami `(d艂ugo艣膰, odleg艂o艣膰)`. Ka偶dy znak, kt贸ry nie jest cz臋艣ci膮 powtarzaj膮cej si臋 sekwencji, jest przekazywany jako litera艂, nieskompresowany znak.
- Etap 2: Kodowanie Entropijne Huffmana: Wyj艣cie etapu LZ77 to mieszany strumie艅 litera艂贸w i wska藕nik贸w. Ten strumie艅 sam w sobie jest wysoce redundantny. Na przyk艂ad pewne d艂ugo艣ci dopasowa艅 s膮 znacznie cz臋stsze ni偶 inne, a pewne litera艂y (jak 'e' i spacja) b臋d膮 bardzo cz臋ste. DEFLATE wykorzystuje to, przepuszczaj膮c ten mieszany strumie艅 przez etap kodowania Huffmana. Dynamicznie buduje dwa drzewa Huffmana: jedno do kompresji litera艂贸w i d艂ugo艣ci dopasowa艅, a drugie do kompresji odleg艂o艣ci dopasowa艅.
To dwuetapowe podej艣cie jest niezwykle skuteczne. Etap LZ77 radzi sobie z powt贸rzeniami strukturalnymi na du偶膮 skal臋, podczas gdy etap Huffmana radzi sobie z redundancj膮 statystyczn膮 na ma艂膮 skal臋 pozosta艂ych danych i samych wska藕nik贸w.
LZ4: Pogo艅 za Ostateczn膮 Szybko艣ci膮
Chocia偶 DEFLATE zapewnia doskona艂膮 kompresj臋, jego poszukiwanie najd艂u偶szego dopasowania mo偶e by膰 czasoch艂onne. W wielu nowoczesnych zastosowaniach, zw艂aszcza tych zajmuj膮cych si臋 ogromnymi ilo艣ciami danych w pami臋ci, szybko艣膰 dekompresji jest czynnikiem najwa偶niejszym. Dla tej potrzeby opracowano algorytmy takie jak LZ4.
LZ4 jest wariantem LZ77, kt贸ry priorytetowo traktuje szybko艣膰. Osi膮ga pr臋dko艣ci dekompresji, kt贸re s膮 cz臋sto o rz膮d wielko艣ci wy偶sze ni偶 w DEFLATE. Osi膮ga to dzi臋ki kilku optymalizacjom, w tym u偶yciu tablicy haszuj膮cej do szybkiego znajdowania potencjalnych dopasowa艅 (zamiast skanowania ca艂ego bufora wyszukiwania) i posiadaniu bardzo prostego, szybkiego do parsowania formatu wyj艣ciowego. Jego wsp贸艂czynnik kompresji nie jest tak wysoki jak w bardziej z艂o偶onych algorytmach, ale w scenariuszach takich jak kompresja w bazach danych, systemach plik贸w w czasie rzeczywistym (np. ZFS, Btrfs) i 艂adowaniu zasob贸w gier, jego niewiarygodna pr臋dko艣膰 jest prze艂omowa.