dwa zera
Transkrypt
dwa zera
Temat: Algorytm kompresji plików metodą Huffmana 1. Wymagania dotyczące kompresji danych Przez M oznaczmy zbiór wszystkich możliwych symboli występujących w pliku (alfabet pliku) . Przykład M= 2, gdy plik tekstowy zawiera znaki alfabetu Morse'a: kropka i kreska M= 5, gdy plik tekstowy ma następującą zawartość: abaacdaae M = {a, b, c, d, e} M= 256 dla dowolnego pliku, jeżeli każdemu bajtowi pliku zinterpretowanemu jako znak (typ char) przyporządkujmy jego kod ASCII. Załóżmy, że znane jest prawdopodobieństwo wystąpienia dowolnego znaku mi (i=1, 2, ..., n; gdzie n = M) w alfabecie M. Ustalamy również, że wszystkie znaki alfabetu chcemy zakodować w postaci ciągów zerojedynkowych. Oznaczmy to prawdopodobieństwo przez P(mi). Wówczas: P (m1 ) + ... + P (m n ) = 1 1 Def. Entropią źródła M nazywamy wartość wyrażenia (*) Lave = P (m1 ) ⋅ L(m1 ) + ... + P (mn ) ⋅ L(mn ) , gdzie L(mi ) = − log 2 (P(mi )) . Wartość L(mi ) ma sens minimalnej długości kodu dla symbolu mi. W roku 1948 Claude E. Shannon wykazał, że równanie (*) daje najlepszą możliwą średnią długość kodu, kiedy symbole tworzące kod i częstości ich wystąpienia są znane. Żaden algorytm kompresji danych nie może dawać wyniku lepszego niż Lave , a im bliższy jest tej liczby, tym lepszy (wyższy) jest współczynnik kompresji zdefiniowany następująco: długość ciągu wejściowego - długość ciągu wyjściowego długość ciągu wejściowego Przykład Niech M= 3, a prawdopodobieństwa wystąpienia poszczególnych symboli wynoszą: P(m1 ) = 0,25, P(m2 ) = 0,25, P(m3 ) = 0,5 . Wówczas długości minimalne przypisanych kodów będą wynosiły: 1 − log 2 (P(m1 )) = − log 2 (P(m2 )) = − log 2 (0,25) = log 2 = log 2 (4) = 2 0,25 oraz − log 2 (P(m3 )) = log 2 (2) = 1 , a średnia długość kodu będzie wynosiła Lave = P (m1 ) ⋅ 2 + P (m2 ) ⋅ 2 + P (m3 ) ⋅ 1 = 1,5 . 2 W metodach kompresji danych dąży się do zminimalizowania średniej długości kodu, konstruując kod optymalny wg zasady: Im mniejsze prawdopodobieństwo wystąpienia znaku tym dłuższy jest jego kod kompresji. Aby kompresja była poprawna muszą być spełnione następujące warunki: 1) Każdy kod odpowiada dokładnie jednemu symbolowi. 2) Dekodowanie nie powinno wymagać podglądania większego fragmentu zakodowanego tekstu. Po wczytaniu z pliku pojedynczego symbolu powinniśmy umieć stwierdzić, czy osiągnięty został koniec napisu kodującego symbo pierwotnej wiadomości. Nie są więc potrzebne żadne specjalne znaki oddzielające dwa kody w sąsiedniej wiadomości. 3 Przykład Trzy różne sposoby zakodowania trzech symboli: Symbol A B C Kod 1 Kod 2 Kod 3 1 1 11 0 00 10 10 10 01 - Pierwszy kod nie rozróżnia ciągu znaków AB od C. AB: 10 C : 10 - Drugi kod wymaga podglądania następnych znaków. W ciągu: 1000 pierwszy znak 1 można zakodować jako A. - Wtedy następne dwa zera sugerują, że po A występuje B. Z tym jednak, że ostatnie zero nie koduje żadnego symbolu alfabetu. Zatem początek kodu musi "dawać" C, a pozostałe dwa zera dają B. Stąd poprawnie odkodowany ciąg to CB. Możemy to ustalić dopiero po odczytaniu całego ciągu 1000. - Jedynie Kod 3 spełnia warunki 1) i 2). 3) Długość kodu danego symbolu nie powinna przekraczać długości kodu symbolu mniej prawdopodobnego. Czy jeśli P (mi ) ≥ P m j , to L(mi ) ≤ L m j dla i ≥ 1, j ≤ n. ( ) ( ) 4) W optymalnym systemie kodowania nie powinny się być wykorzystane kody o długości k zanim nie zostaną wykorzystane optymalne kody o długościach mniejszych od k. Gdyby ten warunek nie był spełniony, to oznaczałoby to niepotrzebne wydłużanie kodów. 4 Przykład Ciąg kodów 01, 000, 001, 100, 101 dla pewnego alfabetu nie jest optymalny, ponieważ kod 11 nigdzie nie jest używany. Kodowanie to można przekształcić w optymalny ciąg 01, 10, 11, 000, 001. Ciąg ten spełnia warunki od 1) do 4). 2. Metoda Huffmana Metoda kompresji Huffmana oparta jest na algorytmie tworzenia tzw. drzewa Huffmana. Idea algorytmu tworzenia drzewa Huffmana 1. Dla każdego symbolu utwórz jednowęzłowe drzewo. 2. Uporządkuj wszystkie drzewa niemalejąco względem prawdopodobieństwa wystąpień symboli. 3. Weź dwa drzewa d1 i d2 o najmniejszych prawdopodobieństwach p1 i p2 występowania symboli i utwórz drzewo o synach d1 i d2 i prawdopodobieństwie w korzeniu równym p1 + p2. 4. Krok 3 powtarzaj aż do momentu, gdy zostanie tylko jedno drzewo. 5. Każdą krawędź skierowaną w lewo oznacz zerem, a każdą skierowaną w prawo jedynką; 6. Utwórz kod dla każdego symbolu, przechodząc drzewo od korzenia do liścia odpowiadającego temu symbolowi i łącząc napotykane zera i jedynki. W korzeniu otrzymanego drzewa prawdopodobieństwo wynosi 1. 5 Przykład Przyjmijmy, że: M= 5, M={A, B, C, D, E}, a prawdopodobieństwa wystąpienia poszczególnych symboli wynoszą: P( A) = 0,09, P(B ) = 0,12, P(C ) = 0,19, P(D ) = 0,21, P(E ) = 0,39 1. Drzewa jednowęzłowe 0,09 A 0,12 B 0,19 C 0,21 D 0,39 E 2. Etapy budowania drzewa Huffmana 0,40 0,19 C 0,21 0,09 A 0,21 D 0,39 E 0,12 B 6 1,0 0,60 0,40 0,21 0,19 C 0,09 A 0,21 D 0,39 E 0,12 B Dla ustalenia efektywności kompresji metodą Huffmana wykorzystuje się pojęcie ważonej długości ścieżki Lhuf, definiowanej tak samo jak Lave w równaniu (*), tylko wartości L(mi) zastępujemy długością kodu dla symbolu mi . Tak więc: Lave = 0,09 ⋅ 3,474 + 0,12 ⋅ 3,059 + 0,19 ⋅ 2,396 + 0,21 ⋅ 2, 252 + 0,39 ⋅ 1,238 = 2,09 Lhuf = 0,09 ⋅ 3 + 0,12 ⋅ 3 + 0,19 ⋅ 2 + 0,21 ⋅ 2 + 0,39 ⋅ 2 = 2,21 Ważona długość ścieżki różni się nieznacznie, bo tylko o 5% od entropii źródła. 7 Zauważmy, że dla każdego przypadku drzewa Huffmana zbudowanego dla tego samego pliku, otrzymujemy taką samą ważoną długość ścieżki. Prześledzimy teraz na przykładzie pewnego pliku wszystkie kroki algorytmów: kompresji i dekompresji metodą Huffmana. Dla uproszczenia będziemy zakładali, że kompresji poddajemy "krótki" plik tekstowy odczytywany znak po znaku. Plik: ABAACBDABBCEDAE Algorytm kompresji metodą Huffmana 1. Przeglądamy plik i ustalamy tablicę częstości wystąpienia poszczególnych symboli: A-5 B-4 C-2 D-2 E- 2 2. Tworzymy listę jednowęzłowych drzew symboli z częstościami uporządkowaną niemalejąco wg częstości Lista: E - 2 C - 2 D-2 B-4 A-5 3. Na podstawie listy tworzymy drzewo Huffmana 15 0 1 9 6 0 D-2 1 0 B-4 4 0 E-2 1 A-5 1 C-2 8 4. Ustalamy kody kompresji poszczególnych symboli ma podstawie drzewa Huffmana. A: 11 B: 01 C: 101 D: 00 E: 100 5. Odczytujemy znaki pliku, który jest poddawany kompresji i przypisujemy im odpowiednie kody kompresji. Plik: ABAACBDABBCEDAE Kody kompresji:1101111110101001101011011000011100 5. Do pliku powstającego po kompresji wstawiamy: - tablicę częstości symboli niezbędną przy dekompresji, - znaki o kodach obliczonych jako liczba dziesiętna powstała po zamianie ośmiobitowych serii kodu kompresji Kody kompresji z podziałem na serie ośmiobitowe: 11011111 10101001 10101101 10000111 00 I bajt II bajt III bajt IV bajt V bajt (niepełny) I bajt: (11011111)2 = (223)10 II bajt: (11010100)2 = (212)10 III bajt: (10101101)2 = (173)10 IV bajt: (10000111) 2 = (135)10 V bajt uzupełniony: (00 000000) 2 = (0)10 Plik powstały w wyniku kompresji: 5 4 2 2 2 #223 #212#173#135#0 (#k - operator zwracający znak o kodzie k) 9 Algorytm dekompresji metodą Huffmana Plik powstały w wyniku kompresji: 5 4 2 2 2 #223 #212#173#135#0 1. Odczytujemy częstości wystąpienia symboli i obliczamy liczbę elementów w pliku, który został skompresowany. Liczba elementów pliku przed kompresją: 5+4+2+2+2=15 2. Na podstawie tablicy częstości budujemy drzewo Huffmana (identycznie jak w algorytmie kompresji) 15 1 0 9 6 0 D-2 1 0 B-4 4 0 E-2 1 A-5 1 C-2 3. Ustalamy kody kompresji poszczególnych symboli na podstawie drzewa Huffmana (identycznie jak w algorytmie kompresji) A: 11 B: 01 C: 101 D: 00 E: 100 10 4. Odczytujemy znaki pliku, który jest poddawany dekompresji. Każdemu znakowi odczytanemu z pliku przyporządkowujemy ośmiobitową serię zerojedynkową stanowiącą reprezentację dwójkową kodu znaku. I bajt: (223)10= (11011111)2 II bajt: (212)10 = (11010100)2 III bajt: (173)10 = (10101101)2 IV bajt: (135)10 = (10000111) 2 V bajt: (0)10 = (00000000) 2 Plik powstały w wyniku kompresji: 5 4 2 2 2 #223 #212#173#135#0 (#k - operator zwracający znak o kodzie k) 5.Ustalamy symbole po dekompresji na podstawie powstałego ciągu binarnego, kodów kompresji i drzewa Huffmana. Ciąg binarny : 1101111110101001101011011000011100 Sym.po dekomp.: A B A A CBDA B B C E D A E 6 "dostawionych" bitów - Ustalenie pojedynczego symbolu po dekompresji zaczyna się zawsze w korzeniu drzewa Huffmana, a kończy po osiągnięciu liścia w tym drzewie. - Dzięki temu, że znamy liczbę elementów pliku, który został poddany kompresji możemy po ustaleniu 15 symboli po dekompresji zakończyć proces dekodowania i tym samym "dostawione" bity zostaną zignorowane. 11 Koszt czasowy algorytmu kompresji: Rozmiar zadania: n - rozmiar alfabetu m - liczba znaków pliku, który kompresujemy 1. Tworzenie uporządkowanej listy jednowęzłowych drzew kosztuje optymalnie Θ(nlogn). 2. Jeden krok procesu scalania dwóch węzłów drzewa Huffmana jest realizowany kosztem stałym Θ(1). Cały proces tworzenia drzewa Huffmana kosztuje zatem Θ(n2). 3. Proces ustalania wszystkich kodów kompresji kosztuje Θ(n).Można go zrealizować stosując metodę przeglądania drzewa binarnego w porządku inorder (poprzeczny: L – K– P). 4. Krok algorytmu kompresji, który ustala kody znaków wpisywanych do skompresowanego pliku ma również koszt rzędu Θ(m). Stąd wynika, że koszt algorytmu kompresji metodą Huffmana pliku zawierającego m znaków nad n elementowym alfabetem wynosi Θ(n2+m). 12