Przeczytaj
W roku 1952 David Huffman opracował prefiksową metodę kompresji bezstratnejkompresji bezstratnej.
Mówimy, że kodowanie jest prefiksowe, gdy żadne słowo kodowesłowo kodowe nie jest początkiem innego słowa.
Słowem kodowym w kontekście kodowania Huffmana określamy ciągi bitów, które jednoznacznie interpretują jakiś znak.
Zbadajmy przykład zastosowania kodów o stałej długości, kodując pojedynczą literę za pomocą ciągu binarnego o długości 2. W podobny sposób skonstruowany jest system ASCIIASCII.
Spróbujmy teraz zastosować kody o zmiennej długości.
W przypadku zastosowania kodów o zmiennej długości żaden kod nie może być prefiksem innego kodu. Ten warunek zapewnia, że tekst będzie dało się odkodować jednoznacznie. W powyższym przykładzie znak A jest prefiksem B, dlatego kodowanie jest błędne.
Binarne drzewo Huffmana ma pewną właściwość – jest ono regularne, co oznacza, że każdy węzeł ma zero lub dwóch potomków.
Funkcja, którą napisaliśmy powyżej, równie dobrze nada się do kodowania tekstu o dowolnej długości słów kodowych.
Zdekodowanie takiej wiadomości nie będzie już tak proste. Wynika to z faktu, że nie możemy podzielić wiadomości na określoną liczbę części, a następnie odkodować każdej z nich po kolei.
Aby dobrze zaprojektować drzewo, wykorzystamy tabelę częstości pojawiania się znaków w ciągu do zakodowania. Zdefiniujemy klasę zawierającą odpowiednie metody oraz funkcję tworzącą drzewo.
Musimy pamiętać o posortowaniu elementów tabeli. Dzięki dynamicznemu obliczaniu częstościczęstości występowania znaków otrzymamy wydajny algorytm. Stosując go, za każdym razem uzyskamy optymalny wynik: najkrótszy kod będzie odpowiadał najczęściej pojawiającemu się znakowi (zaś kod najdłuższy – najrzadziej występującej literze). Np. tworząc drzewo dla wierzchołków o częstości kolejno 1, 2, 3, najpierw połączymy ze sobą wierzchołki 1 i 2. Kolejnym krokiem będzie połączenie nowo utworzonego węzła z wierzchołkiem o częstości 3. Węzły, które łączymy najpierw, będą ostatecznie znajdowały się głębiej w drzewie, co oznacza, że trzeba będzie przejść po większej liczbie gałęzi, aby do nich dotrzeć. Podczas kodowania każda z gałęzi odpowiada jednemu znakowi. Oznacza to, że im płycej w drzewie położony jest dany wierzchołek, tym mniejsza będzie długość jego kodu.
Wymaga to zapamiętywania tablicy kodów znaków.
Poniżej przykład implementacji i wywołania funkcji, tworzącej posortowaną tabelę częstości dla zadanego parametru napis
. Funkcja dla każdego znaku występującego w ciągu znaków napis
, zlicza liczbę jego wystąpień, a następnie zebrane dane sortuje nierosnąco po liczbie wystąpień.
Bazując na przygotowanej tabeli, utworzymy klasę, dzięki której zbudujemy drzewo binarne. Zdefiniujemy też funkcję, która będzie odpowiadać za skonstruowanie słownika tablicy kodów znaków oraz jego odwróconej wersji.
Teraz możemy już zdefiniować funkcje, które używając słownika, zakodują i odkodują ciąg znaków.
W tej sekcji w programie wykorzystaliśmy słowo kluczowe lambda. W języku Python pozwala ono na deklarowanie funkcji anonimowych.
Wyrażenia lambda używamy jako argumentu funkcji wyższego rzędu (czyli funkcji, która przyjmuje inne funkcje jako argumenty).
Te funkcje w programach często stosuje się przy okazji sortowania. Stanowią one wyrażenia zwracające klucz (wartość), według którego mają zostać posortowane elementy.
Dana jest następująca lista:
W jaki sposób możemy ją wykorzystać?
sorted(szczyty)
– posortuje listę według pierwszego elementu każdej tupli, czyli nazwy szczytu;sorted(szczyty, key=lambda x: x[1])
– posortuje listę według klucza zwróconego przez jednoargumentową funkcję lambda, w tym wypadku będzie to wysokość szczytu (nazwa szczytu znajduje się pod indeksem 0, a rok zdobycia szczytu pod indeksem 2);max(szczyty, key=lambda x: x[1])
– zwróci najwyższy szczyt;min(szczyty, key=lambda x: x[2])
– zwróci szczyt najwcześniej zdobyty.
Przyjrzyjmy się przykładowemu programowi.
Dana jest lista szczyty
, która składa się z pięciu krotek. Każda krotka zawiera dane o nazwie szczytu, jego wysokości oraz roku, w którym szczyt został zdobyty.
Chcemy wyświetlić krotkę przechowującą informacje o najwcześniej zdobytym szczycie, dlatego skorzystamy z polecenia print
.
Wykorzystamy wbudowaną w język Python funkcję min()
.
Przeszukujemy listę szczyty
.
Opcjonalny argument key
opisuje, jak porównać elementy, aby znaleźć minimum.
Wykorzystamy wyrażenie lambda.
Do wyrażenia lambda przekażemy poszczególne trójelementowe krotki reprezentujące informacje na temat szczytów.
Do zmiennej x
argumentu wyrażenia lambda podstawiana jest krotka, a następnie wyznaczana jest wartość x[2]
, która z trójelementowej krotki pobiera rok zdobycia szczytu.
Wynik działania programu:
Słownik
(ang. American Standard Code for Information Interchange) pierwotnie siedmiobitowy system kodowania znaków (współcześnie rozszerzony do ośmiu bitów); w oryginalnej wersji kodom z zakresu 0–127 przyporządkowano 26 liter łacińskich, 10 cyfr (0–9) oraz dodatkowe znaki
specyficzna cecha wszystkich języków naturalnych; w przypadku każdego z nich można określić, jak często w słowniku pojawiają się poszczególne litery alfabetu; dla języka polskiego takie częstości zbadał i przedstawił m.in. A. Przepiórkowski z Instytutu Podstaw Informatyki Polskiej Akademii Nauk
metoda kompresji zapewniająca możliwość odtworzenia oryginalnej (niezmienionej) postaci informacji pierwotnej z danych skompresowanych
słowo, wyrażenie, znak lub ciąg bitów odpowiadające jakiemuś słowu, które chcemy zakodować; we wczesnych etapach rozwoju kryptografii słowa kodowe były układane w ramach książek kodowych i wykorzystywane do szyfrowania wiadomości