LZ77

Lempel-Ziv 77, skracane zwykle do LZ77 (algorytm LZ77) – metoda strumieniowej słownikowej kompresji danych. Metoda LZ77 wykorzystuje fakt, że w danych powtarzają się ciągi bajtów (np. w tekstach naturalnych będą to słowa, frazy lub całe zdania) – kompresja polega na zastępowaniu powtórzonych ciągów o wiele krótszymi liczbami wskazującymi, kiedy wcześniej wystąpił ciąg i z ilu bajtów się składał; z punktu widzenia człowieka jest to informacja postaci „taki sam ciąg o długości 15 znaków wystąpił 213 znaków wcześniej”.

Algorytm LZ77 jest wolny od wszelkich patentów, co w dużej mierze przyczyniło się do jego popularności i szerokiego rozpowszechnienia. Doczekał się wielu ulepszeń i modyfikacji, dających lepsze współczynniki kompresji albo dużą szybkość działania. Na LZ77 opiera się m.in. algorytm deflate, używany jest również w formatach ZIP, gzip, ARJ, RAR, PKZIP, a także PNG.

Algorytm został opracowany w 1977 przez Abrahama Lempela i Ja’akowa Ziwa i opisany w artykule A universal algorithm for sequential data compression opublikowanym w IEEE Transactions on Information Theory (s. 8–19)[1]. Rok później autorzy opublikowali ulepszoną wersję metody, znaną pod nazwą LZ78.

Organizacja IEEE uznała algorytm Lempel-Ziv za kamień milowy w rozwoju elektroniki i informatyki[2].

Zasada działania

[edytuj | edytuj kod]

W LZ77 zapamiętywana jest w słowniku pewna liczba ostatnio kodowanych danych – przeciętnie kilka do kilkudziesięciu kilobajtów. Jeśli jakiś ciąg powtórzy się, to zostanie zastąpiony przez liczby określające jego pozycję w słowniku oraz długość ciągu; do zapamiętania tych dwóch liczb trzeba przeznaczyć zazwyczaj o wiele mniej bitów niż do zapamiętania zastępowanego ciągu.

Metoda LZ77 zakłada, że ciągi powtarzają się w miarę często, tzn. na tyle często, żeby wcześniejsze wystąpienia można było zlokalizować w słowniku – ciągi powtarzające się zbyt rzadko nie są brane pod uwagę. Wady tej pozbawiona jest metoda LZ78, w której – przynajmniej teoretycznie – słownik rozszerza się w nieskończoność.

Bardzo dużą zaletą kodowania LZ77 (a także innych metod słownikowych z rodziny LZ, tj. LZSS, LZ78, LZW itp.) jest to, że słownika nie trzeba zapamiętywać i przesyłać wraz z komunikatem – zawartość słownika będzie na bieżąco odtwarzana przez dekoder.

Algorytm kompresji jest bardziej złożony i trudniejszy w realizacji niż algorytm dekompresji. W metodzie LZ77 można wpływać na prędkość kompresji oraz zapotrzebowania pamięciowe, regulując parametry kodera (rozmiar słownika i bufora kodowania).

Algorytm kompresji

[edytuj | edytuj kod]

Metoda LZ77 korzysta z bufora (okna), który logicznie podzielony jest na dwie części:

  • bufor słownikowy (słownik), przechowujący ostatnio przetwarzanych symboli (sufiks);
  • bufor wejściowy (lub bufor kodowania), przechowujący symboli do zakodowania.

Bufor słownikowy obejmuje indeksy bufor wejściowy

Rozmiary i mogą być dowolne, w praktyce dobiera się je tak, aby były całkowitą potęgą dwójki, dzięki czemu w pełni wykorzystywane są słowa bitowe przeznaczone do ich zapamiętania. Rozmiar bufora słownikowego wynosi w praktyce kilka-kilkadziesiąt kilobajtów, bufor kodowania jest dużo mniejszy. Na przykład w programie gzip słownik ma 32 kB, bufor kodowania natomiast 258 bajtów.

