Изменения

Перейти к: навигация, поиск

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

3087 байт убрано, 15:14, 27 марта 2016
Нет описания правки
'''Алгоритм Хаффмана''' (англ. ''Huffman's algorithm'') — алгоритм [[Задача_об_оптимальном_префиксном_коде_с_сохранением_порядка._Монотонность_точки_разреза | оптимального префиксного кодирования]] алфавита. Был разработан в 1952 году аспирантом Массачусетского технологического института Дэвидом Хаффманом при написании им курсовой работы. Используется во многих программах сжатия данных, например, PKZIP 2, LZH и др.
 
== Определение ==
 
{{Определение
|definition=
Пусть <tex>A=\{a_{1},a_{2}, \ldots ,a_{n}\}</tex> — алфавит из <tex>n</tex> различных символов, <tex>W=\{w_{1},w_{2}, \ldots ,w_{n}\}</tex> — соответствующий ему набор положительных целых весов. Тогда набор бинарных кодов <tex>C=\{c_{1},c_{2}, \ldots ,c_{n}\}</tex>, где <tex>c_{i}</tex> является кодом для символа <tex>a_{i}</tex>, такой, что: :* <tex>c_{i}</tex> не является префиксом для <tex>c_{j}</tex>, при <tex>i \ne j</tex>, :* cумма <tex>\sum\limits_{i \in [1, n]} w_{i}\cdot |c_{i}|</tex> минимальна (<tex>|c_{i}|</tex> — длина кода <tex>c_{i}</tex>), называется '''Коды''' или кодом Хаффмана'''.}} == Алгоритм построения бинарного кода Хаффмана == Построение кода Хаффмана''' ('''Huffman codes''') — широко распространенный и очень эффективный метод сжатия данныхсводится к построению соответствующего [[ Двоичная_куча | бинарного дерева]] по следующему алгоритму: # Составим [[Список | список]] кодируемых символов, при этом будем рассматривать один символ как дерево, состоящее из одного элемента c весом, равным частоте появления символа в строке.# Из списка выберем два узла с наименьшим весом.# Сформируем новый узел с весом, которыйравным сумме весов выбранных узлов, и присоединим к нему два выбранных узла в зависимости от характеристик этих данныхкачестве детей.# Добавим к списку только что сформированный узел вместо двух объединенных узлов.# Если в списке больше одного узла, то повторим пункты со второго по пятый. === Время работы ===Если сортировать элементы после каждого суммирования или использовать [[Приоритетные_очереди | приоритетную очередь]], обычно позволяет сэкономить от 20% то алгоритм будет работать за время <tex>O(N \log N)</tex>.Такую асимптотику можно [[Алгоритм_Хаффмана_за_O(n) |улучшить до 90% объема<tex>O(N)</tex>]], используя обычные массивы.}}Рассматриваются данные, представляющие собой последовательность символов=== Пример === [[Файл:Huffman_abracadabra. В жадном алгоритме jpg|400px|thumb|right|Дерево Хаффмана используется таблицадля слова <tex>abracadabra</tex>]] Закодируем слово <tex>abracadabra</tex>. Тогда алфавит будет <tex>A= \{a, b, r, c, d\} </tex>, содержащая частоты а набор весов (частота появления тех или иных символов. С помощью этой таблицы определяется оптимальное представление каждого символа алфавита в виде бинарной строки. кодируемом слове) <tex>W=\{5, 2, 2, 1, 1\}</tex>: В дереве Хаффмана будет <tex>5</tex> узлов:
== Построение кода Хаффмана ==
В основу алгоритма Хаффмана положена идея: кодировать более коротко те символы, которые встречаются чаще, а те, которые встречаются реже кодировать длиннее. Для построения кода Хаффмана нам необходима таблица частот символов. Рассмотрим пример построения кода на простой строке '''''abacaba'''''<br>
{| class="wikitable"
! Узел || a || b || r || c с || d
|-
| 4 Вес || 5 || 2 || 2 || 1 ||1
|}
Следующим шагом будет построение дерева, где вершины - "символы", а пути до них соответствуют их префиксным кодам.
Для этого на каждом шаге будем брать два символа с минимальной частотой вхождения, и объединять их в новые так называемые "символы" с частотой равной сумме частот тех символов, которые мы объединяли.
В примере мы объединим символы b и с в символ bc с частотой 3.
[[Файл:Haffman1.jpg]]
Хаффман изобрел жадный алгоритм, позволяющий составить оптимальный префиксный код, который получил название код Хаффмана. Доказательство корректности этого алгоритма основывается на свойстве жадного выбора и оптимальной подструктуре. Вместо того чтобы демонстрировать, что эти свойства выполняются, а затем разрабатывать псевдокод, сначала мы представим псевдокод. Это поможет прояснить, как алгоритм осуществляет жадный выбор. В приведенном ниже псевдокоде предполагается, что <tex>C</tex> — множество, состоящее из <tex>n</tex> символов, и что каждый из символов <tex>c\in C</tex> — объект По алгоритму возьмем два символа с определенной наименьшей частотой {{---}} это <tex>f(c)</tex>. В алгоритме строится дерево и <tex>Td</tex>, соответствующее оптимальному коду, причем построение идет в восходящем направлении. Процесс построения начинается с множества, состоящего Сформируем из них новый узел <tex>|C|cd</tex> листьев, после чего последовательно выполняется весом <tex>|C|-12</tex> операций и добавим его к списку узлов: {| class="слиянияwikitable", ! Узел || a || b || r || cd |-| Вес || 5 || 2 || 2 || 2|} Затем опять объединим в результате которых образуется конечное дерево. Для идентификации двух наименее часто встречающихся объектов, подлежащих слиянию, используется очередь с приоритетами один узел два минимальных по весу узла {{---}} <tex>Qr</tex>, ключами в которой являются частоты и <tex>fcd</tex>. В результате слияния двух объектов образуется новый объект, частота появления которого является суммой частот объединенных объектов:<br><br>'''Huffman(<tex>C</tex>)''' <br>{| class="wikitable"! Узел || a || rcd || b |-<tex>n \gets |CВес || 5 || 4 || 2 |</tex> <br>}<tex>Q \gets C</tex> <br>'''for''' Еще раз повторим эту же операцию, но для узлов <tex>i \gets 1rcd</tex> '''to''' и <tex>n - 1b</tex> <br>: {| class="wikitable"! Узел || brcd || a:'''do''' Выделить память для узла <tex>z</tex> <br>|-::left[<tex> z</tex>]<tex> \gets x \gets</tex> Extract_Min(<tex> Q</tex>)<br> | Вес || 6 || 5 ::right[<tex>z</tex>]<tex>\gets y \gets </tex> Extract_Min(<tex>Q</tex>) <br>|}::<tex>f[z] \gets f[x]+f[y]</tex> ::Insert(На последнем шаге объединим два узла {{---}} <tex>Qbrcd</tex>, и <tex>za</tex> ) <br>:'''return''' Extract_Min(<tex>Q</tex> ) <tex> \rhd </tex> Возврат корня дерева <br><br>{| class=== Пример работы алгоритма ==="wikitable"! Узел || abrcd|-| Вес || 11|}[[Файл:Huffman.jpg]]<br>На каждом этапе показано содержимое очереди, элементы которой рассортированы в порядке возрастания их частот. На каждом шаге работы алгоритма объединяются два объекта (дерева) с самыми низкими частотами. Листья изображены в виде прямоугольников, в каждом из которых указана буква и соответствующая ей частота. Внутренние узлы представлены кругами, содержащими сумму частот дочерних узлов. Ребро, соединяющее внутренний Остался один узел с левым дочерним узлом, имеет метку 0значит, а ребро, соединяющее его с правым дочерним узлом, — метку 1. Слово кода для буквы образуется последовательностью меток на ребрах, соединяющих корень с листом, представляющим эту букву. По скольку данное множество содержит шесть букв, размер исходной очереди равен 6(часть ''а'' рисунка), а для построения мы пришли к корню дерева требуется пять слияний. Промежуточные этапы изображены в частях ''б-д''. Конечное дерево Хаффмана (''е''смотри рисунок) представляет оптимальный префиксный код. Как уже говорилось, Теперь для каждого символа выберем кодовое слово кода для буквы — это (бинарная последовательность меток на пути , обозначающая путь по дереву к этому символу от корня к листу ): {| class="wikitable"! Символ || a || b || r || с этой буквой.<br> || dВ строке 2 инициализируется очередь с приоритетами <tex>Q</tex>, состоящая из элементов множества <tex>С</tex>. Цикл '''for''' в строках 3|-8 поочередно извлекает по два узла, <tex>x</tex> и <tex>у</tex>, которые характеризуются в очереди наименьшими частотами, и заменяет их в очереди новым узлом, представляющим объединение упомянутых выше элементов. Частота появления <tex>z</tex> вычисляется в строке 7 как сумма частот <tex>x</tex> и <tex>y</tex>. Узел <tex>x</tex> является левым дочерним узлом <tex>z</tex>, а <tex>y</tex> — его правым дочерним узлом. (Этот порядок является произвольным; перестановка левого и правого дочерних узлов приводит к созданию другого кода с той же стоимостью.) После <tex>n - 1</tex> объединений в очереди остается один узел — корень дерева кодов, который возвращается в строке 9.=== Оценка времени работы ===| Код || 0 || 11 || 101 || 1000 || 1001|} При анализе времени работы алгоритма Хаффмана предполагаетсяТаким образом, что закодированное слово <tex>Qabracadabra</tex> реализована будет выглядеть как бинарная неубывающая пирамида. Для множества <tex>C</tex>, состоящего из <tex>n</tex> символов, инициализацию очереди <tex>Q</tex> в строке 2 можно выполнить за время <tex>O(n)01110101000010010111010</tex>. Цикл for в строках 3Длина закодированного слова {{--8 выполняется ровно <tex>n - 1</tex> раз, и поскольку для каждой операции над пирамидой требуется время}} <tex>O(lg(n))</tex>, вклад цикла во время работы алгоритма равен <tex>O(n \cdot lg(n))23</tex>бита. Таким образомСтоит заметить, полное время работы процедуры Huffman что если бы мы использовали алгоритм кодирования с входным множествомодинаковой длиной всех кодовых слов, состоящим из то закодированное слово заняло бы <tex>n33</tex> символовбита, равно <tex>O(n \cdot lg(n))</tex>что существенно больше.
== Корректность алгоритма Хаффмана ==
Чтобы доказать корректность жадного алгоритма HuffmanХаффмана, покажем, что в задаче о построении оптимального префиксного кода проявляются свойства жадного выбора и оптимальной подструктуры. В сформулированной ниже лемме показано соблюдение свойства жадного выбора.
{{Лемма
|id=lemma1
|about=1
|statement=Пусть <tex>C</tex> — алфавит, каждый символ <tex>c \in C</tex> которого встречается с частотой <tex>f[c]</tex>. Пусть <tex>x</tex> и <tex>y</tex> — два символа алфавита <tex>C</tex> с самыми низкими частотами.  Тогда для алфавита <tex>C</tex> существует оптимальный префиксный код, кодовые слова символов <tex>x</tex> и <tex>y</tex> в котором имеют одинаковую максимальную длину и отличаются лишь последним битом. |proof=Идея доказательства состоит в том, чтобы взять Возьмем дерево <tex>T</tex>, представляющее произвольный оптимальный префиксный код, и преобразовать для алфавита <tex>C</tex>. Преобразуем его в дерево, представляющее другой оптимальный префиксный код, в котором символы <tex>x</tex> и <tex>y</tex> являются листьями — листья с общим родительским узлом, причем в новом дереве эти листья находятся находящиеся на максимальной глубине.  Пусть символы <tex>a</tex> и <tex>b</tex> — два символа, представленные листьями с общим родительским узлом, которые имеют общий родительский узел и находятся на максимальной глубине дерева <tex>T</tex>. Предположим без потери общности, что <tex>f[a] \le leqslant f[b]</tex> и <tex>f[x] \le leqslant f[y]</tex>. Поскольку Так как <tex>f[x]</tex> и <tex>f[y]</tex> — две самые маленькие наименьшие частоты (в указанном порядке), а <tex>f[a]</tex> и <tex>f[b]</tex> — две произвольные частоты, то выполняются соотношения отношения <tex>f[x] \le leqslant f[a]</tex> и <tex>f[y] \le leqslant f[b]</tex>. В результате перестановки в дереве Пусть дерево <tex>T'</tex> — дерево, полученное из <tex>T</tex> путем перестановки листьев <tex>a</tex> и <tex>x</tex> получается , а дерево <tex>T''</tex> — дерево полученное из <tex>T'</tex>, а при последующей перестановке в дереве V перестановкой листьев <tex>b</tex> и <tex>y</tex> получается дерево . Разность стоимостей деревьев <tex>T''</tex>. Разность стоимостей деревьев Т и Т" равна <brtex>T'</tex>равна: <tex>B(T) - B(T') = \sum_sum\limits_{c \in C} f(c)d_T(Cc) - \sum_sum\limits_{c \in C} f(c)d_{T'}(Cc) =</tex><br><tex> = (f[a] - f[x])(d_T(a) - d_T(x)) \ge ,</tex> что больше либо равно <tex>0</tex>,<br> поскольку так как величины <tex>f[a] - f[x]</tex> и <tex>d_T(a) - d_T(x)</tex> неотрицательны. Величина <tex>f[a] - f[x]</tex> неотрицательна, потому что х <tex>x</tex> — лист с минимальной частотой, а величина <tex>d_T(a) - d_T(x)</tex> неотрицательнаявляется неотрицательной, потому что так как лист <tex>a</tex> — лист находится на максимальной глубине в дереве <tex>T</tex>. Аналогично, Точно так же перестановка листьев <tex>y</tex> и <tex>b</tex> не приведет будет приводить к увеличению стоимости. Таким образом, поэтому величина разность <tex>B(T') - B(T'')</tex> неотрицательнатоже будет неотрицательной.  Таким образом, выполняется неравенство <tex>B(T'') \le leqslant B(T'')</tex>. С другой стороны, и поскольку <tex>T</tex> — оптимальное дерево, то поэтому должно также выполняться неравенство <tex>B(T'') \le leqslant B(T'')</tex>, откуда . Отсюда следует, что <tex>B(T') = B(T'')</tex>. Таким образомЗначит, <tex>T''</tex> — оптимальное дерево, представляющее оптимальный префиксный код, в котором символы <tex>x</tex> и <tex>y</tex> — находящиеся на максимальной глубине дочерние листья одного и того же узлаимеют одинаковую максимальную длину, что и доказывает лемму.
}}
{{Лемма
|id=lemma2.
|about=2
|statement=Пусть дан алфавит <tex>C</tex>, в котором для каждого символа <tex>c \in C</tex> определены частоты <tex>f[c]</tex>. Пусть <tex>x</tex> и <tex>y</tex> — два символа из алфавита <tex>C</tex> с минимальными частотами. Пусть <tex>C'</tex> — алфавит, полученный из алфавита <tex>C</tex> путем удаления символов <tex>x</tex> и <tex>y</tex> и добавления нового символа <tex>z</tex>, так что <tex>C ' = C \backslash \{хx,уy \} \cup {z}</tex>. По определению частоты <tex>f</tex> в алфавите <tex>C'</tex> совпадают с частотами в алфавите <tex>C</tex>, за исключением частоты <tex>f[z] = f[x] + f[y]</tex>. Пусть <tex>T'</tex> — произвольное дерево, представляющее оптимальный префиксный код для алфавита <tex>C'</tex> Тогда дерево <tex>T</tex>, полученное из дерева <tex>T'</tex> путем замены листа <tex>z</tex> внутренним узлом с дочерними элементами <tex>x</tex> и <tex>y</tex>, представляет оптимальный префиксный код для алфавита <tex>C</tex>. |proof=Сначала покажем, что стоимость <tex>B(T)</tex> дерева <tex>T</tex> можно выразить может быть выражена через стоимость <tex>B(T')</tex> дерева <tex>T'</tex>. Для каждого символа <tex>c \le in C - \backslash \{x,y\}</tex> выполняется соотношение верно <tex>d_T(C) = d_{T'}(c)</tex>, следовательнозначит, <tex>f[c]d_T(Cc) = f[c]d_{T'}(c)</tex>. Поскольку Так как <tex>d_T(x) = d_{T}d_T(y) = d_{tT'}(z) + 1</tex>, получаем соотношение<br>то <tex>f[x]d_T(x) + f[y]d_{T}d_T(y) = (f[x] + f[y])(d_{T'}(z) + 1) = f[z]d_{T'}(z) + (f[x] + f[y])</tex><br>из которого чего следует равенство <br>, что <tex> B(T) = B(T') + f[x] + f[y] </tex> <br>ИЛИ <br>или <tex> B(T') = B(T) - f[x] - f[y] </tex>. <br> Докажем лемму методом от противного. Предположим, что дерево <tex> T </tex> не представляет оптимальный префиксный код для алфавита <tex> C </tex>. Тогда существует дерево <tex> T'' </tex>такое, для которого справедливо неравенство что <tex> B(T'') < B(T) </tex>. Согласно лемме (1), элементы <tex>x</tex> и <tex>y</tex> без потери общности можно считать дочерними элементами одного и того же узла. Пусть дерево <tex>T'''</tex> получено из дерева <tex>T''</tex> путем замены заменой элементов <tex>x</tex> и <tex>y</tex> листом <tex>z</tex> с частотой <tex>f[z] = f[x] + f[y] </tex>. Тогда можно записать:<br>  <tex>B(T''') = B(T'') - f[x] - f[y] < B(T) - f[x] -f[y] = B(T')</tex>,<br>  что противоречит предположению о том, что дерево <tex>T'</tex> представляет оптимальный префиксный код для алфавита <tex>C'</tex>. Таким образомЗначит, наше предположение о том, что дерево <tex>T</tex> должно представлять не представляет оптимальный префиксный код для алфавита <tex>C</tex>, неверно, что и доказывает лемму.
}}
Лемма 16.3.
Доказательство.
{{Теорема
|id=th1
|statement=
Процедура Huffman Алгоритм Хаффмана дает оптимальный префиксный код.
|proof=
Справедливость теоремы непосредственно следует из лемм (1) и (2)
}}
== Литература См. также ==*[[Оптимальное_хранение_словаря_в_алгоритме_Хаффмана | Оптимальное хранение словаря в алгоритме Хаффмана]] == Источники информации == * Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн . Алгоритмы: построение и анализ — 2-е изд. — М.: «Вильямс», 2007. — Сс. 1296459. — ISBN 5-8489-0857-4*[http://en.wikipedia.org/wiki/Huffman_coding Wikipedia — Huffman coding]*[http://ru.wikipedia.org/wiki/%C4%E2%EE%E8%F7%ED%EE%E5_%E4%E5%F0%E5%E2%EE Википедия — Бинарное дерево]*[http://ru.wikipedia.org/wiki/Префиксный_код Википедия — Префиксный код] [[Категория: Дискретная математика и алгоритмы]] [[Категория:Алгоритмы сжатия]]
Анонимный участник

Навигация