Опубликован: 11.04.2007 | Уровень: специалист | Доступ: свободно
Лекция 7:

Подстановочные или словарно-ориентированные алгоритмы сжатия информации. Методы Лемпела-Зива

< Лекция 6 || Лекция 7: 1234 || Лекция 8 >
Аннотация: История происхождения, положительные и отрицательные стороны, сравнение и применение на практике таких алгоритмов, как: LZ77, LZ78, LZSS, LZW. Практические задания для укрепления основного материала лекции. Особенности программ архиваторов. Непосредственное применение алгоритмов кодирования в архиваторах для обеспечения продуктивной работы в MS-DOS и WINDOWS

Методы Шеннона-Фэно, Хаффмена и арифметическое кодирование обобщающе называются статистическими методами. Словарные алгоритмы носят более практичный характер. Их частое преимущество перед статистическими теоретически объясняется тем, что они позволяют кодировать последовательности символов разной длины. Неадаптивные статистические алгоритмы тоже можно использовать для таких последовательностей, но в этом случае их реализация становится весьма ресурсоемкой.

Алгоритм LZ77 был опубликован в 1977 г. Разработан израильскими математиками Якобом Зивом (Ziv) и Авраамом Лемпелом (Lempel). Многие программы сжатия информации используют ту или иную модификацию LZ77. Одной из причин популярности алгоритмов LZ является их исключительная простота при высокой эффективности сжатия.

Основная идея LZ77 состоит в том, что второе и последующие вхождения некоторой строки символов в сообщении заменяются ссылками на ее первое вхождение.

LZ77 использует уже просмотренную часть сообщения как словарь. Чтобы добиться сжатия, он пытается заменить очередной фрагмент сообщения на указатель в содержимое словаря.

LZ77 использует "скользящее" по сообщению окно, разделенное на две неравные части. Первая, большая по размеру, включает уже просмотренную часть сообщения. Вторая, намного меньшая, является буфером, содержащим еще незакодированные символы входного потока. Обычно размер окна составляет несколько килобайт, а размер буфера - не более ста байт. Алгоритм пытается найти в словаре (большей части окна) фрагмент, совпадающий с содержимым буфера.

Алгоритм LZ77 выдает коды, состоящие из трех элементов:

  • смещение в словаре относительно его начала подстроки, совпадающей с началом содержимого буфера;
  • длина этой подстроки;
  • первый символ буфера, следующий за подстрокой.

Пример. Размер окна - 20 символ, словаря - 12 символов, а буфера - 8. Кодируется сообщение "ПРОГРАММНЫЕ ПРОДУКТЫ ФИРМЫ MICROSOFT". Пусть словарь уже заполнен. Тогда он содержит строку "ПРОГРАММНЫЕ ", а буфер - строку "ПРОДУКТЫ". Просматривая словарь, алгоритм обнаружит, что совпадающей подстрокой будет "ПРО", в словаре она расположена со смещением 0 и имеет длину 3 символа, а следующим символом в буфере является "Д". Таким образом, выходным кодом будет тройка <0,3,'Д'>. После этого алгоритм сдвигает влево все содержимое окна на длину совпадающей подстроки +1 и одновременно считывает столько же символов из входного потока в буфер. Получаем в словаре строку "РАММНЫЕ ПРОД", в буфере - "УКТЫ ФИР". В данной ситуации совпадающей подстроки обнаружить не удаться и алгоритм выдаст код <0,0,'У'>, после чего сдвинет окно на один символ. Затем словарь будет содержать "АММНЫЕ ПРОДУ", а буфер - "КТЫ ФИРМ". И т.д.

Декодирование кодов LZ77 проще их получения, т.к. не нужно осуществлять поиск в словаре.

Недостатки LZ77:

  • с ростом размеров словаря скорость работы алгоритма-кодера пропорционально замедляется;
  • кодирование одиночных символов очень неэффективно.

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

Пример. Закодировать по алгоритму LZ77 строку "КРАСНАЯ КРАСКА".

