
Kod stałej długości => każdemu znakowi przypisujesz kod
Kod zmiennej długości => Znakom występującym częściej dajemy krótszy kod.
Kod prefiksowy => żaden kod nie jest początkiem innego kodu. Czytasz bit po bicie i jak dopasujesz do jakiegoś znaku => wtedy wiesz że to on.
Kody te trzyma się w drzewie binarnym, żeby przyspieszyć lookup znaku odpowiadającemu danemu kodowi.
Problem optymalnego kodu prefiksowego
Szukamy drzewa binarnego, reprezentującego cały alfabet, które minimalizuje
gdzie d_T(c) to długość kodu znaku c. -> liczba krawędzi od korzenia do liścia zawierającego znak c.
Algorytm
function Huffman(C: zbior wezlow, f:C => N){
Q = new priority_queue<int>(C,f(c)) // kolejka priorytetowa z elementami c zainicjalizowana priorytetami nadanymi przez f;
while Q.Count >1 {
z <- new node();
z.left = q.extractMin();
z.right = q.Extractmin();
f(z) = f(x) + f(y);
}
return q.extractmin();
}
Idea algorytmu polega na tym, że w optymalnym drzewie nie ma wewnętrznych węzłów, które mają tylko jedno dziecko. Jeśli taki węzęł ma tylko jedno dziecko, to można je przepiąć do węzła wewnętrznego wyżej, co daje nam mniejszy .
Niech x i y to dwa najrzadziej używane znaki. Wtedy Istnieje optymalne drzewo kodowe, gdzie x i y mają wspólnego rodzica.
Jeśli drzewo T’ jest optymalnym drzewem kodowym dla alfabetu C’ = C - {x,y} {z}, gdzie f(z) = f(x) + f(y), wtedy T otrzymane z T’ przez dodanie x i y jako dzieci węzła z jest optymalne, bo
Gdyby istniało B(T*) < B(T) dla C, to zbudowalibyśmy drzewo T” dla C’ o koszcie
B(T*) - f(x) - f(y) < B(T’) , co jest sprzeczne z optymalnością T’.
Indukcja po rozmiarze alfabetu kończy dowód.
Pierwsza to własność zachłannego wyboru (slajd 12): istnieje jakieś optymalne drzewo, w którym x i y (dwa najrzadsze) są rodzeństwem na najgłębszym poziomie. Dowód przez wymianę: weź dowolne optymalne T. Z lematu o pełnym drzewie najgłębszy liść ma rodzeństwo — nazwij tę parę a, b. Zamień x z a oraz y z b. Policz zmianę kosztu przy zamianie x i a:
a jest na najgłębszym poziomie, więc d(a)≥d(x)d(a) \ge d(x) d(a)≥d(x); x jest najrzadszy, więc f(x)≤f(a)f(x) \le f(a) f(x)≤f(a). Iloczyn nieujemnego i niedodatniego jest ≤0\le 0 ≤0, czyli koszt nie rośnie. Skoro T było optymalne, koszt nie może też spaść — więc się nie zmienił, a nowe drzewo jest równie optymalne. To pokazuje, że zachłanny ruch (sklejenie dwóch najrzadszych) niczego nie psuje: jest optymalne drzewo, które się z nim zgadza.