| Крупнейший каталог ресурсов по сжатию! Пополняйте! |
| ||
|
Сайт о сжатии >>
Новинки |
О сервере
(Compression Catalog! |
ENGLISH)
Книга "Методы сжатия данных" >> Без потерь | Изображений | Видео Разделы >> Cтатьи | Видео | Arctest | Ссылки | Ru.compress | Форум Проекты >> Д.Ватолина | А.Ратушняка | М.Смирнова | В.Юкина | Е.Шелвина | Д.Шкарина |
||
|
|
||||||||||||||||||||||||||||||||
|
Процедура InitTree инициализирует двоичное дерево, обнуляя значения, веса, и указатели на родителя и потомков текущего листа. Далее выполняется первый проход по источнику данных с целью проверки частотности символов - результаты которого запоминаются в массив CharCount. Размерность этого массива должна равняться количеству элементов алфавита источника. В общем случае, для сжатия произвольного компьютерного файла, его длина должна составлять 256 элементов. Затем, в соответствии с полученным набором частот строим двоичное дерево ReBuildTree. Во время второго прохода, перекодируем источник в соответствии с деревом и записываем получаемые последовательности бит в сжатый файл. Для того, чтоб потом возможно было восстановить сжатые данные - необходимо, также, в полученный файл, сохранить копию двоичного дерева WriteTree.
|
|
|||||||||||||||||||||||||||||
|
Динамическое сжатие использует более гибкий подход. Первоначально двоичное дерево кодов является пустым и процедура InitTree записывает только лист содержащий "пустой" символ. Затем кодируется и записывается в сжатый файл поступивший на вход текущий символ. Процедура ReBuildTree обновляет дерево в соответствии с этим символом. Символы добавляются в словарь только по мере их появления в файле источнике. Если появившийся символ раньше не встречался, то выводим в поток код "пустого" символа, то есть символа, который раньше не встречался, а затем ASCII код самого символа, который гарантирует нам, что при разархивировании восстанавливаемая информация не сможет двояко трактоваться.
Динамическое сжатие методом Хаффмана было предложено независимо Фоллером [1973] и Галлагером [1978]. В 1985 Дональд Кнут разработал окончательный усовершенствованный вариант алгоритма - вследствие чего алгоритм получил название FGK (Faller, Gallager, Knuth) алгоритма. Основным принципом FGK алгоритма - является свойство "братства". Двоичное дерево обладает данным свойством, если каждый его узел (за исключением корневого) имеет в дереве узла "брата" и, при расположении всех узлов в списке, в порядке неувеличения их весов, узлы "братья" находятся по соседству. "Братьями" назовём два узла, которые имеют общего родителя.
Например, для указанного дерева, при выписывании узлов в требуемый список - получаем:
Где верхний ряд означает узел дерева, а нижний его вес. Таким образом выясняем, что данное дерево обладает свойством "братства", так как каждый, кроме корневого, узел имеет "брата" и все узлы "братья" находятся в списке по соседству { А - Б }, { В - Г }, { Уз1 - Уз2 }. Листья такого дерева представляют собой символы извлекаемые из исходного файла, а веса листьев - количество появлений этих символов в исходном файле. По мере прохождения алгоритма через файл веса листьев могут либо увеличиваться, либо оставаться без изменений. Первоначально дерево состоит из одного "пустого" узла. Этот узел является единственным листом, который не содержит никакого символа и имеет нулевой вес. Он предназначается для кодирования тех символов, которые ранее не попадали во входной поток. Далее, по мере поступления на вход программы архиватора символов из исходного файла, проверяется, есть ли уже такой символ в дереве или нет. Если символ присутствует - его код передаётся сжатому файлу, затем его (и всех его родителей) вес инкрементируется и дерево пересчитывается по новой. При отсутствии символа в дереве - в сжатый файл передаётся код "пустого" символа, следующие за которым биты содержат информацию однозначно определяющий новый символ (чаще всего это ASCII код). После чего в дерево добавляется ещё один лист, который содержит новый символ и имеет вес 1. Инкрементируются веса всех родителей новоиспечённого узла. Снова пересчитываем дерево. Так как "пустой" узел имеет вес 0, то при пересчёте дерева он всегда будет иметь самый длинный код. Следовательно, каждый появившийся новый символ будет иметь код равный по длине коду "пустого" узла или меньший его на 1 бит. Благодаря чему самые редковстречаемые символы, которые будут, соответственно, позже появляться - будут иметь самые длинные коды - что полностью соответствует принципам алгоритма сжатия Хаффмана.
В соответствии с описанным выше алгоритмом сжатия методом Хаффмана можно выделить три основные процедуры от алгоритмов реализации которых зависят характеристики (производительность, коэффициент сжатия и т.д.) программы сжатия. Процедура InitTree - инициализирующая двоичное дерево, процедура добавления нового символа в дерево и процедура ReBuildTree - модифицирующая текущее дерево в соответствии с новой поступившей информацией. Рассмотрим принципы их реализации в обратном порядке. От процедуры ReBuildTree во многом зависит быстродействие программы архиватора, так как именно она должна после каждого поступившего символа заново перестраивать двоичное дерево кодов. Самый простой и надёжный способ - конечно же полное построение дерева - однако в этом случае алгоритм будет настолько медленным, что при архивации даже самого маленького файла можно будет успеть выпить пару чашек кофе и немножко вздремнуть. Полное построение двоичного дерева - довольно большая и нелёгкая работа даже для современных компьютеров. Но выход всё-таки есть! Вспомним, что мы пользуемся для сжатия данных FGK алгоритмом, который обладает свойством "братства". Согласно правилам, при расположении всех узлов в списке, в порядке неувеличения их весов, узлы "братья" находятся по соседству. Откуда следует, что вместо того, чтоб постоянно полностью модифицировать всё дерево можно хранить его в виде упорядоченного списка и изменять только те элементы списка, которые требуют изменения. Для рассмотренного выше дерева упорядоченный список представлял собой:
Теперь, если очередным символом во входящем потоке будет символ "Г", тогда увеличиваем его вес на 1. Очевидно, что вес родителя Уз2 теперь не соответствует сумме весов потомков, поэтому его тоже следует инкрементировать. Увеличив вес узла Уз2 - переходим к его родителю - корню дерева Уз3, вес которого так же увеличивается на 1. Новый список теперь будет иметь вид:
А дерево соответственно станет:
Нам потребовалось всего три действия, чтоб изменить дерево. В общем случае необходимо увеличить на 1 вес листа поступившего на вход символа, а затем инкрементировать веса всех его родителей вплоть до корня дерева. То есть среднее число операций будет равняться среднему количеству битов (или средней длине пути от листа к корню дерева), необходимых для того, чтоб закодировать символ. Однако возможен вариант, когда упорядоченность списка нарушается. Например, если для полученного дерева следующий символ снова будет "Г", и мы просто увеличим веса, тогда после их кодирования список примет вид:
Как видно свойство упорядоченности нарушено. Символ "Г" вес которого больше чем вес Уз1 стоит раньше него в списке. В таком случае необходимо произвести операцию упорядочивания списка. Для этого после инкрементации веса символа начинаем двигаться по списку, в сторону увеличения веса, до тех пор пока не найдем последний узел с меньшим весом чем текущий. Затем переставляем текущий и найденный узлы восстанавливая таким образом порядок в списке. При этом родители обоих узлов тоже изменяются. Родителем текущего узла становится родитель найденного и наоборот. Дальше увеличивается вес нового родителя и так далее, до самого корня дерева. Новый список уже будет выглядеть следующим образом:
А дерево соответственно:
Из приведённого примера видно, что чем чаще встречается тот либо иной символ, тем ближе он находится к началу списка и короче код имеет. В последнем двоичном дереве самый часто встречаемый символ "Г" имеет самый короткий код - 1 бит. В общем виде алгоритм модификации дерева можно записать так:
|
|
|||||||||||||||||||||||||||||
|
Теперь, после того как стали ясны принципы построения и модификации двоичного дерева, возникает вопрос о добавлении в дерево нового символа. Согласно алгоритму, при появлении во входном потоке нового символа в файл архив выдаётся код "пустого" символа, а затем последовательность бит однозначно идентифицирующих новый символ. Процедура добавления нового символа состоит в том, что в хвост упорядоченного списка, коим является наше дерево кодов, добавляются два новых элемента - первый из них становится листом для нового символа, а второй дополнительным узлом. Только что созданный дополнительный узел берёт на себя родителя "пустого" символа и сам становится родителем для нового и "пустого" символов. Дополнительный узел необходим, так как общее количество узлов дерева должно быть равным количеству листьев -1.
Например, при имеющемся двоичном дереве с одним символом "А" и нулевым узлом, если следующим символом поступающим из файла источника является "Б" необходимо добавить его в список.
Первоначально веса нового символа и его родительского узла устанавливаются равными 0, однако в конце процедуры добавления символа идёт обращение к процедуре построения дерева ReBuildTree, где указывается только что созданный символ. ReBuildTree увеличивает на один вес символа, а так же его родителей в дереве и перемещает их (если это необходимо) согласно алгоритму по списку так, чтоб сохранялись принципы упорядоченности списка, и не нарушалось построение двоичного дерева.
|
|
||||||||||||||||||||||||
|
Инициализация дерева, процедура InitTree, представляет собой ни что иное, как просто создание "пустого" символа. В самом начале упорядоченного списка. Так как при начале кодирования дерево не содержит ни одного символа, то "пустой" символ не смотря на свой нулевой вес будет первым и единственным и при обращении к нему выдаст в поток однобитный код 0.
Статическое кодирование Хаффмана довольно широко применяется в современных алгоритмах сжатия, но не в чистом виде, а как одна из ступеней сжатия в более сложных алгоритмах. Динамический же вариант алгоритма на практике, в последнее время, уступил более гибким и скоростным алгоритмам и применяется в основном только в экспериментальных целях. наверх
Сайт о сжатии >> Новинки | О сервере | Статистика Книга "Методы сжатия данных" >> Универсальные | Изображений | Видео Разделы >> Download (статьи+исходники) | Ссылки | Ru.compress | Arctest | Видео | Каталог ссылок | Форум Проекты >> Д.Ватолина | А.Ратушняка | М.Смирнова | В.Юкина | Е.Шелвина | А.Филинского | Д.Шкарина | С.Оснача |
||||||||||||||||||||
|
Оставьте ваши замечания, предложения, мнения! О найденных ошибках пишите на compression_на_graphicon.ru © Д.Ватолин, А.Ратушняк, М.Смирнов, В.Юкин, Е.Шелвин, Д.Шкарин и др., текст, состав., 2001-2008 © А.Андреев, оформление, 2002
|
||||