[Список тем] [Вступление к этой теме] Страницы темы: [1] [2]
Оптимальное кодирование обладает определенными свойствами, которые можно использовать для его построения.
ЛЕММА |
---|
Пусть ![]() ![]() ![]() |
Доказательство
От противного. Пусть i < j, рi > рj и li > 1j. Тогда рассмотрим
' = {a1
1, ..., аi
j, ..., аj
i, ..., аn
n}.
Имеем:
l
- l
' = (pili + рjlj) - (pilj + pjli) = (рi – рj)(li - lj) > 0,
что противоречит тому, что
— оптимально.
Таким образом, не ограничивая общности, можно считать, что li <=
… <= 1n.
ЛЕММА |
---|
Если ![]() ![]() ![]() |
Доказательство
От противного.
Теорема |
---|
Если ![]() ![]() ![]() р1 >= … >= рj-1 >= рj+1 >= … >= рn-1 >= q' >= q" > 0, то кодирование со схемой ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() является оптимальным префиксным кодированием для распределения вероятностей Рn = p1, ..., рj-1, pj+1, ..., рn-1, q', q". |
Доказательство
Следующий рекурсивный алгоритм строит схему оптимального префиксного
алфавитного кодирования для заданного распределения вероятностей появления букв.
Алгоритм Хаффмена |
---|
Построение оптимальной схемы - рекурсивная процедура Huffman Вход: n - количество букв, Р : аrrау [1..n] of real - массив вероятностей букв, упорядоченный по убыванию. Выход: С : аrrау [1..n, 1..L] of 0..1 – массив элементарных кодов, l: аrrау [1..n] of 1..L –массив длин элементарных кодов схемы оптимального префиксного кодирования if n=2 then С[1,1]:= 0; l[1]:= 1{ первый элемент } С[2,1]:= 1; l[2]:=1{ второй элемент } else q:= Р[n - 1] + Р[n] { сумма двух последних вероятностей } j:= Uр(n, q) { поиск места и вставка суммы } Huffman (Р, n - 1) { рекурсивный вызов } Down (n, j) { достраивание кодов } end if |
Функция Uр находит в массиве Р место, в котором должно находиться число q (см. предыдущий алгоритм) и вставляет это число, сдвигая вниз остальные элементы. |
Вход: n — длина обрабатываемой части массива Р, q — вставляемая сумма. Выход: измененный массив Р. for i from n-1 downto 2 do if P[i-1] <= q then Р[i]:= P[i-1] { сдвиг элемента массива } else j:= i-1 { определение места вставляемого элемента } exit for i { все сделано — цикл не нужно продолжать } end if end for Р[j]: = q { запись вставляемого элемента } return j |
Процедура Down строит оптимальный код для n букв на основе построенного оптимального кода для n - 1 буквы. Для этого код буквы с номером j временно исключается из массива С путем сдвига вверх кодов букв с номерами, большими j, а затем в конец обрабатываемой части массива С добавляется пара кодов, полученных из кода буквы с номером j удлинением на 0 и 1, соответственно. Здесь С'[i, *] означает вырезку из массива, то есть i-ю строку массива С. |
Вход: n — длина обрабатываемой части массива Р, j — номер “разделяемой” буквы. Выход: оптимальные коды в первых n элементах массивов С и l. с:= С[j, *]{ запоминание кода буквы j } l:= l[j] { и длины этого кода } for i from j to n-2 do C[i, *]:= С[i+1, *] { сдвиг кода } l[i]:= l[i+1] { и его длины } end for С[n-1, *]:= с; С[n, *]:= с{ копирование кода буквы j } С[n-1, l+1]:= 0; С[n, l+1]:= 1{ наращивание кодов } l[n-1]:= l+1; l[n]:= l + 1{ и увеличение длин } |
Обоснование
Для пары букв при любом распределении вероятностей оптимальное кодирование очевидно: первой букве нужно назначить код 0, а второй — 1. Именно это и делается в первой части оператора if основной процедуры Huffman. Рекурсивная часть алгоритма в точности следует доказательству теоремы предыдущего подраздела. С помощью функции Uр в исходном упорядоченном массиве Р отбрасываются две последние (наименьшие) вероятности, и их сумма вставляется в массив Р, так чтобы массив (на единицу меньшей длины) остался упорядоченным. Заметим, что при этом место вставки сохраняется в локальной переменной j. Так происходит до тех пор, пока не останется массив из двух элементов, для которого оптимальный код известен. После этого в обратном порядке строятся оптимальные коды для трех, четырех и т. д. элементов. Заметим, что при этом массив вероятностей Р уже не нужен — нужна только последовательность номеров кодов, которые должны быть изъяты из массива кодов и продублированы в конце с добавлением разряда. А эта последовательность хранится в экземплярах локальной переменной j, соответствующих рекурсивным вызовам процедуры Huffman.
Пример
Построение оптимального кода Хаффмена для n = 7. В левой части таблицы показано изменение массива Р, а в правой части — массива С. Позиция, соответствующая текущему значению переменной j, выделена полужирным начертанием.
0.20 | 0.20 | 0.23 | 0.37 | 0.40 | 0.60 | 0 | 1 | 00 | 01 | 10 | 10 |
0.20 | 0.20 | 0.20 | 0.23 | 0.37 | 0.40 | 1 | 00 | 01 | 10 | 11 | 11 |
0.19 | 0.19 | 0.20 | 0.20 | 0.23 | 01 | 10 | 11 | 000 | 000 | ||
0.12 | 0.18 | 0.19 | 0.20 | 11 | 000 | 001 | 010 | ||||
0.11 | 0.12 | 0.18 | 001 | 010 | 011 | ||||||
0.09 | 0.11 | 011 | 0010 | ||||||||
0.09 | 0011 |
Цена кодирования составляет
0.20 * 2 + 0.20 * 2 + 0.19 * 3 + 0.12 * 3 + 0.11 * 3 + 0.09 * 4 + 0.09 * 4 = 2.78,
что несколько лучше, чем в кодировании, полученном алгоритмом Фано.
[Список тем] [Вступление к этой теме] Страницы темы: [1] [2]