Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Федеральное агентство по образованию РФ

ГОУ ВПО «Уральский государственный технический университет - УПИ»

Факультет дистанционного образования

Лабораторная работа № 2

по дисциплине: Теория информации

на тему: Сжатие данных. Метод Хаффмана

Семестр № 4

Преподаватель

Студент гр. № ИТЗ–26010ДТ

Номер зачётной книжки

Екатеринбург

2008

 

Лабораторная работа № 2 по Теории информации

№ записи в книге регистрации____________ дата регистрации _______________2008 г.

Преподаватель

Студент группа № ИТЗ-26010ДТ

Деканат ФДО____________


СОДЕРЖАНИЕ:

ВВЕДЕНИЕ. 3

Мультимедиа-сжатие. 4

алгоритмы сжатия без потерь. 7

Метод повторяющихся символов (Running) 7

Зива – Лемпеля - Велча (LZW) 7

Хаффмана (Huffman) 8

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

Руководство к программе. 14

Сравнение сжатия файлов.. 17

Код программы.. 18

Вывод.. 34

Литература: 35


ВВЕДЕНИЕ

Проблема сжатия информации была актуальна всегда, т. к. объем носителей, хоть и неуклонно растёт, но всё же ограничен.

Стоит ли в таких условиях задумываться о сути архиваторов и следует ли вообще их использовать? Для этого есть целый ряд причин:

·  при передаче файлов по почте или с помощью терминальных программ критичным является каждый килобайт, да и, кроме того, при пересылке большого числа файлов (особенно со сложной структурой каталогов) проще в письмо вложить всего один файл - архив;

·  пропускная способность локальных сетей ограничена. При пересылке больших массивов информации по сети рекомендуется использовать архиваторы не только для уменьшения трафика, но и с целью упрощения конечной верификации: после копирования проще проверить корректность архива, нежели проверять сотни файлов на целостность, просматривая их содержимое;

НЕ нашли? Не то? Что вы ищете?

·  пишущий CD-ROM стал стандартом для ПК среднего класса, многие из нас сталкиваются с проблемой, когда на компакт диск необходимо перенести очень сложную структуру каталогов, тогда как файловая система компакт-диска не допускает более чем восьмикратный уровень вложенности папок. Это было бы очень серьезной проблемой, если бы не архиваторы, позволяющие упаковывать всю структуру в один файл;

·  многие архиваторы являются очень эффективными кодировщиками, позволяющими скрыть конфиденциальную информацию от чужих глаз, запаковав свои файлы и установив на архив пароль.

Из выше сказанного можно сделать важный вывод: архивация сейчас используется для увеличения скорости передачи при ограниченной пропускной способности.

На сегодняшний день существует достаточно много способов архивации данных. Все они делятся на два больших, принципиально разных подхода: сжатие с потерями и без потерь. Под сжатием с потерями (или необратимым сжатием) предполагают такое преобразование входного массива данных, при котором выходной поток представляет, достаточно похожий и, возможно, даже не отличный от исходного по внешним характеристикам поток, однако, отличается от него объемом и составляющим содержимым. Примерами такого сжатия являются mp3 файлы и jpeg изображения.

Но данные алгоритмы не всегда удобны и эффективны. В некоторых случаях, например с текстом, сжатие с потерями абсолютно нецелесообразно, ибо текст – единое целое и исчезновение символов, пусть даже при уменьшении веса файла, недопустимо.

Для этого случая были разработаны так называемые алгоритмы сжатия информации без потерь данных.

Все приведенные выше выкладки имеют целью показать актуальность рассмотрения алгоритмов сжатия.

Мультимедиа-сжатие

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

Сжатие графической информации основано на том, что человеческий глаз, обычно, способен воспринимать лишь ограниченное число градаций основных цветов. Вспоминается, в связи с этим, пример из литературы, как опытные ткачихи, подчас, способны были различать несколько сотен градаций одного и того же цвета (например, черного).

Так вот, соседние пиксели, отличающиеся друг от друга по цвету незначительно, отождествляются (коэффициент "похожести" соседних пикселей определяет баланс качества изображения и степени сжатия, см. рис.). Одинаковые группы пикселей объединяются в блоки и, далее, применяется один из методов сжатия без потери качества. Например, в формате JPEG сначала производится разбиение изображения на блоки 8x8. Затем данные блоки изображения подвергаются так называемому дискретному косинус-преобразованию (ДКП, на данном этапе происходит потеря качества) и, на заключительном этапе, применяется метод Хаффмана. Данный формат очень эффективен при хранении фотографий и прочей полноцветной графики.

Кроме того, ключевым моментом является количество бит, выделяемых для хранения цвета одного пикселя. Чем больше бит выделено под хранение одного пикселя, тем реалистичнее картинка и тем больше объем графического файла. Так, в большинстве случаев мы имеем дело с графикой, цветовая палитра которой - 16 или 24 бит (Hi и True color соответственно). Всем известный формат графических файлов GIF ограничивает цветовую палитру одним байтом (всего 256 цветов для каждого пикселя), тем самым, достигая значительной экономии в объеме файла. Следует учесть, что фотографии в данном формате хранить не следует. Приличное качество недостижимо, а объем уменьшается незначительно. Преимуществом данного формата является возможность хранения простейшей анимации.

В рамках наиболее популярного сегодня формата MPEG3, аудио-сжатие основывается на удалении наименее значимых деталей звучания (согласно психоакустической модели). Например, человек способен воспринимать звук узкого частотного диапазона, не воспринимаются сигналы, мощность которых ниже определенного пограничного значения и т. д. Очищая звуковой поток от "лишних", невоспринимаемых человеком сигналов, достигается более чем десятикратная экономия.