Algorytm przebiega następująco:

  • Bufor słownikowy jest wypełniany pierwszym symbolem wejściowym i ten symbol jest zapisywany na wyjście[3].
  • Do bufora wejściowego wstawiane jest pierwszych symboli wejściowych.
  • Dopóki w buforze wejściowym są jakieś dane:
    1. W obrębie bufora słownikowego wyszukiwany jest najdłuższy podciąg równy początkowi bufora wejściowego (najdłuższy prefiks bufora kodowania). Wynikiem wyszukiwania jest indeks początku wyszukanego podciągu oraz jego długość ograniczona z góry przez Część podciągu może być wspólna z buforem wejściowym!
      Jeśli podciągu nie uda się znaleźć, to może mieć dowolną wartość, natomiast
    2. Na wyjście wyprowadzana jest trójka ( symbol z bufora wejściowego następujący po dopasowanym podciągu).
    3. Okno (bufor słownikowy + bufor wejściowy) przesuwane jest w lewo o pozycji i na koniec bufora dopisywane jest tyleż kolejnych symboli wejściowych (o ile jeszcze są jakieś).

Stopień kompresji LZ77 w dużej mierze zależy od długości słownika oraz długości bufora wejściowego (bufora kodowania). Programy wykorzystujące tę metodę mają zwykle możliwość ustalania rozmiaru słownika, istnieją również programy dynamicznie zmieniające rozmiar słownika.

Prędkość kompresji natomiast jest uzależniona głównie od efektywności procedury wyszukującej najdłuższy prefiks. Używane są tutaj różne rozwiązania. Np. w programie gzip używa się tablicy haszującej adresowanej pierwszymi trzema literami z bufora kodowania. Stosowane są również zwykłe tablice, a do lokalizacji prefiksu używa się algorytmów wyszukiwania wzorca, np. algorytmu Karpa-Rabina.

Jeśli słownik jest realizowany jako tablica, to aby uniknąć fizycznego, czasochłonnego przesuwania danych w oknie (punkt 3. algorytmu), używa się buforów cyklicznych.

Przykładowy krok algorytmu

[edytuj | edytuj kod]

1. Wyszukanie najdłuższego podciągu równego początkowi bufora wejściowego (tutaj: aac).

          słownik               |  bufor wejściowy  | nieprzetworzone symbole   0   1   2   3   4   5   6   7 | 0   1   2   3   4 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+----- | a | a | a | a | c | c | a | b | a | a | c | b | a | c | a | b | a | c | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+----- |                      okno                         | 

2. Wynik wyszukiwania:

P = 2 (pozycja w słowniku) C = 3 (długość podciągu) S = b (symbol z bufora wejściowego następujący po dopasowanym podciągu) 

3. Przesunięcie bufora o pozycji, dopisanie do bufora wejściowego nieprzetworzonych symboli.

          słownik               |  bufor wejściowy  | nieprzetworzone symbole   0   1   2   3   4   5   6   7 | 0   1   2   3   4 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+--------------------- | c | c | a | b | a | a | c | b | a | c | a | b | a | c | ... +---+---+---+---+---+---+---+---+---+---+---+---+---+---+--------------------- 

Przykład

[edytuj | edytuj kod]
Kilka początkowych kroków LZ77
sytuacja początkowa
inicjacja słownika pierwszym symbolem, wypisanie go na wyjście
przesunięcie okna o 1 pozycję w lewo
symbolu 'b' nie ma w słowniku
przesunięcie okna o 1 pozycję
znaleziono ciąg 'aa' (długość 2, pozycja 0), wypisanie dodatkowo następnego symbolu z bufora kodowania 'c'
przesunięcie okna o 2+1 pozycji
znaleziono ciąg 'baa' (długość 3, pozycja 2), wypisanie na wyjście pozycji tego ciągu oraz następnego symbolu z bufora kodowania 'c'
przesunięcie okna o 3+1 pozycji
symbolu 'd' nie ma w słowniku (itd.)

