[Список тем] [Вступление к этой теме] Страницы темы: [1] [2]


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


Оптимальное кодирование обладает определенными свойствами, которые можно использовать для его построения.
ЛЕММА
Пусть = (аi i)ni=1 — схема оптимального кодирования для распределения вероятностей Р = р1 >= … > = рn > 0. Тогда если рi > рj, то li < = lj.

Доказательство
От противного. Пусть 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.
ЛЕММА
Если = (аi i)ni=1 — схема оптимального префиксного кодирования для распределения вероятностей Р = р1 > = … >= рn > 0, то среди элементарных кодов, имеющих максимальную длину, имеются два, которые различаются только в последнем разряде.

Доказательство
От противного.

  1. Пусть кодовое слово максимальной длины одно и имеет вид n = b, где b = 0 b = 1. Имеем: i 1..n - 1 1i <= | |. Так как схема префиксная, то слова 1, ..., n-1 не являются префиксами . С другой стороны, не является префиксом слов 1, ..., n-1, иначе было бы = j, а значит, j было бы префиксом n. Тогда схема ':= <а1 1, ..., аn > тоже префиксная, причем l '(Р) = l (Р) – рn, что противоречит оптимальности .
  2. Пусть теперь два кодовых слова n-1 и n максимальной длины отличаются не в последнем разряде, то есть n-1 = 'b', n = "b", ' ", причем ', " не являются префиксами для 1, ..., n-2 и наоборот. Тогда схема ':= <а1 1, ..., аn-2 n-2, аn-1 'b', аn "> также является префиксной, причем l'(Р) = l (Р) – рn, что противоречит оптимальности .
Теорема
Если n-1 = (ai i)n-1i=1 — схема оптимального префиксного кодирования для распределения вероятностей Р = р1 >= … >= рn-1 > 0 и рj = q' + q", причем
р1 >= … >= рj-1 >= рj+1 >= … >= рn-1 >= q' >= q" > 0,
то кодирование со схемой
n = (a1 1, ..., аj-1 j-1, аj+1 j+1, ..., an-1 n-1, aj j0, an j1)
является оптимальным префиксным кодированием для распределения вероятностей Рn = p1, ..., рj-1, pj+1, ..., рn-1, q', q".

Доказательство

  1. Если n-1 было префиксным, то n тоже будет префиксным по построению.

  2. Пусть схема 'n: = {аi i}ni=1 оптимальна для Рn. Тогда по предшествующей лемме 'n = {a1 '1, ..., аn-2 'n-2, аn-1 0, аn 1}. Положим l' = || и рассмотрим схему 'n-1: = {а1 '1, ..., аj , ..., аn-2 'n-2}, где j определено так, чтобы рj-1 >= q' + q" >= рj+1.


  3. 'nn - префиксное, значит, 'n-1 тоже префиксное.

  4. 'n - оптимально, значит, l 'n-1n-1) >= l n-1(Pn-1).

  5. lnn) = l n-1n-1) + рj <= l'n-1n-1) + pj = l'nn), но 'n - оптимально, значит, n оптимально.

Алгоритм Хаффмена

Следующий рекурсивный алгоритм строит схему оптимального префиксного алфавитного кодирования для заданного распределения вероятностей появления букв.
Алгоритм Хаффмена
Построение оптимальной схемы - рекурсивная процедура 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
Функция находит в массиве Р место, в котором должно находиться число 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. Рекурсивная часть алгоритма в точности следует доказательству теоремы предыдущего подраздела. С помощью функции в исходном упорядоченном массиве Р отбрасываются две последние (наименьшие) вероятности, и их сумма вставляется в массив Р, так чтобы массив (на единицу меньшей длины) остался упорядоченным. Заметим, что при этом место вставки сохраняется в локальной переменной j. Так происходит до тех пор, пока не останется массив из двух элементов, для которого оптимальный код известен. После этого в обратном порядке строятся оптимальные коды для трех, четырех и т. д. элементов. Заметим, что при этом массив вероятностей Р уже не нужен — нужна только последовательность номеров кодов, которые должны быть изъяты из массива кодов и продублированы в конце с добавлением разряда. А эта последовательность хранится в экземплярах локальной переменной j, соответствующих рекурсивным вызовам процедуры Huffman.
Пример
Построение оптимального кода Хаффмена для n = 7. В левой части таблицы показано изменение массива Р, а в правой части — массива С. Позиция, соответствующая текущему значению переменной j, выделена полужирным начертанием.
0.200.200.230.370.400.600100011010
0.200.200.200.230.370.4010001101111
0.190.190.200.200.23  011011000000
0.120.180.190.20    11000001010
0.110.120.18       001010011
0.090.11         0110010
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]