Материал: 1_2_3_ДХафф

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

13

Эта страница «сцепляется» с 1.1

1.2. Код ФаноШеннона

В методе, предложенном Р. Фано (R. M. Fano) и К. Шенноном (C. E. Shannon), префиксный код (и соответственно – кодовое дерево) строится следующим образом («сверху вниз»). Пусть набор (wi)1n – упорядочен, а именно: w1  w2  … wn  1  wn. В качестве корня дерева выбирается такой узел (и соответственно набор (wi)1n разбивается на 2 поднабора и так), что веса поддеревьев различаются минимально, т. е. если k = , то коды сообщений оказываются в левом поддереве, а коды сообщений в правом поддереве. Эта процедура повторяется для поддеревьев до тех пор, пока не будет получен лист в качестве текущего поддерева.

Пример построения кода ФаноШеннона. Пусть n = 5, m = 20 и

wi

8

3

3

3

3

i

А

Б

В

Г

Д

Т

20(АБВГД)

огда кодовое дерево ФаноШеннона есть

Здесь каждому узлу дерева приписаны символ или группа символов и их веса. Кодовые слова даны в таблице

wi

8

3

3

3

3

i

А

Б

В

Г

Д

ci

00

01

10

110

111

Полная длина кода есть L = 2(8 + 3 + 3) + 3(3 + 3) = 46 бит. Равномерный код (по 3 бита) дал бы суммарную длину L = 320 = 60 бит.

Отметим, что левые и правые поддеревья в кодовом дереве можно менять местами. При этом код будет изменяться, но значение L не изменится. Для определенности удобно, например, с целью облегчения проверки выполнения заданий, левым поддеревом выбирать поддерево с меньшим весом. Тогда в последнем примере будем иметь результирующее дерево следующего вида

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

порождает код

wi

8

3

3

3

3

i

А

Б

В

Г

Д

ci

0

100

101

110

111

Для этого кода имеем L = 81+ 3(3 + 3 + 3 + 3) = 44 бита, что меньше, чем дает код ФаноШеннона. Этот пример показывает, что код ФаноШеннона не является оптимальным кодом.

1.3. Задача оптимального кодирования

Итак, задача построения оптимального префиксного кода есть задача минимизации функции = i=1..n wi li  целочисленных положительных переменных (li)1n при заданном наборе (wi)1n и при условии (пока не формализованном) выполнения свойства префиксности кода. Набор переменных (li)1n, минимизирующий L, определяет структуру дерева (кода).

Интересно, что аналогичным образом формулируются и некоторые, казалось бы, совершенно другие задачи.

Задача поиска (тестирования). Производится поиск на основе последовательных сравнений (решений) или последовательных тестов: каждый новый вопрос (тест) задается (проводится) в зависимости от предыдущих ответов (от результатов предыдущих тестов). Рассматриваются бинарные тесты (задаются вопросы с ответами «да» или «нет»). Этот процесс можно описать с помощью бинарных деревьев решений. Узлы в таких деревьях соответствуют вопросам (тестам), ветви – исходам теста («да»/«нет» или 1/0). Деревья – строго бинарные. Лист дерева решений соответствует завершению (исходу) поиска (тестирования). В качестве примера можно привести анализ алгоритма бинарного поиска, приведенный в [8, 5.3]. Пусть {1, 2, …, n} – множество исходов поиска. Число шагов поиска (длина теста) есть длина пути li в дереве решений от корня до листа i. Пусть  wi – вероятность P( i) или частота предъявления элемента для поиска, приводящего к исходу поиска i. Тогда M(l) = i=1..n wi li  есть математическое ожидание времени поиска (среднее число шагов поиска или последовательного теста). Итак, задача поиска формулируется следующим образом: по заданным n, (i)1n и (wi)1n, где wi = P( i), построить стратегию поиска (дерево решений), минимизирующую математическое ожидание числа шагов поиска M(l) = i=1..n wi li.

Задача слияния множества упорядоченных списков. Заданы n упорядоченных списков S1S2, …, Sn. Пусть  i  1..n: wi = Si  длина списка  Si. Требуется построить один упорядоченный список S путем попарного слияния исходных S1S2, …, Sn и получаемых в процессе этих действий промежуточных упорядоченных списков. Базовая операция слияния двух упорядоченных списков Merge (SiSj) требует wi + wj  элементарных операций (сравнений и перемещений). Алгоритм Merge (SiSj) можно найти, например, в [8, 4.5]. Общее количество операций зависит от порядка попарных слияний. Этот порядок можно задать строго бинарным деревом слияний. Например, дерево

описывает следующую последовательность слияний:

S1, 2 = Merge (S1S2),

S5, 6 = Merge (S5S6),

S1, 2, 3 = Merge (S1, 2S3),

S4, 5, 6 = Merge (S5, 6S4),

S = Merge (S1, 2, 3S4, 5, 6).

Легко видеть, что здесь общее количество элементарных операций есть 3w1 + 3w2  + 2w3 + 2w4 + 3w5 + 3w6.

В общем случае совокупное количество элементарных операций есть i=1..n wi li , где li – количество слияний с участием элементов списка Si  или, что то же, уровень листа Si  в дереве слияний. Требуется построить дерево слияний, структура которого определит оптимальный порядок слияний, а минимальное общее число операций i=1..n wi li  будет определяться величинами (li)1n.

1.4. Метод Хаффмана

Элегантное решение (алгоритм) для задачи построения оптимального префиксного кода нашел Д. Хаффман (D. A. Haffman) [18]. Дадим описание этого алгоритма в рекурсивной форме.

Обозначим Wn = (wi)1n. Пусть набор Wn  упорядочен:

w1  w2  … wn  1  wn.

Если n = 2, то завершаем процесс кодирования, приписав сообщению с весом w1  код 1, а сообщению с весом w2  – код 0. Иначе (т. е. при n  2) выполняем следующие действия:

  1. Из минимальных весов wn  1  и  wn образуем .

  2. Из набора Wn исключаем элементы wn  1  и  wn и добавляем в него новый элемент . Полученный набор обозначим .

  3. Решаем таким же способом задачу с набором весов , а затем в полученном решении заменяем узел (лист) на поддерево из двух листьев wn  1  и  wn, приписав им коды 1 и 0 соответственно.