Основные параметры, определяющие качество сжатого файла - это битрейт и частота дискретизации. Битрейт (bit rate, битовая частота) определяет количество передаваемой за единицу времени информации; чем больше битрейт, тем выше реалистичность и соответствие исходному звуковому потоку (оптимальное значение, обеспечивающее качество компакт-диска - 128 kBit/s).

Убедиться в обоснованности этого числа можно так: для прослушивания обычного, несжатого потока аудио (записанного на Audio CD) требуется привод CD-ROM с формулой 1x. Скорость линейного чтения у такого привода равна 150 kByte/s (т. е. 1200 kBit/s). Разделим это число на отношение объемов несжатого и сжатого аудио-потоков. Как мы уже говорили, коэффициент сжатия в формате mp3 примерно равен десяти. Получаем 120 kBit/s (что и требовалось доказать).

Частота дискретизации - еще один очень важный параметр, определяющий максимальную частоту выходного сигнала (и, соответственно, чем он больше, тем ближе звучание mp3 файла к оригиналу). Общепринята точка зрения, согласно которой восприятие человеком звука ограничено частотным диапазоном от 20Hz до 20kHz. Чаще всего значение частоты дискретизации устанавливается в два раза большее, чем верхний порог восприятия (44.1 или 48 kHz).

Все известные на сегодня форматы видео поддерживают сжатие с потерей качества. Видео состоит из потока сменяющих друг друга кадров и синхронизованного с ним аудио-потока. В каждом из форматов видео используется комбинация из наиболее эффективных способов сжатия графики и звука. Но, помимо сжатия каждого кадра и аудио-потока, используются и специфические приемы.

Как известно, пороговая частота дискретного восприятия человеком сменяющих друг друга графических образов - 25 кадров/сек. В силу этого обстоятельства, наличие в выходном потоке большего числа кадров не оправдано.

Более того, смежные кадры содержат одни и те же объекты сцены. Следовательно, хранение каждого из кадров можно заменить хранением изменений, произошедших со времени показа предыдущего кадра. Таким образом, весь фильм можно представить в виде последовательности ключевых кадров, сохраненных с небольшим коэффициентом сжатия и промежуточных (зависимых) кадров, ссылающихся на предыдущие ключевые или зависимые кадры. Зависимые кадры минимальны по размеру и содержат изменения, которые претерпела сцена. В силу этого достигается впечатляющая экономия дискового пространства.

Существует несколько видео-форматов. MPEG1 - первый и довольно эффективный формат. Этот формат сделал возможным просмотр видео фильмов даже на самых скромных ПК на базе процессоров Pentium (без поддержки мультимедиа-инструкций MMX). Основным недостатком этого формата являлся недостаточный коэффициент сжатия. Например, полуторачасовой фильм в таком формате занимал два компакт диска. Пользователи, смотревшие мелодраму, очень сильно переживали необходимость смены компакт-дисков в моменты развязки сюжета :).

Формат видео MPEG2 (и его VBR-реализация) используется в рамках технологии DVD и обеспечивает высокое качество выходного видео. Особой степенью сжатия не характеризуется, в силу того, что носители, используемые в технологии DVD, обладают достаточной емкостью (DVD-Disk имеет емкость от 4,7 GByte). Соответственно процесс декодирования не особенно требователен к аппаратным ресурсам ПК.

Формат MPEG4 обладает очень высокой степенью сжатия при довольно приличном качестве. Основным недостатком этого формата являются довольно высокие требования к вычислительной мощности ПК. Так, при просмотре фильмов в этом формате на системе с процессором ниже Pentium III с частотой 600 MHz, иногда, можно наблюдать слайд-шоу. Удивляет тот факт, что аудио поток в таком случае будет воспроизводиться без искажений. Таким образом, при выборе ПК, одной из задач которого будет воспроизведение видео в данном формате, следует ориентироваться на систему на базе процессоров класса Pentium III или Athlon с частотой от 1GHz (или их дешевые аналоги Celeron и Duron). Особое внимание следует уделить производительности дисковой подсистемы (в силу того, что "картинка" и "звук" хранятся в видео-файле отдельно и требуется высокая скорость произвольного доступа к диску).

алгоритмы сжатия без потерь

Метод повторяющихся символов (Running)

Это самый простой метод сжатия информации. Суть метода заключается в том, что последовательность повторяющихся подряд байт можно заменить структурой:

·  сам байт

·  «флаг», показывающий, что данный участок сжат

·  кол-во повторений первого байта

Пример:

Допустим, что есть строка, содержащая 10 подряд идущих пробелов. Данная последовательность из 10 одинаковых байт кодируется в 3.

·  Первый байт - собственно сам пробел (кодируемый байт).

·  Второй байт - "флаг", который указывает, что мы должны развернуть предыдущий в строке байт в последовательность при восстановлении строки.

·  Третий байт - байт счетчик. Будет равен 10, т. к. мы кодируем 10 пробелов.

Можно увидеть, что достаточно иметь последовательность из более 3-х одинаковых символов, чтобы на выходе получить блок информации меньший по размеру, но допускающий восстановление информации в исходном виде.

Плюс: простота реализации

Минус: малая степень сжатия (не так часто встречаются непрерывные серии одинаковых символов).

Зива – Лемпеля - Велча (LZW)

История этого алгоритма начинается с опубликования в мае 1977 г. Дж. Зивом и А. Лемпелем статьи в журнале "Информационные теории". В последствии этот алгоритм был доработан А. Велчем, и в окончательном варианте отражен в статье "IEEE Computer" в июне 1984 . В этой статье описывались подробности алгоритма и некоторые общие проблемы, с которыми можно столкнуться при его реализации. Позже этот алгоритм получил название - LZW (Lempel - Ziv - Welch) .

