Опрос

Какой архиватор наиболее эффективный?:

Новички

Виктор Васильев
Юрий Антонов
Сергей Андреевич
Генадий
Avanasy

Адаптивное арифметическое кодирование

Две характерные черты арифметического кодирования позволяют

легко расширить этот метод сжатия.

1. Основной шаг кодирования состоит из двух операций

Low := Low+(High-Low+l)*LowCmnFreq[X]/10; High := Low+(High-Low+l)*HighCumFreq[X]/10-l;

Это означает, что для кодирования символа X необходимо сообщить кодеру накопленные частоты этого символа и символа, расположенного над ним (см. табл. 1.24 с примером накопленных частот). Но тогда частота X (или ее вероятность) может изменяться каждый раз после кодирования, при условии, что кодер и декодер будут это делать согласованно.

2. Порядок символов в табл. 1.24 не имеет значения. Символы могут даже менять свое место в таблице, если это делать согласованно с декодером.

Имея это в виду, легко понять, как может работать процесс адаптивного арифметического кодирования. Алгоритм кодирования состоит из двух частей: вероятностная модель и арифметический кодер. Вероятностная модель читает следующий символ из входного файла и вызывает кодер, сообщая ему символ и две текущие накопленные частоты. Затем модель увеличивает на единицу счетчик символов и изменяет накопленные частоты. Главным здесь является то, что вероятности символов определяются моделью по старым значениям счетчиков, которые увеличиваются только после кодирования данного символа. Это позволяет декодеру делать зеркальные действия. Кодер знает символ, который ему предстоит закодировать, а декодер должен его распознать по сжатому коду, поэтому декодер знает только старые значения счетчиков, но может увеличивать и изменять их значения точно так же как и кодер.

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

Такой структурой является двоичное сбалансированное дерево. (Сбалансированным двоичным деревом служит полное дерево, в котором некоторые правые нижние узлы отсутствуют.) На дереве должны быть узлы для каждого символа алфавита, и поскольку оно сбалансировано, его высота равна [log2п\ , где п - размер алфавита. При п = 256 высота сбалансированного дерева равна 8, поэтому начав с корня, поиск символа займет не более 8 шагов. Дерево организовано так, что более вероятный символ (у которого самый большой счетчик) находится ближе к корню дерева, что ускоряет процесс его поиска. В табл. 1.36а показан пример алфавита из 10 символов вместе со счетчиками. В табл. 1.36Ь этот же алфавит упорядочен по значениям счетчиков.

Упорядоченный массив размещен на дереве, изображенном на рис. 1.38а. Это простой и элегантный способ построения дерева. Сбалансированное двоичное дерево можно построить без использования указателей. Правило такое: первый элемент массива (с индексом 1) находится в корне, два узла-потомка элемента с индексом % имеют индексы 2г и 2г + 1, а родительский узел узла j имеет индекс [j/2\ . Легко видеть, как процесс упорядочения массива разместит символы с большими счетчиками ближе к корню.

В дополнение к символу и его счетчику в каждом узле дерева будет храниться общая сумма счетчиков его левого поддерева. Эти величины будут использоваться при подсчете накопленных частот. 

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

Этот поиск может быть просто линейным, если массив короткий, или двоичным, если массив длинный. В нашем случае это будет элемент а2, который следует переставить с ад (табл. 1.37Ь). На рис. 1.38Ь показано дерево после этой перестановки. Обратите внимание на то, как меняются счетчики левых поддеревьев.

Наконец покажем, как вычисляются накопленные частоты с помощью этого дерева. Когда необходимо найти эту величину для символа X, модель идет по веткам из корня до узла содержащего X, добавляя числа в переменную af. Каждый раз, когда выбирается правая ветвь из внутреннего узла N, к переменной af добавляются два числа (счетчик символа и счетчик левого дерева), лежащие в этом узле. При выборе левой ветви переменная af не меняется. Когда узел, содержащий X найден, счетчик левого дерева этого узла добавляется к af. Теперь значение переменной af равно в точности величине LowCumFreq [X].

Для примера проследим на рис. 1.38а из корня дерева до символа аб, чья накопленная частота равна 28. Правая ветвь выбрана в узле а2, поэтому в af добавлена сумма 12+16. В узле а\ выбрана левая ветвь, поэтому к af ничего не добавлено. В результате af=12+16=28, что можно проверить на рис. 1.38с. Величина HighCumFreq[X] получается прибавлением счетчика а^ (равного 1) к LowCumFreq[X].

При проходе по дереву от корня до символа clq алгоритм выполняет следующие действия.

1. Находит clq в массиве с помощью двоичного поиска по дереву. В нашем примере clq находится в позиции 10 массива.

2. Делит 10 на 2. Остаток равен 0. Это означает, что clq является левым потомком своего родителя. Частное 5 равно индексу в массиве его родителя.

3. Находит в позиции 5 символ а\. Делит 5 пополам. Остаток 1 говорит о том, что а\ является правым потомком своего родителя, имеющего индекс 2.

4. Элемент массива с индексом 2 равен а,2. Алгоритм делит 2 пополам. Остаток равен 0, то есть, а2 - левый потомок родителя, который имеет индекс 1 и находится в корне, поэтому процесс останавливается.

Метод компрессии РРМ, описанный в [Cleary, Wit ten 84] и [Mof-fat 90], является хорошим примером статистической модели, использующей арифметическое кодирование.