logo search
Теория чисел (расчётка)

Оптимальное кодирование

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

Коды, представляющие первичные алфавиты с неравномерным распределением символов, имеющие среднюю минимальную длину кодового слова во вторичном алфавите, называются оптимальными неравномерными кодами (ОНК).

Эффективность ОНК оценивают при помощи двух коэффициентов:

1. коэффициента статического сжатия

,

который характеризует уменьшение количества двоичных знаков на символ сообщения при применении ОНК по сравнению с применением методов нестатистического кодирования.

2. коэффициента относительной эффективности

,

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

Из свойств оптимальных кодов вытекают принципы их построения:

Принципы оптимального кодирования определяют методики построения оптимальных кодов.

  1. Построение ОНК по методике Шеннона-Фано для ансамбля из M сообщений сводится к следующей процедуре:

  1. множество из M сообщений располагают в порядке убывания вероятностей;

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

  3. первой группе присваивают символ 0, второй группе символ 1;

  4. каждую из подгрупп делят на две группы так, чтобы их суммарные вероятности были по возможности равны;

  5. первым подгруппам каждой из групп вновь присваивают 0, а вторым - 1, в результате чего получают вторые цифры кода. Затем каждую из четырех подгрупп вновь делят на равные (с точки зрения суммарной вероятности) части и т. д. До тех пор, пока в каждой из подгрупп останется по одной букве.

Алгоритм 2.1.1 – Алгоритм построения кода, близкого к оптимальному

Вход : Р : array [1..n] of real - массив вероятностей появления букв в сообщении, упорядоченный по убыванию; 1  Р [1]  …  Р [n] > 0,

Р [1] + … + Р [n] = 1.

Выход : С : array [1..n, 1..L] of 0..1 - массив элементарных кодов.

Fano (1, n, 0) {вызов рекурсивной процедуры Fano}

Основная работа по построению элементарных кодов выполняется следующей рекурсивной процедурой Fano.

Вход: b - индекс начала обрабатываемой части массива P,

e - индекс конца обрабатываемой части массива P,

k - длина уже построенных кодов в обрабатываемой части массива C.

Выход: заполненный массив С.

if e > b then

k : = k + 1 {место для очередного разряда в коде}

m : = Med (b, e) {деление массива на две части}

for i from b to e do

C [i, k] : = i > m {в первой части добавляем 0, во второй – 1}

end for

Fano (b, m, k) {обработка первой части}

Fano (m + 1, е, k) {обработка второй части}

end if

Функция Med находит медиану указанной части массива Р [b..e], т.е. определяет такой индекс m (b m < e), что сумма элементов Р [b..m] наиболее близка к сумме элементов Р [m + 1..e].

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

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

Решение. Так вероятности данного ансамбля сообщений равны p1 = = p2 = ...= p8 = 2-3 и порядок их расположения не играет роли, то расположим их так, как показано в таблице 2.1.1. Затем разбиваем данное множество на две равновероятные группы. Первой группе в качестве первого символа кодовых слов присваиваем 0, а второй - 1. Во второй колонке таблицы записываем четыре нуля и четыре единицы. После чего разбиваем каждую из групп еще на две равновероятные подгруппы. Затем каждой первой подгруппе присваиваем 0, а второй - 1 и записываем в третью колонку таблицы. Далее каждую из четырех подгрупп разбиваем на две равновероятные части и первой из них присваиваем 0, а второй - 1. Таким образом в четвертой колонке появится значение третьего символа кодовых слов.

Таблица 2.1.1 – Оптимальный код равновероятных букв

Буква

Кодовое слово полученное после разбиения

первого

второго

третьего

А1

А2

А3

А4

А5

А6

А7

А8

0

0

0

0

1

1

1

1

0

0

1

1

0

0

1

1

0

1

0

1

0

1

0

1

Проверка оптимальности кода осуществляется путем сравнения энтропии кодируемого (первичного) алфавита со средней длиной кодового слова во вторичном алфавите.

Для рассматриваемого примера энтропия источника сообщений