Algorytm dekompresji

[edytuj | edytuj kod]

Dekompresja danych w metodzie LZ77 jest o wiele prostsza i szybsza niż kompresja. Do zdekodowania wymagane jest istnienie bufora (okna) o dokładnie takich samych parametrach jak przy kodowaniu – bufor podzielony jest na część słownikową obejmującą pierwszych pozycji i bufor wyjściowy zajmujący kolejnych pozycji.

Dekodowanie przebiega następująco:

  • Bufor słownikowy jest wypełniany początkowym symbolem
  • Dla wszystkich trójek () wykonuj:
    1. Skopiuj z bufora słownikowego do bufora wyjściowego symbole z zakresu
    2. Doklej na koniec bufora wyjściowego symbol
    3. Na wyjście wyprowadź początkowych symboli z bufora wyjściowego.
    4. Przesuń zawartość bufora o pozycji w lewo.

Ad 1) Jeśli przy kompresji podciąg obejmował także bufor wejściowy, to należy bufor słownikowy tymczasowo „przedłużyć” i wypełnić brakującymi symbolami. Podciągi takie mają strukturę xYxYx, gdzie x to pojedynczy symbol, a Y ciąg znajdujący się już w buforze słownikowym. Uzupełnienie polega na cyklicznym kopiowaniu końcówki bufora słownikowego (którą obejmuje początek podciągu). Np. jeśli podciąg ma długość 8 i tylko 3 symbole bca znajdują się w słowniku, to odtworzony ciąg ma postać: bcabcabc. Nie ma potrzeby fizycznego rozszerzania słownika i kopiowania danych – wystarczy odpowiednio przeliczać indeksy.

Tego specjalnego przypadku można uniknąć na etapie kompresji, biorąc pod uwagę tylko te podciągi, które w całości znajdują się w słowniku – przy dużych rozmiarach słownika i niewielkich bufora wejściowego praktycznie nie rzutuje to na stopień kompresji i jest to rozwiązanie stosowane w rzeczywistych zastosowaniach.

Modyfikacja algorytmu

[edytuj | edytuj kod]

Algorytm LZ77 stał się podstawą dla wielu rozwiązań:

  • LZR (1981) – słownik o nieograniczonej wielkości (indeksy do słownika kodowane kodem omega Eliasa)
  • LZSS (1982) – słowa kodowe dwojakiego rodzaju: pojedynczy symbol albo para: (pozycja dopasowania, długość dopasowania)
  • LZB (1987) – wariant LZSS, w którym zarówno odległość dopasowania, jak i jego długość nie są ograniczone (długość kodowana kodem gamma Eliasa)
  • LZH (1987) – zarówno indeks, jak i długość dopasowania kodowane kodem Huffmana
  • LZS – algorytm używany w komercyjnym programie Stacker
  • LZO.

Przykład kompresji

[edytuj | edytuj kod]

Niech długość słownika i bufora wejściowego będą równe 4, tzn. Do zapisu pozycji w słowniku (P), jak i długości ciągu (C) wystarczą 2 bity. Kodowany komunikat składa się z trzynastu symboli: aabbcabbcdddc.

słownik/bufor wejściowy wyjście kodera uwagi
1 aaaa/aabb a słownik wypełniany jest pierwszym symbolem, symbol ten jest zapisywany
2 aaaa/aabb (0,2,b) w słowniku znajduje się 2-elementowy podciąg równy początkowi bufora wejściowego; bufor jest przesuwany o 3 pozycje w lewo
3 aaab/bcab (3,1,c) w słowniku znajduje się 1-elementowy podciąg równy początkowi bufora wejściowego; bufor jest przesuwany o 2 pozycje w lewo
4 abbc/abbc (0,3,c) w słowniku znajduje się 3-elementowy podciąg równy początkowi bufora wejściowego; bufor jest przesuwany o 4 pozycje w lewo
5 abbc/dddc (0,0,d) w słowniku nie ma żadnego podciągu zaczynającego się od d; bufor jest przesuwany o 1 pozycję w lewo
6 bbcd/ddc (3,2,c) w buforze znajduje się 2-elementowy podciąg równy początkowi bufora wejściowego: pierwszy element ciągu znajduje się w słowniku, drugi w buforze wejściowym; bufor jest przesuwany o 3 pozycje w lewo

