Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОТЦС_Ч1_2008.doc
Скачиваний:
12
Добавлен:
01.07.2025
Размер:
4.62 Mб
Скачать

2.3.6. Словарное кодирование. Метод Зива–Лемпеля

Существует множество методов словарного кодирования, начало которым было положено работой двух израильских ученых – Зива и Лемпеля, опубликованной в 1977 году. Все они получили название “LZ-сжатие”.

Идея словарного кодирования достаточно проста. Допустим, нужно закодировать некоторое слово длиной в 8 букв из словаря размером в 32000 слов.нии Для кодирования любой из N букв равномерным двоичным кодом нужно, как известно, n = log2 N бит, то есть для кодирования любой буквы русского алфавита нужно n = log2 32 = 5 бит/букву. Тогда для кодирования любого слова длиной в 8 букв (средняя длина слов русского словаря) понадобится 5бит/букву 8букв = = 40 бит. Если же пронумеровать все слова в словаре (от 0 до 32000) и вместо кодирования самого слова (40 бит) кодировать его положение в словаре, то понадобится n = log2 32000 = 15 бит, то есть будем иметь почти трехкратный выигрыш в длине кода.

На самом деле, это только главная идея работы словарных методов – вместо кодирования самих данных кодировать их положение в словаре. Практически работающие алгоритмы кодирования используют ее по-разному. При этом все известные на сегодня алгоритмы LZ-сжатия можно разбить на две группы.

Методы, принадлежащие к первой группе,  алгоритмы семейства LZ77, – находя в кодируемой последовательности цепочки символов, которые ранее уже встречались, вместо того чтобы повторять эти цепочки, заменяют их указателями на предыдущие повторения (ссылка назад, длина фрагмента).

Приведем схему наиболее популярного варианта LZ77, обладающего самым большим быстродействием:

while (lookAheadBuffer not empty)

get a pointer (position, lenght) to the longest match in

the window for lookahead buffer;

if (lenght > MINIMUM_MATCH_LENGHT)

output a pair (position, lenght);

shift the window lenght characters along;

else

output the first character in the lookahead buffer;

shift the window 1 character along;

endif; endwile

Алгоритм распаковки (декодирования) работает еще проще: если на вход поступает пара (position, length), то нужно выдать на выход фрагмент текста из окна длиной lenght символов, начиная с позиции position, если же на вход поступает один символ, то он копируется в выходной поток. После этого окно сдвигается на соответствующее количество символов.

Таким образом, словарь в этой группе алгоритмов содержится в сжатых данных в неявном виде, сохраняются лишь указатели на встречающиеся цепочки повторяющихся символов. При декодировании сжатого текста происходит замена указателя готовой фразой из словаря (из уже декодированного текста), на которую тот указывает.

Алгоритмы второй группы (семейство алгоритмов LZ78) в дополнение к исходному словарю источника (букв) создают словарь фраз, представляющих собой повторяющиеся комбинации символов, встречающиеся во входных данных. При этом размер словаря источника возрастает и для его кодирования потребуется большее количество бит, но значительная часть этого словаря будет представлять собой уже не отдельные буквы, а буквосочетания или целые слова. Когда кодер обнаруживает фразу, которая ранее уже встречалась, он заменяет ее индексом словаря, содержащим эту фразу.

Самым известным алгоритмом этого класса является алгоритм Тэри Уэлча (Terry Welch), описанный им в 1984 г. для применения в высокопроизводительных контроллерах жестких дисков. Однако наиболее широкое применение он нашел в скоростных модемах и вошел составной частью в протокол связи v.42bis. (Метод обозначается LZW.) Метод LZW в своей работе пользуется словарем, содержащим 4096 элементов. Элементы словаря с номерами 0  255 содержат отдельные символы, которые являются постоянными, то есть не изменяются в процессе работы алгоритма. Остальные элементы с номерами 256  4095 изначально имеют пустые ссылки, а в процессе работы указывают на фразы, составленные по следующему правилу: новая фраза образуется добавлением текущего символа к концу текущей фразы. Схема LZW алгоритма сжатия выглядит следующим образом:

w = NIL

loop

read a character K

if wK exists in the dictiontary

w = wK

else

output the code for w

add wK to the dictionary

w = K

endloop

Когда словарь переполняется, алгоритм Уэлча приписывает нулевые ссылки элементам словаря с номерами 256  4095, т.е. как бы начинает свою работу сначала.

Приведем упрощенные примеры работы алгоритмов второй группы.

Допустим нужно закодировать строку АBCABCABCABCABCABC. Текст сознательно взят с повторяющимися буквосочетаниями, чтобы продемонстрировать главную идею словарного метода.

Кодирование начинается с пустого словаря. Просматривая кодируемые данные, кодер начинает составлять свой словарь согласно следующему правилу: первыми в словаре идут первые однократно встречающиеся символы исходного текста А = 1, B = 2, C = 3. Далее в тексте снова встретилась буква А, но она в словаре уже есть, поэтому в словарь начинают добавляться фразы длиной в два символа из уже имеющихся в словаре: АB = 4, СА = 5, ВС = = 6. Далее в тексте встречается уже имеющееся в словаре буквосочетание – АB, поэтому к словарю начинают добавляться фразы из трех символов – АBС = 7, и т.д.

В результате получается следующий словарь кодера:

Символ словаря Индекс словаря

А 1

B 2

С 3

AB 4

CA 5

BC 6

ABC 7,

а результат кодирования фразы АBCABCABCABCABCABC будет выглядеть, как 123456777, то есть гораздо более компактно.

Рассмотрим немного отличающийся вариант кодирования. В этом случае закодированные данные состоят из пакета {адрес словаря, следующий символ данных}, а каждый новый элемент словаря образуется как {элемент словаря, на который делается ссылка, следующий символ данных}. Допустим, нужно закодировать последовательность символов ABAABABBBBBBBABBBBA. Процедура и результат кодирования выглядят следующим образом.

Закодированные пакеты:{0,A}{0,B}{1,A}{2,A}{2,B}{5,B}{5,A}{6,B}{4,}

Адрес в словаре: 1 2 3 4 5 6 7 8

Содержимое словаря: A B AA BA BB BBB BBA BBBB.

Пакеты {0, A} и {0, B} означают первые одиночные символы в тексте, пакет {1, A} уже ссылается на первую букву текста и добавляет к ней A, пакет {2, A} ссылается на вторую букву текста и добавляет к ней А и т. д.