H = log2 N = log2 8=3 бит/символ

а среднее число двоичных знаков на букву кода

бит

где li - длина i-ой кодовой комбинации; pi - вероятность появления i-го символа комбинации длиной в li.

Таким образом, H = L, т. е. код является оптимальным для данного ансамбля сообщений.

Вывод: Для ансамблей равновероятных сообщений оптимальным является равномерный код. Если число исходных элементов ансамбля равно целой степени двух, то всегда H = L.

Пример. Первичный алфавит состоит из 8 символов со следующими вероятностями появления: a1 = 0,5; a2 = 0,25; a3 = 0,098; a4 = = 0,052; a5 = 0,04; a6 = 0,03; a7 = 0,019; a8 = 0,011. Построить ОНК по методу Шеннона - Фано и подсчитать коэффициенты.

Решение проиллюстрируем в таблице 2.1.2.

Таблица 2.1.2 – Оптимальный код неравновероятных букв

ai

pi

Кодовое слово после разбиения

Знаков

lipi

1

2

3

4

5

6

1

0,5

0

-

-

-

-

-

1

0,5

2

0,25

1

0

-

-

-

-

2

0,5

3

0,098

1

1

0

0

-

-

4

0,392

4

0,052

1

1

0

1

-

-

4

0,208

5

0,04

1

1

1

0

-

-

4

0,16

6

0,03

1

1

1

1

0

-

5

0,15

7

0,019

1

1

1

1

1

0

6

0,114

8

0,011

1

1

1

1

1

1

6

0,066

Недостатком методики Шеннона – Фано является неоднозначность построения ОНК. В методике Хаффмена этого недостатка нет.

ІІ. Хаффмен предложил следующий метод построения ОНК:

  1. Символы первичного алфавита выписываются в порядке убывания вероятностей.

  2. Последние n0 символов, где 2 n0 m и N - n0 / m - 1 - целое число, объединяют в некоторый новый символ с вероятностью, равной сумме вероятностей объединяемых символов.

  3. Последние символы с учетом образованного символа вновь объединяют и получают новый, вспомогательный, символ.

  4. Опять выписывают символы в порядке убывания вероятноьстей с учетом вспомогательного символа и т. д. До тех пор, пока вероятности m оставшихся символов после N - n0/m - 1-го выписывания не дадут в сумме 1.

На практике обычно не производят многократного выписывания вероятностей символов, а обходятся элементарными геометрическими построениями, суть которых для кодов с числом качественных признаков m = 2 сводится к тому, что символы кодируемого алфавита попарно объединяются в новые символы, начиная с символов, имеющих наименьшую вероятность, а затем, с учетом вероятностей вновь образованных символов, опять производят попарное объединение символов с наименьшими вероятностями и таким образом строят двоичное кодовое дерево, в вершине которого стоит символ с вероятностью 1.

П ример.

Построить методом Хаффмена оптимальный код для алфавита со следующим распределением вероятностей появления букв в тексте: A = 0,5; B = 0,15; C = 0,12; D = 0,1;

E =0,04; F = 0,04; G = 0,03;

H = 0,02.

Решение.

Сначала находят буквы с наименьшими вероятностями 0,02 (H) и 0,03 (G), затем проводят от них линию к точке, в которой вероятность появления буквы G равна 0,05. Затем берут две наименьшие вероятности 0,04 (F) и 0,04 (E) и получают новую точку с вероятностью 0,08.

Теперь наименьшими вероятностями обладают точки, соответствующие вспомогательным символам с вероятностями 0,05 и 0,08. Соединяем их линией с новой точкой, соответствующей вспомогательному символу с вероятностью 0,13.

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

Обозначим каждую верхнюю линию (см. рисунок 2.1.1) цифрой 1, а нижнюю - цифрой 0. Получим ОНК, который для каждого слова представляет собой последовательность нулей и единиц, которые встречаются по пути к данному слову от конечной точки (1,00).

Полученные коды представлены в таблице 2.1.3.

Таблица 2.1.3 – Двоичный код Хаффмена для восьми сообщений