Zakodowanych zostało 13 symboli; symbole to przeważnie bajty (8 bitów), więc cały komunikat bity.

Dane wyjściowe kodera to:

  • początkowy symbol – 8 bitów,
  • pięć trójek; każda trójka zajmuje 12 bitów (2 bity na indeks podciągu, 2 na jego długość i 8 bitów na symbol).

Ostatecznie rozmiar skompresowanych danych wynosi 68 bitów, co daje współczynnik kompresji ok. 34%.

Przykład dekompresji

[edytuj | edytuj kod]

Dekompresji zostaną poddane dane otrzymane we wcześniejszym przykładzie:

  • symbol początkowy a;
  • trójki: (0,2,b), (3,1,c), (0,3,c), (0,0,d), (3,2,c).
# wejście dekodera bufor wyjściowy wyjście dekodera uwagi
1 a aaaa/ słownik jest wypełniany początkowym symbolem
2 (0,2,b) aaaa/aab aab ze słownika do bufora wyjściowego kopiowane są 2 symbole i „doklejany” 3. symbol z trójki; bufor wyjściowy jest wyprowadzany na wyjście, a cały bufor przesuwany o 3 pozycje w lewo
3 (3,1,c) aaab/bc bc ze słownika do bufora wyjściowego kopiowany jest jeden symbol i „doklejany” 2. symbol z trójki; bufor wyjściowy jest wyprowadzany na wyjście, a cały bufor przesuwany o 2 pozycje w lewo
4 (0,3,c) abbc/abbc abbc ze słownika do bufora wyjściowego kopiowane są 3 symbole i „doklejany” 4. symbol z trójki; bufor wyjściowy jest wyprowadzany na wyjście, a cały bufor przesuwany o 4 pozycje w lewo
5 (0,0,d) abbc/d d do bufora wyjściowego wprowadzany jest tylko jeden symbol zapisany w trójce; bufor przesuwany jest o 1 pozycję w lewo
6 (3,2,c) bbcd/ddc ddc przed wypełnieniem bufora wyjściowego słownik zawiera 4 symbole: bbcd, kopiowane na wyjście mają być 2 symbole, w tym jeden nie zapisany w słowniku – istniejący symbol jest powielany i słownik na chwilę wydłużany: bbcdd; podkreślone symbole są kopiowane do bufora wyjściowego, dopisywany jest symbol z trójki, a cały bufor wyjściowy kopiowany na wyjście

Ostatecznie zdekodowany komunikat ma postać: aabbcabbcdddc.

Zobacz też

[edytuj | edytuj kod]

Przypisy

[edytuj | edytuj kod]
  1. Praca w 1979 roku uznana przez IEEE Information Theory Society za najlepszą publikację z dziedzinie teorii informacji; A universal algorithm for sequential data compression. itsoc.org. [dostęp 2010-09-23]. (ang.).
  2. IEEE History Center: Milestones:Lempel-Ziv Data Compression Algorithm, 1977. ieee.org: IEEE. [dostęp 2013-09-21]. (ang.).
  3. Sposób inicjowania słownika może być inny, jedynym wymogiem jest, aby został jednakowo przeprowadzony przy kodowaniu i dekodowaniu.

Bibliografia

[edytuj | edytuj kod]
  • Adam Drozdek: Wprowadzenie do kompresji danych. Warszawa: Wydawnictwa Naukowo-Techniczne, 1999. ISBN 83-204-2303-1.
  • Artur Przelaskowski: Kompresja danych: podstawy, metody bezstratne, kodery obrazów. Warszawa: BTC, 2005, s. 158. ISBN 83-60233-05-5.

Linki zewnętrzne

[edytuj | edytuj kod]