Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
53
Добавлен:
19.02.2016
Размер:
220.67 Кб
Скачать

2. Алгоритмы сжатия без потери информации

Методы сжатия текстовой информацииимеют достаточно длинную историю. В основе алгоритмов сжатия подобной информации лежит принцип минимизации избыточности. Фундаментальнаятеорема Шеннона о кодировании источниковговорит, что стоимость кодирования всегда не меньше энтропии источника, хотя может быть сколь угодно близка к ней5. Это утверждение устанавливает теоретические границы сжатия данных.

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

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

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

Если распределение частот символов известно, то можно построить оптимальное кодирование. Задача усложняется, если это распределение заранее неизвестно. В этом случае существует два различных подхода.

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

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

Статическое кодирование Хаффменасопоставляет входным символам, представленным цепочками битов одинаковой длины (например, 8-битовыми байтами), цепочки битов переменной длины. Длина кода для символа пропорциональна (с округлением до целого) двоичному логарифму его частоты, взятому с отрицательным знаком. Это кодирование являетсяпрефиксным, что позволяет легко его декодировать однопроходным алгоритмом. В префиксном кодировании код любого символа не является префиксом кода никакого другого символа.

Префиксный код - это код со словом переменной длины, имеет такое свойство: если в код входит слово a, то для любого непустого строки b слова ab в коде нет. Хотя префиксный код состоит из слов различной длины, эти слова можно записывать без разделительного знака. Например, код, состоящий из слов 0, 10 и 11, является префиксным и сообщения 01001101110 можно разбить на слова единственным образом: 0 10 0 11 0 11 10. Напротив, код, состоящий из слов 0, 10, 11 и 100 , префиксным не является, и то же сообщение можно трактовать несколькими способами.

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

Пример 3.Рассмотрим реализацию метода Хаффмана при кодировании следующего текста:

THIS IS A SIMPLE EXAMPLE OF HUFFMAN ENCODING.

1. Подсчитаем число повторений каждой буквы алфавита в исходном тексте:

Символ

Пробел

A

B

C

D

E

F

G

H

I

J

K

L

M

N

O

P

Q

R

S

T

U

V

W

X

Y

Z

Частота

7

3

0

1

1

4

3

1

2

4

0

0

2

3

3

2

2

0

0

3

1

1

0

0

1

0

0

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

Символ

Пробел

E

I

A

F

M

N

S

H

L

O

P

C

D

G

T

U

X

Частота

7

4

4

3

3

3

3

3

2

2

2

2

1

1

1

1

1

1

3. Построение дерева начнем с самого правого символа в списке. Частоты двух наиболее редко встречающихся символов просуммируем, и результат запишем, в узле дерева, как показано на рис. 1. Исходные частоты стали теперь детьми новой суммарной частоты. Если имеется более двух символов с минимальной частотой повторений, то нужно просто образовать из них самостоятельные пары. Если имеется нечетное количество наименьших значений частоты будем объединять непарную величину со следующей наименьшей частотой.

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

4. Окончательный код Хаффмана для каждого символа исходного текста можно получить, добавив к каждой ветви дерева определенный двоичный атрибут. Все левые ветви, исходящие из всех узлов, будем помечать 1, а все правые ветви — 0. Таким образом, код каждой буквы можно получить, перемещаясь по ветвям дерева от вершины к интересующей букве и записывая по порядку двоичные атрибуты ветвей. Результаты сведем в таблицу, которую будем использовать в качестве «словаря» при кодировании и последующем восстановлении исходного текста:

Полученная таблица кодирования

Пробел

111

L

0101

E

1101

O

0100

I

1100

P

0011

A

1011

C

00101

F

1010

D

00100

M

1001

G

00011

N

1000

T

00010

S

0111

U

00001

H

0110

X

00000

Например, слово THISбудет кодироваться последовательностью 00010 0110 1100 0111. Общая длина кода слова при этом уменьшилась с 32 бит до 17 бит. Для всего предложения коэффициент сжатия составляет 175/3520.497.

Для сжатия графической информации без потерь нашел применение метод группового кодирования RLE(Run Length Encoding), суть которого заключается в том, что файл сжимаемого изображения «вытягивается» в цепочку байт по строкам растра. Само сжатие вRLEпроисходит за счет того, что в исходном изображении встречаются цепочки одинаковых байт. Замена их на пары<счетчик повторений, значение>уменьшает избыточность данных.

Например, последовательность «ААААААА» с помощью алгоритма RLE будет закодирована как «(А, 7)».

В данном алгоритме признаком счетчика служат единицы в двух старших битах считанного файла. Соответственно оставшиеся 6 бит расходуются на счетчик, который может принимать значения от 1 до 64. Строку из 64 повторяющихся байтов мы превращаем в два байта, т.е. сожмем в 32 раза.

Данный алгоритм реализован в формате PCX.

Существует и второй вариант алгоритма, реализующего метод RLE, который имеет больший коэффициент сжатия. Признаком повтора в данном алгоритме является единица в старшем разряде соответствующего байта. Как можно легко подсчитать, в лучшем случае этот алгоритм сжимает файл в 64 раза (а не в 32 раза, как в предыдущем варианте), в худшем увеличивает на 1/128. Средние показатели степени компрессии данного алгоритма находятся на уровне показателей первого варианта. Похожая схема компрессии использована в качестве одного из алгоритмов, поддерживаемых форматомTIFF, а также в форматеTGA.

Более эффективным методом сжатия информации без потерь является метод Лемпелла-Зива (ЛЗ), названный так в честь двух израильских математиков, предложивших его в 1977 году. Метод основан на том, что в каждом файле, как правило, встречается несколько часто повторяемых двоичных комбинаций. Например, в тексте программы на языке программирования высокого уровня постоянно будут встречаться комбинации символов (а в файле с текстом – комбинации соответствующих байт), относящиеся к операторам, процедурам и функциям языка. Очевидно, можно выделить такие комбинации, составить их список и соответственно подобрать им какие-то более короткие комбинации. Затем заменить длинные комбинации на короткие, а в файл записать таблицу, по которой осуществлялась замена. Распаковка файла сводится к операции замен, обратных производимым при записи.

На практике для хранения таблицы используется хэш-таблица, состоящая из элементов в количестве не меньшем, чем 8192 (213) . Каждый элемент таблицы содержит запись:

<код предыдущей подстроки; добавленный символ; код этой строки>.

Ключ для поиска длиной в 20 бит формируется с использованием двух первых элементов, хранимых в таблице как одно число (key). Младшие 12 бит этого числа отданы под код, а следующие 8 бит под значение символа.

В качестве хэш-функции используется:

Index(key)=((key >> 12) key) & 8191,

где >> - побитовый сдвиг вправо; - логическая операция побитового исключающего ИЛИ, & - логическое побитовое И.

Примером использования метода ЛЗ являются графические форматы GIFиTIFF.