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