Алгоритм LZW представляет собой алгоритм кодирования последовательностей неодинаковых символов. Возьмем для примера строку: "Объект TSortedCollection порожден от TCollection.".В данной строке слово "Collection" повторяется дважды. В этом слове 10 символов = 80 бит. Если мы заменим это слово, в выходном файле, во втором его включении, на ссылку на первое включение, то получим сжатие информации. Если рассматривать входной блок информации размером не более 64К и ограничить длину кодируемой строки в 256 символов, то, учитывая байт "флаг" получим, что строка из 80 бит, заменяется 8+16+8 = 32 бита. (1 байт – флаг, 2 байта – ссылка, 1 байт – длинна кодируемой последовательности). Если существуют повторяющиеся строки в файле, то они будут закодированы в таблицу. Очевидным преимуществом алгоритма является то, что нет необходимости включать таблицу кодировки в сжатый файл. Другой важной особенностью является то, что сжатие по алгоритму LZW является однопроходной операцией в противоположность алгоритму Хаффмана, которому требуется два прохода. 

Плюс: высокая степень сжатия

Минус: сложность в реализации.

Хаффмана (Huffman)

Сжатие файла по алгоритму Хаффмана основывается на частоте повторения символов. Для сжатия необходимо подсчитать сколько раз встречается каждый символ из расширенного набора ASCII. После подсчета частоты вхождения каждого символа, необходимо отсортировать получившуюся таблицу (символ – частота вхождения) по частоте. Каждую ссылку из таблицы назовем "узлом". В дальнейшем (в дереве) мы будем позже размещать указатели, которые будут указывать на этот "узел".

Пример:

Пусть есть файл длинной в 100 байт и состоящий из 6 различных символов. Мы подсчитали вхождение каждого из символов в файл и получили следующее:

A

B

C

D

E

F

0.25

0.2

0.05

0.1

0.1

0.3

В дальнейшем элементы таблицы будем считать узлами, а их вероятность – весом узлов. Дерево получим следующим путём:

Сначала выбираются 2 узла с наименьшим весом, вместо них создаётся новый узел, с весом, равным их сумме. Повторяем данную операцию до тех пор, пока не останется 1 узел с весом в единицу.

Теперь можно приступить к кодированию, для этого начинаем обход дерева с корня (узла с весом в 1) и спускаемся до самих символов (узлов, листьев дерева). При этом мы прослеживаем вверх по дереву все повороты ветвей и, если мы делаем левый поворот, то запоминаем 0-й бит, если правый - 1-й бит.

Таким образом, для символа А получим код 00, аналогично для других:

A 00

B 01
C 1000
D 1001

E 101
F 11

Теперь подсчитаем сжатие:

было 100 байт = 800 бит

стало 25*2 + 20*2 + 5*4 + 10*4 + 10*3 + 30*2 = 240 бит = 30 байт

Получили сжатие на 70%.

Но, теперь все это дело надо разархивировать. Т. е. надо сохранить наше дерево. В данной работе сохраняется непосредственно таблица частот. Т. е. процесс построения дерева происходит дважды: при архивации и при разархивации.

6 символов = 6 байт

6 частот = 6*4 = 24 байт

30+6+24 = 60 байт

Также возьмём служебную информацию (время создания, время редактирования, атрибуты, имя файла): 4+4+1+11 (длину файла будем считать 11 символов, включая расширение) = 20 байт

Получили архив в 20+60 = 80 байт

Окончательно сжатие на 20%. В среднем алгоритм Хаффмана даёт 20-25% сжатие (для текстовых документов).

Плюс: нет необходимости в байтах-флагах, т. е. не надо разделять битовые серии.

Минус: требуется два прохода во время архивирования: первый – подсчёт кол-ва вхождения каждого байта, второй – непосредственно архивирование.

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

Это алгоритм архивации без потери качества. Исходный файл состоит в основном из однородных цепочек байтов, либо количество используемых символов довольно мало (т. е. файл состоит из небольшого подмножества элементов таблицы ASCII). Представим себе самый общий случай, когда в файле представлена большая часть таблицы ASCII и почти нет однородных последовательностей. В таком случае выгоду можно получить только если разные байты (символы) встречаются в данном файле с различной частотой. Тогда наиболее часто встречающиеся символы могут быть закодированы меньшим числом бит, а те, что встречаются довольно редко наоборот большим числом бит. В итоге результирующий файл с большой вероятностью будет меньшего объема, чем исходный.

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

Можно представить, что все файлы - это тексты, написанные в алфавите, состоящем из 256 букв (так оно на самом деле и есть). Рассмотрим все множество файлов, размер которых не превышает n byte (где n произвольное число). И допустим, что существует некий алгоритм кодирования, который любой из этих файлов сжимает с "положительной" эффективностью. Тогда множество всех их архивов содержится во множестве всех файлов, размер которых меньше n byte. Согласно нашему предположению существует взаимно-однозначное (биективное, как сказали бы математики) соответствие между двумя конечными множествами, число элементов в которых не совпадает. Чего быть не может (так называемый принцип Дирихле; да, сказывается математическое образование). Отсюда можно сделать довольно значимые выводы:

1) не существует архиватора, который бы одинаково хорошо паковал любые файлы,

2) для любого архиватора найдутся файлы, в результате сжатия которых будут получаться архивы в лучшем случае не меньшего размера, чем исходные файлы.

Теперь приступим к описанию алгоритма Хаффмана. Рассмотрим его на примере следующего файла:

cccacbcdaaabdcdcddcddccccccccccc{End of file}

Распишем частоты каждого из символов:

a - 4,
b - 2,
c - 19,
d - 7.

Весь файл занимает 32 byte. Каждый из символов в исходном файле кодируется согласно таблице ASCII восемью битами:

a - ,
b - ,
c - ,
d - .

Шаг №1. Расположим эти четыре символа в порядке убывания их частот:
{(c,19), (d,7), (a,4), (b,2)}