\centerline{\vbox{\offinterlineskip\tt
\halign{\strut\hfil\ #\ \hfil&
        \vrule#& \hfil\ #\ \hfil&
        \vrule#& \hfil\ #\ \hfil\cr
СЛОВАРЬ(8)&& БУФЕР(5)&& КОД\cr
\noalign{\hrule}
"........"&& "КРАСН" && <0,0,'К'>\cr
".......К"&& "РАСНА" && <0,0,'Р'>\cr
"......КР"&& "АСНАЯ" && <0,0,'А'>\cr
".....КРА"&& "СНАЯ " && <0,0,'С'>\cr
"....КРАС"&& "НАЯ К" && <0,0,'Н'>\cr
"...КРАСН"&& "АЯ КР" && <5,1,'Я'>\cr
".КРАСНАЯ"&& " КРАС" && <0,0,' '>\cr
"КРАСНАЯ "&& "КРАСК" && <0,4,'К'>\cr
"АЯ КРАСК"&& "А...." && <0,0,'А'>\cr}}}

В последней строчке, буква "А" берется не из словаря, т.к. она последняя.

Длина кода вычисляется следующим образом: длина подстроки не может быть больше размера буфера, а смещение не может быть больше размера словаря -1. Следовательно, длина двоичного кода смещения будет округленным в большую сторону \log_2( размер словаря ), а длина двоичного кода для длины подстроки будет округленным в большую сторону \log_2( размер буфера +1). А символ кодируется 8 битами (например, ASCII+).

В последнем примере длина полученного кода равна 9*(3+3+8)=126 бит, против 14*8=112 бит исходной длины строки.

В 1982 г. Сторером (Storer) и Шиманским (Szimanski) на базе LZ77 был разработан алгоритм LZSS, который отличается от LZ77 производимыми кодами.

Код, выдаваемый LZSS, начинается с однобитного префикса, различающего собственно код от незакодированного символа. Код состоит из пары: смещение и длина, такими же как и для LZ77. В LZSS окно сдвигается ровно на длину найденной подстроки или на 1, если не найдено вхождение подстроки из буфера в словарь. Длина подстроки в LZSS всегда больше нуля, поэтому длина двоичного кода для длины подстроки - это округленный до большего целого двоичный логарифм от длины буфера.

Пример. Закодировать по алгоритму LZSS строку "КРАСНАЯ КРАСКА".

\centerline{\vbox{\offinterlineskip\tt
\halign{\strut\hfil\ #\ \hfil&
        \vrule#& \hfil\ #\ \hfil&
        \vrule#& \hfil\ #\ \hfil&
        \vrule#& \hfil\ #\ \hfil\cr
СЛОВАРЬ(8)&& БУФЕР(5)&& КОД   && ДЛИНА КОДА\cr
\noalign{\hrule}
"........"&& "КРАСН" && 0'К'  && 9\cr
".......К"&& "РАСНА" && 0'Р'  && 9\cr
"......КР"&& "АСНАЯ" && 0'А'  && 9\cr
".....КРА"&& "СНАЯ " && 0'С'  && 9\cr
"....КРАС"&& "НАЯ К" && 0'Н'  && 9\cr
"...КРАСН"&& "АЯ КР" && 1<5,1>&& 7\cr
"..КРАСНА"&& "Я КРА" && 0'Я'  && 9\cr
".КРАСНАЯ"&& " КРАС" && 0' '  && 9\cr
"КРАСНАЯ "&& "КРАСК" && 1<0,4>&& 7\cr
"НАЯ КРАС"&& "КА..." && 1<4,1>&& 7\cr
"АЯ КРАСК"&& "А...." && 1<0,1>&& 7\cr}}}

Здесь длина полученного кода равна 7*9+4*7=91 бит.

LZ77 и LZSS обладают следующими очевидными недостатками:

  1. невозможность кодирования подстрок, отстоящих друг от друга на расстоянии, большем длины словаря;
  2. длина подстроки, которую можно закодировать, ограничена размером буфера.
< Лекция 6 || Лекция 7: 1234 || Лекция 8 >