Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛАСТ.doc
Скачиваний:
3
Добавлен:
05.09.2019
Размер:
354.3 Кб
Скачать

§4. Алгоритмы архивирования

Кодирование Шеннона-Фано

(алгоритм с кодом переменной длины)

Кодирование Шеннона-Фано является одним из самых первых алгоритмов сжатия, который впервые сформулировали американские учёные Шеннон (Shannon) и Фано (Fano). Данный метод сжатия имеет большое сходство с кодированием Хаффмана, которое появилось на несколько лет позже. Главная идея этого метода - заменить часто встречающиеся символы более короткими кодами, а редко встречающиеся последовательности более длинными кодами. Таким образом, алгоритм основывается на кодах переменной длины.

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

Для того, чтобы декомпрессор впоследствии смог раскодировать сжатую последовательность, коды Шеннона-Фано должны обладать уникальностью, то есть, не смотря на их переменную длину, каждый код уникально определяет один закодированный символ и не является префиксом любого другого кода.

Рассмотрим алгоритм вычисления кодов Шеннона-Фано (для наглядности возьмём в качестве примера последовательность 'aa bbb cccc ddddd'). Для вычисления кодов, необходимо создать таблицу уникальных символов сообщения c(i) и их вероятностей p(c(i)), и отсортировать её в порядке убывания вероятности символов.

c(i) p(c(i))

d 5 / 17

c 4 / 17

space 3 / 17

b 3 / 17

a 2 / 17

Далее, таблица символов делится на две группы таким образом, чтобы каждая из групп имела приблизительно одинаковую частоту по сумме символов. Первой группе устанавливается начало кода в '0', второй в '1'. Для вычисления следующих бит кодов символов, данная процедура повторяется рекурсивно для каждой группы, в которой больше одного символа. Таким образом для нашего случая получаем следующие коды символов:

символ код

d 00

c 01

space 10

b 110

a 111

Длина кода s(i) в полученной таблице равна int(-lg p(c(i))), если сиволы удалость разделить на группы с одинаковой частотой, в противном случае, длина кода равна int(-lg p(c(i))) + 1.

int(-lg p(c(i))) <= s(i) <= int(-lg p(c(i))) + 1

Успользуя полученную таблицу кодов, кодируем входной поток - заменяем каждый символ соответствующим кодом. Естественно для разжатия полученной последовательности, данную таблицу необходимо сохранять вместе со сжатым потоком, что является одним из недостатков данного метода. В сжатом виде, наша последовательность принимает вид:

111111101101101101001010101100000000000

длиной в 39 бит. Учитывая, что оригинал имел длину равную 136 бит, получаем коэффициент сжатия 3.48 - не так уж и плохо.

Глядя на полученную последовательность, возникает вопрос: "А как же теперь это разжать?". Мы не можем, как в случае кодирования, заменять каждые 8 бит входного потока, кодом переменной длины. При разжатии нам необходимо всё сделать наоборот - заменить код переменной длины символом длиной 8 бит. В данном случае, лучше всего будет использовать бинарное дерево, листьями которого будут являться символы (аналог дерева Хаффмана).

Кодирование Шеннона-Фано является достаточно старым методом сжатия, и на сегодняшний день оно не представляет особого практического интереса (разве что как упражнение по курсу структур данных). В большинстве случаев, длина сжатой последовательности, по данному методу, равна длине сжатой последовательности с использованием кодирования Хаффмана. Но на некоторых последовательностях всё же формируются не оптимальные коды Шеннона-Фано, поэтому сжатие методом Хаффмана принято считать более эффективным. Для примера, рассмотрим последовательность с таким содержанием символов: 'a' - 14, 'b' - 7, 'c' - 5, 'd' - 5, 'e' - 4. Метод Хаффмана сжимает её до 77 бит, а вот Шеннона-Фано до 79 бит.

символ код Хаффмана код Шеннона-Фано

a 0 00

b 111 01

c 101 10

d 110 110

e 100 111

Как же мы делили на группы ? Достаточно просто:

вероятность первой группы (p1) и второй (p2) равна нулю;

p1 <= p2 ?

да: добавить в первую группу символ с начала таблицы;

нет: добавить во вторую группу символ с конца таблицы;

если все символы разделены на группы, то завершить алгоритм, иначе перейти к шагу 2.

Алгоритм с коэффициентом повторения

Еще один механизм, ликвидирующий избыточность, основан на использовании повторов “соседних” данных. Например, в приведенной последовательности

1020 1018 1013 1013 1013 1008 1010 1020 1036 1050 1035 ...

встречается серия из трех повторяющихся друг за другом одинаковых значений 1013.

Можно попытаться использовать это обстоятельство, заменяя каждую серию парой величин – коэффициентом повторения (в примере: 3) и собственно значением (1013). Ясно, что придется либо применить такой механизм ко всему набору, либо всякую пару сопровождать признаком ее появления, чтобы отличать от обычных элементов. Очевидно, для нашего примера коэффициент сжатия данных, в обоих случаях, превышает 1, то есть фактического сжатия не происходит.