Шаг №2. На следующей строке запишем набор, полученный из предыдущего следующим образом:

·  вместо двух последних символов с их частотами запишем новый элемент, у которого вместо названия символа будет запись "Вершина #n", а вместо частоты - сумма частот последних двух элементов предыдущего набора;

·  отсортируем полученный набор по убыванию.
{(c, 19), (d, 7), ("вершина #1", 6)}

Шаг №3. Переходим на шаг №2 до тех пор, пока набор не будет состоять только из одного элемента:

("вершина #last_number", summa_vseh_chastot):
{(c, 19), ("вершина #2", 13)}

{("вершина #3", 32)}

Что же в итоге? Посмотрите на рисунок. Вы видите дерево, растущее с листьев! Если соединять чертой каждый из элементов "вершина #x" с теми элементами предшествующих наборов, сумма частот которых хранится во второй части элемента "вершина #x", то мы получим своего рода дерево (в программировании подобные структуры называются бинарными деревьями).

Смысл этой древесной структуры состоит в том, чтобы каждому из символов сопоставить различное число бит в зависимости от его частоты (как мы уже говорили, в несжатом файле символы хранятся в виде групп по восемь бит). Если внимательно всмотреться в следующий рисунок, вы увидите, что каждая из ветвей помечена либо нулем, либо единицей. Таким образом, если мысленно пройтись по дереву от корня до какой-либо вершины, содержащей символ, то последовательность нулей и единиц, встречающихся у вас на пути, и будет новой комбинацией бит для этого символа. Например, символ "c" как наиболее часто встречающийся будет кодироваться одним битом "0", символ "d" двумя битами "10" и т. д.

Давайте, напоследок, попробуем рассмотреть еще один пример файла, частотное дерево которого выглядит несколько более сложно, нежели в предыдущем случае. Бывает так, что частоты отдельных символов одинаковы или почти одинаковы, это приводит к тому, что такие символы кодируются одним и тем же числом бит. Однако алгоритм построения дерева от этого не меняется:

ddddddaaaaaabbbbbbccccdffffffffffdddd{37 byte, end of file}

В двоичном виде файл будет выглядеть так:

{296 bit, end of file}

Распишем частоты каждого из символов:

a - 6
b - 6
c - 4
d - 11
f - 10

Весь файл занимает 37 byte. Каждый из символов в исходном файле кодируется согласно таблице ASCII восемью битами. Посмотрим, как будут кодироваться эти символы в сжатом файле. Для этого построим соответствующее дерево (не забываем сортировать полученный набор):