Буква

Вероятность

ОНК

Число знаков в кодовом слове

pili

A

B

C

D

E

F

G

H

0,50

0,15

0,12

0,10

0,04

0,04

0,03

0,02

1

0 0 1

0 1 1

0 1 0

0 0 0 1 1

0 0 0 1 0

0 0 0 0 1

0 0 0 0 0

1

3

3

3

5

5

5

5

0,50

0,45

0,36

0,30

0,20

0,20

0,15

0,10

Пример.

Построить код Хаффмена для передачи сообщений при помощи трех частот f1, f2, f3, если символы первичного алфавита встречаются в сообщениях со следующими вероятностями: А = 0,24; Б = 0,18; В = 0,38;

Г = 0,1; Д = 0,06; Е = 0,02; Ж = 0,02.

Решение.

Таблица 2.1.4 – Троичный код Хаффмена для семи сообщений

Буква

Вероятность

Дерево

Код

В

А

Б

Г

Д

Е

Ж

0,38

0,24

0,18

0,1

0,06

0,02

0,02

f1

f2

f3f1

f3f2

f3f3f1

f3f3f2

f3f3f3

Полученный код легко декодируется, так как ни один код не начинается с f1 и f2, кроме одного одноразрядного кода.

Алгоритм 2.1.1 – Алгоритм построения оптимального кода – рекурсивная процедура Huffman

Вход : n – количество букв, Р : array [1..n] of real - массив вероятностей появления букв в сообщении, упорядоченный по убыванию; 1  Р [1]  …  Р [n] > 0, Р [1] + … + Р [n] = 1.

Выход : С : array [1..n, 1..L] of 0..1 - массив элементарных кодов.

l : array [1..n] of 1..L – массив длин элементарных кодов схемы префиксного кодирования.

if n = 2 then

C [1, 1] : = 0; l [1] : = 1 {первый элемент}

C [2, 1] : = 1; l [2] : = 1 {второй элемент}

else

q : = P [n1] + P [n] {сумма двух последних вероятностей}

j : = Up (n, q ) {поиск места и вставка суммы}

Huffman (P, n1) {рекурсивный вызов}

Down (n, j) {достраивание кодов}

end if

Примечание. Функция Up находит место в массиве Р, в котором должно находиться число q и вставляет это число, сдвигая вниз остальные элементы. Процедура Down строит оптимальный код для n букв на основе построенного оптимального кода для n1 буквы. Для этого код буквы с номером j временно исключается из массива С путем сдвига вверх кодов букв с номерами, большими j, а затем в конец обрабатываемой части массива С добавляется мара кодов, полученных из кода буквы с номером j удлинением на 0 и 1, соответственно.

Для пары букв при любом распределении вероятностей оптимальное кодирование очевидно: первой букве нужно назначить код 0, а второй – 1. Именно это и делается в первой части оператора if основной процедуры Huffman. С помощью функции Up в исходном упорядоченном массиве Р отбрасываются две последние (наименьшие) вероятности, и их сумма вставляется в масси Р, так чтобы массив (на единицу меньшей длины) остался упорядоченным. При этом место вставки сохраняется в локальной переменной j. Так происходит до тех пор, пока не останется массив из двух элементов, для которого оптимальный код известен. После этого в обратном порядке строятся оптимальные коды для трех, четырех и т.д. элементов. При этом масси вероятностей Р уже не нужен – нужна только последовательсноть номеров кодов, которые должны быть изъяты из массива кодов и продублированы в конце с добавлением рахряда. А эта последовательность хранится в экземплярах локальной переменной j, соответствующих рекурсивным вызовампроцедуры Huffman.

Данный алгоритм можно использовать для сжатия файлов. Причем, если учитывать все 256 ASCII символов, то разницы в сжатии, например, *.txt и *.exe не будет.

К недостаткам алгоритма относятся:

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

второй – непостредственно для кодирования.

  1. Необходимость хранения вместе с сжатым файлом декодирующего дерева для возможности восстановления файла.