Тем не менее, идея оказывается плодотворной, если ее применить в отношении серий повторяющихся символов. В литературе соответствующий алгоритм носит название RLE. Эта аббревиатура, у разных авторов, заменяет либо полное название Repeated Running Length Encoding, либо короче – Run Length Encoding. По-русски же этот алгоритм обычно называют сжатием последовательностей одинаковых символов.

Применим описанный подход к символьной последовательности, например, такой: ABBCCCDDDDEEEEE

Очевидно, результатом преобразования будет более короткая последовательность 1A2B3C4D5E

Для того чтобы избежать перемешивания в последовательности числовых значений коэффициентов и, собственно, символов, целесообразно кодировать коэффициенты, как символы ASCII c соответствующими кодами. Так, вместо серии, включающей 65 рядом стоящих символов S, получаем паруAS, поскольку под порядковым номером 65 в таблице ASCII значится символ A.

Коэффициент сжатия здесь составит K = 65/2 = 32.5

Сразу возникает вопрос, как же закодировать серию из 256 и более одинаковых символов, ведь резервов ASCII таблицы уже не хватает. Проблема решается произвольным разбиением длинной серии на несколько коротких так, чтобы каждая включала менее 256 символов. Скажем, серию из 320 символов S можно представить как ZSZSZS2S

Код Z — 9010, код 2 — 3010, а последовательностью SSSSSSSS, при желании, можно заменить 332 — символьную серию букв S(код S — 8310).

Достаточно очевиден тот факт, что для обычного текста применение RLE-преобразования приводит не к сжатию, а противоположному результату. Действительно, сжимаются лишь серии длиной не менее 3 символов, а даже 2 одинаковых символа рядом – скорее исключение, чем правило.

Выходит, описанный механизм представляет лишь теоретический интерес? Не совсем так. До недавнего времени широкой популярностью пользовался файловый формат сохранения изображений, разработанный еще в 80-е годы фирмой ZSoft. Я говорю о формате PCX. Если кодировать цвет знакоместа или пикселя экрана одним байтом, то строки изображения будут содержать большое количество серий одинаковых символов. Указанный формат изначально был рассчитан на использование видеоадаптеров, обеспечивающих палитру из 64(26) цветов. Расширение палитры до 256 цветов осуществляется специальным приемом, но это не относится к предмету нашего рассмотрения. При диапазоне значений цвета, от 0 до 63, в байте оказываются задействованными лишь 6 младших бит, с номерами от 0 до 5, а биты 6 и 7 заведомо свободны, то есть содержат нули.

Дальнейшее – очевидно, но кодирование PCX-файлов имеет и некоторую специфику. Если длина серии менее 2, то элементы изображения не подвергаются сжатию. В противном случае признаком кодирования для байта-счетчика служат две битовых 1 в старших разрядах, а остальные 6 бит содержат двоичную длину серии, содержание которой определяет следующий байт пары. Поэтому кодировать таким образом можно серии длиной не более 6310.

Идея использовать “лишние” биты позволяет продемонстрировать еще один вариант применения RLE-сжатия – в отношении 7-битных ASCII наборов. Помните, мы с ними уже встречались, обсуждая алгоритм 7-битного кодирования.

Теперь, если встречается серия длиной не менее трех одинаковых символов, мы заменим ее парой, и в первый байт со счетчиком запишем 1 в старший бит. Это то же самое, что добавление 12810. Соответственно, отсутствие такого признака будет означать, что очередной байт перенесен в выходной набор без изменений. Естественно, длина серии не должна превосходить 12710.

Можно попытаться применить RLE- механизм к двоичным последовательностям. В этом случае вместо пары, заменяющей очередную серию, достаточно в выходной набор помещать лишь коэффициент повторения, поскольку алфавит {0,1} однозначно определяет чередование содержания серий.

Как, при этом, отличать двоичные последовательности, начинающиеся с 0, от последовательностей, началом которых является 1? Можно просто договориться, что первым всегда стоит счетчик для 0.

Например, согласно ASCII таблице, 6-символьная последовательность, составляющая имя принца Датского –Hamlet– представляется шестнадцатеричными кодами 48 61 6D 6C 65 74

или, в двоичном виде,

0100 1000 0110 0001 0110 1101 0110 1100 0110 0101 0111 0100

При замене двоичного кода на последовательность десятичных счетчиков получаем 11214241121211121232211113112

К сожалению, больших успехов на этом пути не добиться. Проблема состоит в том, что коэффициенты повторения должны сами представляться в двоичном коде. Соответственно, для них нужно выделить зоны записи одинаковой длины. Скажем, если договориться о предоставлении для каждого счетчика по 2 битовых позиции, то значение 4 из приведенного примера туда уже не поместится. Приходится разбивать эту 4-серию на 3 других, с пустой посередине, например, так: 301. В результате последовательность счетчиков удлинится и превратится в такую:

112130123011121211121232211113112

или, вновь в двоичном виде –

010110011100011011000101011001100101011001101110100101010111010110

Итого, вместо исходных 48 бит получили 67, то есть коэффициент сжатия K=48 /67 (< 1), что нас вряд ли может устроить.