1. {(d,11), (f,10), (a,6), (b,6), (c,4)};
2. {(d,11), (f,10), (вершина #1, 10), (a,6)} (заметьте, сортировка по убыванию частот обязательна!);
3. {(вершина #2, 16), (d,11), (f,10)};
4. {(вершина #3, 21), (вершина #2, 16)};
5. {(вершина #4, 37)}

Анализируя полученное дерево, расписываем, как будет кодироваться каждый из символов после сжатия:

a - "11"
b - "100"
c - "101"
d - "00"
f - "01".

В двоичном виде файл будет выглядеть так:

хххх{88 bit, end of file}

Сжатый файл занял 11 байт (последние четыре бита xxxx записываются произвольно, ведь весь файл на самом деле умещается в 84 бита, что не кратно восьми). Коэффициент сжатия при таком кодировании равен 3,5.

Руководство к программе

Запустите программу

Рисунок 1 Главное окно программы

На панели «Установки» можно выбрать какое действие произвести с данными (упаковать/распаковать). Также можно выбрать выводить или нет отчет о ходе процесса архивирования. Пути для открытия и сохранения устанавливаются нажатием соответствующей кнопки.

Рисунок 2 Выбор файла для архивирования и задание будущего архива

Если при упаковке файла в установках стояла галочка выводить отчет то отобразится следующее окно

Рисунок 3 Отчёт о процессе архивирования

При повторном нажатии кнопки Далее файл архивируется

Рисунок 4 Завершение процесса архивирования

Если галочка о выводе отчета не была установлена окно выполнения архивирования будет выглядеть следующим образом

Рисунок 5 Завершение процесса архивирования

Процесс разархивации происходит аналогично.

Рисунок 6 Завершение процесса разархивирования

Сравнение сжатия файлов

В результате работы с программой, при упаковке различных файлов с информацией были получены следующие данные размеров файлов.

Код программы

unit unit1;

interface

uses

Mpacklib, Listlib, Errlib, ExtCtrls, Dialogs, Gauges, ComCtrls, Buttons,

Grids, StdCtrls, Controls, Classes, Forms, SysUtils, Graphics, XPMan;

type

TForm1 = class(TForm)

OpenName: TEdit;

SaveName: TEdit;

OpenDialog: TOpenDialog;

SaveDialog: TSaveDialog;

OpenTxt: TLabel;

SaveTxt: TLabel;

PackButton: TRadioButton;

UnPackButton: TRadioButton;

ShowReport: TCheckBox;

SettingBox: TGroupBox;

StatusBar1: TStatusBar;

Gauge: TGauge;

Timer1: TTimer;

ReportBox: TGroupBox;

StringGrid1: TStringGrid;

Speed: TLabel;

Time: TLabel;

ProcessBox: TGroupBox;

ExitBtn: TBitBtn;

PrevBtn: TBitBtn;

NextBtn: TBitBtn;

CancelBtn: TBitBtn;

RepUnpackTxt: TLabel;

RepPackTxt: TLabel;

RepRatioTxt: TLabel;

RepUnpackVal: TLabel;

RepPackVal: TLabel;

RepRatioVal: TLabel;

RepHeaderTxt: TLabel;

RepEntropyTxt: TLabel;

RepEntropyVal: TLabel;

RepHeaderVal: TLabel;

OpenBtn: TSpeedButton;

SaveBtn: TSpeedButton;

Image1: TImage;

Image2: TImage;

Image3: TImage;

Bevel1: TBevel;

BitBtn1: TBitBtn;

SaveReport: TSpeedButton;

XPManifest1: TXPManifest;

Image4: TImage;

procedure OpenBtnClick(Sender: TObject);

procedure SaveBtnClick(Sender: TObject);

procedure FormCreate(Sender: TObject);

procedure Timer1Timer(Sender: TObject);

procedure FormClose(Sender: TObject; var Action: TCloseAction);

procedure PackButtonClick(Sender: TObject);

procedure UnPackButtonClick(Sender: TObject);

procedure SaveReportClick(Sender: TObject);

procedure NextBtnClick(Sender: TObject);

procedure ExitBtnClick(Sender: TObject);

procedure PrevBtnClick(Sender: TObject);

procedure CancelBtnClick(Sender: TObject);

procedure BitBtn1Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

type

Thread = class(TThread)

private

{ Private declarations }

protected

procedure Execute; override;

end;

type TPackFunc = function(si, so:string; genlist:boolean):boolean;

var

Form1:TForm1;

step:byte;

l:ptr;

PackFunc:TPackFunc;

PackFuncResult, NeedStepParser, RunProcess:boolean;

NThread:Thread;

MsgDlg:record

strng:string;

typ:TMsgDlgType;

enable:boolean;

end;

TecTime, Timer, Time1,Time2:TDateTime;

Do1,Do2:integer;

Title:string;

implementation

procedure Error(strng:string; typ:TMsgDlgType);

begin

MsgDlg. strng:=strng;

MsgDlg. typ:=typ;

MsgDlg. enable:=true;

end;

function CheckMask(name, ext:string):string;

begin

if pos('.',name)=0 then

CheckMask:=name+'.'+ext

else

CheckMask:=name;

end;

function GetSec(time:TDateTime):real;

var Hour, Min, Sec, MSec Word;

begin

DecodeTime(Time, Hour, Min, Sec, MSec);

GetSec:=Sec+MSec/1000;

end;

procedure TimerInit;

begin

Form1.Speed. Caption:='Скорость:'; Form1.Speed. visible:=true;

Form1.Time. Caption:='Время: 0:00:00 / 0:00:00'; Form1.Time. visible:=true;

Timer:=Now; TecTime:=Now;

Time1:=Timer; Time2:=Timer;

Do1:=0; Do2:=0;

end;

=*=

function Pack1(si, so:string; genlist:boolean):boolean;

var i, err, fsi, fsh:longint;

fso:currency;

root:PointRec;

h:extended;

p:ptr;

el:elementtype;

begin

Pack1:=false;

Title:='Построение дерева...';

if si='' then begin

Error('Путь для открытия файла неустановлен',mtWarning);

exit;

end;

{ инициализация словаря }

for i:=1 to 256 do begin

dict[i].n:=i-1;

dict[i].c:=0;

dict[i].b:='';

dict[i].f:=true;

end;

{ открытие входного файла }

assign(fi, si);

reset(fi,1);

err:=ioresult;

if err<>0 then begin

Error(si+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

{ анализ }

fsi:=filesize(fi);

if fsi=0 then begin

Error('файл '+si+' пустой',mtInformation);

exit;

end;

TimerInit;

RunProcess:=true;

while not eof(fi) do begin

if NThread. Terminated then exit;

i:=round(filepos(fi)/fsi*100);

Form1.Gauge. progress:=i;

Title:='('+inttostr(i)+'%) Построение дерева...';

TecTime:=now;

BlockRead(fi, Buffer, BuffSize, BuffSizeOk);

err:=ioresult;

if err<>0 then begin

Error(si+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

for i:=1 to BuffSizeOk do

inc(dict[buffer[i]+1].c);

end;

{ сортировка }

Sort(256,'c');

{ расчет кол-ва ненулевых эл-тов }

nd:=256;

while dict[nd].c=0 do dec(nd);

{ построение дерева }

nt:=0;

TreeBuild(root);

BitMapCompile(root,'');

Pack1:=true;

if not genlist then exit;

{ рапорт }

h:=0;

ListMakeNull(l);

for i:=1 to nd do begin

el. n:=i;

el. char:=dict[i].n;

el. count:=dict[i].c;

el. hi:=ln(fsi/dict[i].c)/ln(2);

h:=h+dict[i].c/fsi*el. hi;

el. bmp:=dict[i].b;

ListInsert(el, p,l);

end;

fso:=0;

for i:=1 to nd do

fso:=fso+dict[i].c*length(dict[i].b);

fso:=trunc((fso-1)/8+1);

if nd*(sizeof(dict[i].n)+sizeof(dict[i].c)) < 256*sizeof(dict[i].c) then

fsh:=nd*(sizeof(dict[i].n)+sizeof(dict[i].c))

else

fsh:=256*sizeof(dict[i].c);

fsh:=5+sizeof(fsi)+sizeof(nd)+fsh;

Form1.RepEntropyVal. Caption:=floattostrf(h, ffGeneral,8,8);

Form1.RepHeaderVal. Caption:=inttostr(fsh)+' байт';

Form1.RepUnpackVal. Caption:=inttostr(fsi)+' байт';

Form1.RepPackVal. Caption:=floattostr(fso)+' байт';

Form1.RepRatioVal. Caption:=floattostr(round(fso/fsi*10000)/100)+'%';

end;

=*=

function Pack2(si, so:string; genlist:boolean):boolean;

var i, j,err, fs, ab:longint;

abn:byte;

id:string[5] absolute Buffer;

begin

Pack2:=false;

Title:='Упаковка...';

if so='' then begin

Error('Путь для сохранения файла неустановлен',mtWarning);

exit;

end;

{ открытие выходного файла }

assign(fo, so);

rewrite(fo,1);

err:=ioresult;

if err<>0 then begin

Error(so+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

reset(fi,1);

fs:=filesize(fi);

{ заголовок }

id:='HAF'#26;

if nd*(sizeof(dict[i].n)+sizeof(dict[i].c)) < 256*sizeof(dict[i].c) then

id[5]:=#1

else

id[5]:=#2;

BlockWrite(fo, id[1],5);

BlockWrite(fo, fs, sizeof(fs));

BlockWrite(fo, nd, sizeof(nd));

if id[5]=#1 then begin

for i:=1 to nd do begin

BlockWrite(fo, dict[i].n, sizeof(dict[i].n));

BlockWrite(fo, dict[i].c, sizeof(dict[i].c));

end;

Sort(256,'n');

end

else begin

Sort(256,'n');

for i:=1 to 256 do

BlockWrite(fo, dict[i].c, sizeof(dict[i].c));

end;

{ сжатие }

abn:=0;

TimerInit;

RunProcess:=true;

if nd<>1 then while not eof(fi) do begin

if NThread. Terminated then exit;

i:=round(filepos(fi)/fs*100);

Form1.Gauge. progress:=i;

Title:='('+inttostr(i)+'%) Упаковка...';

TecTime:=now;

BlockRead(fi, Buffer, BuffSize, BuffSizeOk);

err:=ioresult;

if err<>0 then begin

Error(si+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

for i:=1 to BuffSizeOk do

for j:=1 to length(dict[Buffer[i]+1].b) do begin

if dict[Buffer[i]+1].b[j]='1' then

ab:=ab or (longint(1) shl abn)

else

ab:=ab and not (longint(1) shl abn);

inc(abn);

if abn=32 then begin

BlockWrite(fo, ab, sizeof(ab));

err:=ioresult;

if err<>0 then begin

Error(so+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

abn:=0;

end;

end;

end;

if abn>0 then begin

BlockWrite(fo, ab, trunc((abn-1)/8+1));

err:=ioresult;

if err<>0 then begin

Error(so+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

end;

{ close(fi);

close(fo);}

Pack2:=true;

end;

function UnPack(si, so:string; genlist:boolean):boolean;

var root, next:PointRec;

i, j,err, fs:integer;

id:string[5] absolute Buffer;

begin

UnPack:=false;

Title:='Распаковка...';

if (si='') or (so='') then begin

Error('Имя файла неустановлено',mtWarning);

exit;

end;

assign(fi, si);

reset(fi,1);

err:=ioresult;

if err<>0 then begin

Error(si+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

BlockRead(fi, id[1],5,BuffSizeOk); id[0]:=#5;

if (BuffSizeOk<>5) or ((id<>'HAF'#26#1) and (id<>'HAF'#26#2)) then begin

Error(si+' - Файл упакован неправильно',mtWarning);

exit;

end;

BlockRead(fi, fs, sizeof(fs));

BlockRead(fi, nd, sizeof(nd));

if id[5]=#1 then

for i:=1 to nd do begin

BlockRead(fi, dict[i].n, sizeof(dict[i].n));

BlockRead(fi, dict[i].c, sizeof(dict[i].c));

dict[i].f:=true;

end

else begin

for i:=1 to 256 do begin

dict[i].n:=i-1;

BlockRead(fi, dict[i].c, sizeof(dict[i].c));

dict[i].f:=true;

end;

Sort(256,'c');

end;

nt:=0;

TreeBuild(root);

assign(fo, so);

rewrite(fo,1);

err:=ioresult;

if err<>0 then begin

Error(so+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

if dict[1].c=fs then begin

FillChar(Buffer, BuffSize, dict[1].n);

while filepos(fo)<fs do begin

if NThread. Terminated then exit;

i:=round(filepos(fo)/fs*100);

Form1.Gauge. progress:=i;

Title:='('+inttostr(i)+'%) Распаковка...';

if filepos(fo)+BuffSize<fs then

BlockWrite(fo, Buffer, BuffSize)

else

BlockWrite(fo, Buffer, fs-filepos(fo));

err:=ioresult;

if err<>0 then begin

Error(so+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

end;

end

else begin

next:=root;

TimerInit;

RunProcess:=true;

while not eof(fi) do begin

if NThread. Terminated then exit;

BlockRead(fi, Buffer, BuffSize, BuffSizeOk);

err:=ioresult;

if err<>0 then begin

Error(si+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end;

i:=round(filepos(fo)/fs*100);

Form1.Gauge. progress:=i;

Title:='('+inttostr(i)+'%) Распаковка...';

for i:=1 to BuffSizeOk do

for j:=0 to 7 do begin

if ((Buffer[i] shr j) and 1) <> 0 then next:=tree[next. i].l else next:=tree[next. i].r;

if next. d='d' then begin

if filepos(fo)<fs then begin

TecTime:=now;

BlockWrite(fo, dict[next. i].n, sizeof(dict[next. i].n));

err:=ioresult;

if err<>0 then begin

Error(so+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError);

exit;

end

end

else

break;

next:=root;

end;

end;

end;

end;

UnPack:=true;

end;

procedure StepParser;

var p:ptr;

y:integer;

begin

case step of

1: begin {initiate}

Form1.OpenName. Enabled:=true;

Form1.SaveName. Enabled:=true;

Form1.OpenBtn. Enabled:=true;

Form1.SaveBtn. Enabled:=true;

Form1.PackButton. Enabled:=true;

Form1.UnPackButton. Enabled:=true;

if Form1.PackButton. Checked then

Form1.ShowReport. Enabled:=true

else

Form1.ShowReport. Enabled:=false;

Title:='Сжатие данных методом Хаффмана';

Form1.Speed. Visible:=false;

Form1.Time. Visible:=false;

Form1.SaveReport. Visible:=false;

Form1.Gauge. Visible:=false;

Form1.PrevBtn. Enabled:=false;

Form1.NextBtn. Enabled:=true;

Form1.NextBtn. Visible:=true;

Form1.CancelBtn. Visible:=false;

Form1.ExitBtn. Enabled:=true;

Form1.SettingBox. Visible:=true;

Form1.ReportBox. Visible:=false;

end;

2: begin {pack1/unpack}

Form1.OpenName. Enabled:=false;

Form1.SaveName. Enabled:=false;

Form1.OpenBtn. Enabled:=false;

Form1.SaveBtn. Enabled:=false;

Form1.PackButton. Enabled:=false;

Form1.UnPackButton. Enabled:=false;

Form1.ShowReport. Enabled:=false;

Form1.Gauge. Progress:=0;

Form1.Gauge. Visible:=true;

Form1.PrevBtn. Enabled:=false;

Form1.CancelBtn. Enabled:=true;

Form1.CancelBtn. Visible:=true;

Form1.NextBtn. Visible:=false;

Form1.ExitBtn. Enabled:=false;

if Form1.PackButton. Checked then begin

PackFunc:=Pack1;

NThread:=Thread. Create(false);

end

else begin

PackFunc:=UnPack;

NThread:=Thread. Create(false);

end;

end;

3: begin {end}

if PackFuncResult then begin

Title:='Выполнено';

Form1.Gauge. Progress:=100;

end

else begin

Title:='Не законцено!';

end;

Form1.PrevBtn. Enabled:=true;

Form1.NextBtn. Enabled:=false;

Form1.CancelBtn. Enabled:=false;

Form1.ExitBtn. Enabled:=true;

end;

4: begin {report}

Form1.Gauge. Progress:=100;

Form1.PrevBtn. Enabled:=true;

Form1.NextBtn. Enabled:=true;

Form1.NextBtn. Visible:=true;

Form1.CancelBtn. Visible:=false;

Form1.ExitBtn. Enabled:=true;

Title:='Просмотр отчёта';

p:=l; y:=1;

Form1.StringGrid1.cells[0,y-1]:='№';

Form1.StringGrid1.cells[1,y-1]:='символ';

Form1.StringGrid1.cells[2,y-1]:='счёт';

Form1.StringGrid1.cells[3,y-1]:='высота';

Form1.StringGrid1.cells[4,y-1]:='набор бит';

while p<>nil do begin

inc(y);

Form1.StringGrid1.cells[0,y-1]:=inttostr(p^.element. n);

Form1.StringGrid1.cells[1,y-1]:=inttohex(p^.element. char,2)+'('+inttostr(p^.element. char)+')';

Form1.StringGrid1.cells[2,y-1]:=inttostr(p^.element. count);

Form1.StringGrid1.cells[3,y-1]:=floattostr(p^.element. hi);

Form1.StringGrid1.cells[4,y-1]:=p^.element. bmp;

p:=p^.next;

end;

Form1.StringGrid1.RowCount:=y;

Form1.ReportBox. Visible:=true;

Form1.SettingBox. Visible:=false;

Form1.SaveReport. Visible:=true;

end;

5: begin {pack2}

Form1.Gauge. Progress:=0;

Form1.PrevBtn. Enabled:=false;

Form1.CancelBtn. Enabled:=true;

Form1.CancelBtn. Visible:=true;

Form1.NextBtn. Visible:=false;

Form1.ExitBtn. Enabled:=false;

PackFunc:=Pack2;

NThread:=Thread. Create(false);

end;

end;

end;

procedure Thread. Execute;

begin

FreeOnTerminate:=true;

PackFuncResult:=PackFunc(Form1.openname. text, Form1.savename. text, Form1.ShowReport. Checked);

RunProcess:=false;

close(fi);

close(fo);

if not PackFuncResult then erase(fo);

if Form1.PackButton. Checked and (step=2) and PackFuncResult then

if Form1.ShowReport. Checked then

step:=4

else

step:=5

else

step:=3;

NeedStepParser:=true;

end;

procedure TForm1.OpenBtnClick(Sender: TObject);

begin

if OpenDialog. Execute then

if OpenDialog. FilterIndex=1 then

openname. text:=CheckMask(OpenDialog. FileName,'haf')

else

openname. text:=OpenDialog. FileName;

end;

procedure TForm1.SaveBtnClick(Sender: TObject);

begin

if SaveDialog. Execute then

if SaveDialog. FilterIndex=1 then

savename. text:=CheckMask(SaveDialog. FileName,'haf')

else

savename. text:=SaveDialog. FileName;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

step:=1;

l:=nil;

NeedStepParser:=true;

MsgDlg. enable:=false;

FileMode:=fmOpenRead+fmShareDenyWrite;

end;

procedure TForm1.NextBtnClick(Sender: TObject);

begin

Image4.Visible:=False;

inc(step);

NeedStepParser:=false;

StepParser;

end;

procedure TForm1.Timer1Timer(Sender: TObject);

begin

if NeedStepParser then begin

NeedStepParser:=false;

StepParser;

end;

if MsgDlg. enable then begin

MsgDlg. enable:=false;

MessageDlg(MsgDlg. strng, MsgDlg. typ, [mbOk], 0);

end;

if RunProcess then begin

if GetSec(TecTime-timer)>0 then

Form1.Time. Caption:='Время: '+timetostr(TecTime-Timer)+' / '

+timetostr(TecTime-Timer+(filesize(fi)-filepos(fi))/(filepos(fi)/(TecTime-Timer)));

if Time2=Timer then begin

if GetSec(TecTime-Timer)>10 then begin

Time2:=TecTime; Do2:=filepos(fi);

end;

if Time1=Timer then

if GetSec(TecTime-Timer)>5 then begin

Time1:=TecTime; Do1:=filepos(fi);

end;

end

else

if GetSec(TecTime-Time2)>15 then begin

Time2:=Time1; Do2:=Do1;

Time1:=TecTime; Do1:=filepos(fi);

end;

if GetSec(TecTime-Time2)<>0 then

Form1.Speed. Caption:='Скорость: '+inttostr(round((filepos(fi)-Do2)/GetSec(TecTime-Time2)/1024))+' kb/s';

end;

Form1.Caption:=Title;

Application. Title:=Title;

end;

procedure TForm1.ExitBtnClick(Sender: TObject);

begin

close;

end;

procedure TForm1.PrevBtnClick(Sender: TObject);

begin

Image4.Visible:=True;

step:=1;

NeedStepParser:=false;

StepParser;

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

ListMakeNull(l);

end;

procedure TForm1.PackButtonClick(Sender: TObject);

begin

OpenDialog. FilterIndex:=2;

SaveDialog. FilterIndex:=1;

ShowReport. Enabled:=true;

end;

procedure TForm1.UnPackButtonClick(Sender: TObject);

begin

OpenDialog. FilterIndex:=1;

SaveDialog. FilterIndex:=2;

ShowReport. Enabled:=false;

end;

procedure TForm1.SaveReportClick(Sender: TObject);

var i, j,err:integer;

fname:string;

f:textfile;

begin

SaveDialog. Filter:='HTML документ|*.htm*';

if SaveDialog. Execute then begin

fname:=CheckMask(SaveDialog. FileName,'htm');

assignfile(f, fname);

rewrite(f);

err:=ioresult;

if err<>0 then

error(fname+' - '+ErrStr(err)+' ('+inttostr(err)+')',mtError)

else begin

writeln(f,'<HTML>');

writeln(f,'<HEAD>');

writeln(f,'<TITLE>Хаффман отчёт</TITLE>');

writeln(f,'</HEAD>');

writeln(f,'<BODY>');

writeln(f,'<TABLE BORDER>');

for i:=0 to StringGrid1.RowCount-1 do begin

write(f,'<TR>');

for j:=0 to StringGrid1.ColCount-1 do

write(f,'<TD>'+StringGrid1.cells[j, i]+'</TD>');

writeln(f,'</TR>');

end;

writeln(f,'</TABLE>');

writeln(f, Form1.RepEntropyTxt. Caption+#32+Form1.RepEntropyVal. Caption+'<br>');

writeln(f, Form1.RepUnpackTxt. Caption+#32+Form1.RepUnpackVal. Caption+'<br>');

writeln(f, Form1.RepPackTxt. Caption+#32+Form1.RepPackVal. Caption+'<br>');

writeln(f, Form1.RepRatioTxt. Caption+#32+Form1.RepRatioVal. Caption+'<br>');

writeln(f, Form1.RepHeaderTxt. Caption+#32+Form1.RepHeaderVal. Caption+'<br>');

writeln(f,'</BODY>');

writeln(f,'</HTML>');

closefile(f);

end;

end;

SaveDialog. Filter:='{Хаффман файлы|*.haf|Все файлы|*.*';

end;

procedure TForm1.CancelBtnClick(Sender: TObject);

begin

NThread. Terminate;

inc(step);

NeedStepParser:=false;

StepParser;

end;

procedure TForm1.BitBtn1Click(Sender: TObject);

begin

MessageDlg('АРХИВАТОР'+#13+'Сжимает данные по методу Хаффмана.'+#13+'Автор: '+#13+' гр. ИТЗ26010дт '+#13+'УГТУ-УПИ 2008г', mtInformation, [mbOk], 0);

end;

end.

Вывод

В результате проделанной работы было изучено большое количество литературы, рассмотрены несколько алгоритмов сжатия. Написана программа для сжатия/распаковки данных по алгоритму Хаффмана. Программа была протестирована на компьютерах с не установленной на них средой Delphi, и успешно работала. Был получен достаточно высокий метод сжатия.

Алгоритм основан на том факте, что некоторые символы из стандартного 256-символьного набора в произвольном тексте могут встречаться чаще среднего периода повтора, а другие, соответственно, – реже. Следовательно, если для записи распространенных символов использовать короткие последовательности бит, длиной меньше 8, а для записи редких символов – длинные, то суммарный объем файла уменьшится.

Хаффман предложил очень простой алгоритм определения того, какой символ необходимо кодировать каким кодом для получения файла с длиной, очень близкой к его энтропии (то есть информационной насыщенности).

В теории кодирования информации показывается, что код Хаффмана является префиксным, то есть код никакого символа не является началом кода какого-либо другого символа. А из этого следует, что код Хаффмана однозначно восстановим получателем, даже если не сообщается длина кода каждого переданного символа. Получателю пересылают только дерево Хаффмана в компактном виде, а затем входная последовательность кодов символов декодируется им самостоятельно без какой-либо дополнительной информации.

«С 1952 года алгоритм Хаффмана стал базой для огромного количества дальнейших исследований в этой области. По сей день в компьютерных журналах можно найти большое количество публикаций, посвященных различным реализациям данного алгоритма и поискам его лучшего применения. Кодирование Хаффмана используется в коммерческих программах сжатия (например, в PKZIP и LHA), встроено в некоторые телефаксы, используется в алгоритме JPEG сжатия графических изображений с потерями.»

Литература:

1.  А. Хомоненко «Delphi 7» СПб.: 2003г

2.  Д. Мастрюков «Алгоритмы сжатия информации»

3.  Д. Арапов «Пишем упаковщик»

4.  Журнал «Монитор» статья «Кодирование Хаффмана», номер 7-8, 1993г

5.  http://*****/compress/ Всё о сжатии

6.  http://algolist. *****/ Общие алгоритмы сжатия и кодирования

7.  http://piterustinoff. *****/ Архиватор собственными руками