Арифметическое кодирование — различия между версиями
Migan (обсуждение | вклад) м (→Псевдокод) |
Migan (обсуждение | вклад) м (→Псевдокод) |
||
Строка 13: | Строка 13: | ||
=== Псевдокод === | === Псевдокод === | ||
+ | *<math>\mathtt{s}\,</math> {{---}} текст, подаваемый на вход; | ||
*<math>\mathtt{n}\,</math> {{---}} мощность алфавита исходного текста; | *<math>\mathtt{n}\,</math> {{---}} мощность алфавита исходного текста; | ||
*<math>\mathtt{letters[n]}\,</math> {{---}} массив символов, составляющих алфавит исходного текста; | *<math>\mathtt{letters[n]}\,</math> {{---}} массив символов, составляющих алфавит исходного текста; | ||
Строка 18: | Строка 19: | ||
*<math>\mathtt{segment}\,</math> {{---}} структура, задающая подотрезок отрезка <tex>[0; 1)</tex>, соответствующего конкретному символу на основе частотного анализа. Имеет поля: | *<math>\mathtt{segment}\,</math> {{---}} структура, задающая подотрезок отрезка <tex>[0; 1)</tex>, соответствующего конкретному символу на основе частотного анализа. Имеет поля: | ||
**<math>\mathtt{left}\,</math> {{---}} левая граница подотрезка; | **<math>\mathtt{left}\,</math> {{---}} левая граница подотрезка; | ||
− | **<math>\mathtt{right}\,</math> {{---}} правая граница подотрезка. | + | **<math>\mathtt{right}\,</math> {{---}} правая граница подотрезка; |
+ | *<math>\mathtt{left}\,</math>, <math>\mathtt{right}\,</math> {{---}} границы отрезка, содержащего возможный результат арифметического кодирования. | ||
<code> | <code> | ||
Строка 25: | Строка 27: | ||
'''double''' right | '''double''' right | ||
} | } | ||
+ | |||
'''void''' DefineSegments(letters: '''char'''[n], probability: '''double'''[n]) | '''void''' DefineSegments(letters: '''char'''[n], probability: '''double'''[n]) | ||
'''double''' l = 0 | '''double''' l = 0 | ||
Строка 30: | Строка 33: | ||
segment[letters[i]].left = l | segment[letters[i]].left = l | ||
segment[letters[i]].right = l + probability[i] | segment[letters[i]].right = l + probability[i] | ||
− | |||
− | |||
− | |||
− | |||
'''double''' ArithmeticCoding(s: '''string'''): | '''double''' ArithmeticCoding(s: '''string'''): | ||
Строка 45: | Строка 44: | ||
right = newRight | right = newRight | ||
'''return''' (left + right) / 2 | '''return''' (left + right) / 2 | ||
+ | </code> | ||
'''Замечание:''' для оптимизации размера кода можно выбрать из полученного на последнем шаге диапазона <tex>[left; right]</tex> число, содержащее наименьшее количество знаков в двоичной записи. | '''Замечание:''' для оптимизации размера кода можно выбрать из полученного на последнем шаге диапазона <tex>[left; right]</tex> число, содержащее наименьшее количество знаков в двоичной записи. |
Версия 18:53, 17 июня 2016
Арифметическое кодирование (англ. Arithmetic coding) — алгоритм сжатия информации без потерь, который при кодировании ставит в соответствие тексту вещественное число из отрезка алгоритм Хаффмана, является энтропийным, т.е. длина кода конкретного символа зависит от частоты встречаемости этого символа в тексте. Арифметическое кодирование показывает более высокие результаты сжатия, чем алгоритм Хаффмана, для данных с неравномерными распределениями вероятностей кодируемых символов. Кроме того, при арифметическом кодировании каждый символ кодируется нецелым числом бит, что эффективнее кода Хаффмана (теоретически, символу с вероятностью появления допустимо ставить в соответствие код длины , следовательно, при кодировании алгоритмом Хаффмана это достигается только с вероятностями, равными обратным степеням двойки).
. Данный метод, как иСодержание
Принцип действия
Кодирование
На вход алгоритму передаются текст для кодирования и список частот встречаемости символов.
- Рассмотрим отрезок на координатной прямой.
- Поставим каждому символу текста в соответствие отрезок, длина которого равна частоте его появления.
- Считаем символ из входного потока и рассмотрим отрезок, соответствующий этому символу. Разделим этот отрезок на части, пропорциональные частотам встречаемости символов.
- Повторим пункт (3) до конца входного потока.
- Выберем любое число из получившегося отрезка, которое и будет результатом арифметического кодирования.
Псевдокод
- — текст, подаваемый на вход;
- — мощность алфавита исходного текста;
- — массив символов, составляющих алфавит исходного текста;
- — массив вероятностей обнаружения символа в тексте;
- — левая граница подотрезка;
- — правая граница подотрезка;
— структура, задающая подотрезок отрезка , соответствующего конкретному символу на основе частотного анализа. Имеет поля:
- , — границы отрезка, содержащего возможный результат арифметического кодирования.
struct segment { double left double right }
void DefineSegments(letters: char[n], probability: double[n]) double l = 0 for i = 1 to n segment[letters[i]].left = l segment[letters[i]].right = l + probability[i]
double ArithmeticCoding(s: string): double left = 0 double right = 1 for i = 0 to length(s)-1 char symb = s[i] double newRight = left + (right - left) * segment[symb].right double newLeft = left + (right - left) * segment[symb].left left = newLeft right = newRight return (left + right) / 2
Замечание: для оптимизации размера кода можно выбрать из полученного на последнем шаге диапазона
число, содержащее наименьшее количество знаков в двоичной записи.Декодирование
Алгоритм по вещественному числу восстанавливает исходный текст.
- Выберем на отрезке , разделенном на части, длины которых равны вероятностям появления символов в тексте, подотрезок, содержащий входное вещественное число. Символ, соответствующий этому подотрезку, дописываем в ответ.
- Нормируем подотрезок и вещественное число.
- Повторим пункты 1—2 до тех пор, пока не получим ответ.
Псевдокод
- — вещественное число, подаваемое на вход;
- — длина восстанавливаемого текста;
- — левая граница подотрезка;
- — правая граница подотрезка;
- — значение символа.
— структура, задающая подотрезок отрезка , соответствующего конкретному символу на основе частотного анализа. Имеет поля:
string ArithmeticDecoding(code: double): string s = "" for i = 1 to length for j = 1 to n if code >= segment[j].left and code < segment[j].right s = s + segment[j].character code = (code – segment[j].left) / (segment[j].right – segment[j].left) break return s
Замечание: кодировщику и декодировщику должно быть известно, когда завершать работу. Для этого можно передавать в качестве аргумента длину текста или символ конца файла, после которого процесс должен быть остановлен.
Пример работы
Рассмотрим в качестве примера строку
:Кодирование
Символ | Частота появления |
---|---|
| |
| |
|
Считанный символ | Левая граница отрезка | Правая граница отрезка |
---|---|---|
| ||
| ||
| ||
| ||
| ||
| ||
| ||
|
Код:
Декодирование
Код:
Декодируемый символ | Код |
---|---|
| |
| |
| |
| |
| |
| |
|
Замечание: при декодировании текста можно не только нормализовывать рабочий отрезок и текущий код, но и уменьшать рабочий отрезок (аналогично кодированию), не изменяя значение кода.
Декодирование (второй способ)
Код:
Декодируемый символ | Границы отрезка | |||
---|---|---|---|---|
| ||||
| ||||
| ||||
| ||||
| ||||
| ||||
|
Оценка длины кодового слова
Теорема: |
При арифметическом кодировании длина кодового слова не превышает энтропии исходного текста. |
Доказательство: |
Введём следующие обозначения: — длина текста; — размер алфавита; — частота встречаемости символа; — вероятность вхождения символа;Размер сообщения Число бит в закодированном тексте: можно найти по формуле: |