Арифметическое кодирование — различия между версиями
Mervap (обсуждение | вклад) м |
Mervap (обсуждение | вклад) м (→Псевдокод) |
||
| Строка 27: | Строка 27: | ||
*<math>\mathtt{left}\,</math>, <math>\mathtt{right}\,</math> {{---}} границы отрезка, содержащего возможный результат арифметического кодирования. | *<math>\mathtt{left}\,</math>, <math>\mathtt{right}\,</math> {{---}} границы отрезка, содержащего возможный результат арифметического кодирования. | ||
| − | + | ||
'''struct''' Segment: | '''struct''' Segment: | ||
'''double''' left | '''double''' left | ||
| Строка 52: | Строка 52: | ||
right = newRight | right = newRight | ||
'''return''' (left + right) / 2 | '''return''' (left + right) / 2 | ||
| − | |||
'''Замечание:''' для оптимизации размера кода можно выбрать из полученного на последнем шаге диапазона <tex>[left; right]</tex> число, содержащее наименьшее количество знаков в двоичной записи. | '''Замечание:''' для оптимизации размера кода можно выбрать из полученного на последнем шаге диапазона <tex>[left; right]</tex> число, содержащее наименьшее количество знаков в двоичной записи. | ||
Версия 23:27, 4 марта 2018
| Определение: |
| Арифметическое кодирование (англ. Arithmetic coding) — алгоритм сжатия информации без потерь, который при кодировании ставит в соответствие тексту вещественное число из отрезка . Данный метод, как и алгоритм Хаффмана, является энтропийным, т.е. длина кода конкретного символа зависит от частоты встречаемости этого символа в тексте. Арифметическое кодирование показывает более высокие результаты сжатия, чем алгоритм Хаффмана, для данных с неравномерными распределениями вероятностей кодируемых символов. |
Содержание
Принцип действия
При арифметическом кодировании каждый символ кодируется нецелым числом бит, что эффективнее кода Хаффмана (теоретически, символу с вероятностью появления допустимо ставить в соответствие код длины , следовательно, при кодировании алгоритмом Хаффмана это достигается только с вероятностями, равными обратным степеням двойки).
Кодирование
На вход алгоритму передаются текст для кодирования и список частот встречаемости символов.
- Рассмотрим отрезок на координатной прямой.
- Поставим каждому символу текста в соответствие отрезок, длина которого равна частоте его появления.
- Считаем символ из входного потока и рассмотрим отрезок, соответствующий этому символу. Разделим этот отрезок на части, пропорциональные частотам встречаемости символов.
- Повторим пункт до конца входного потока.
- Выберем любое число из получившегося отрезка, которое и будет результатом арифметического кодирования.
Псевдокод
- — текст, подаваемый на вход;
- — длина исходного текста;
- — мощность алфавита исходного текста;
- — массив символов, составляющих алфавит исходного текста;
- — массив вероятностей обнаружения символа в тексте;
- — структура, задающая подотрезок отрезка , соответствующего конкретному символу на основе частотного анализа. Имеет поля:
- — левая граница подотрезка;
- — правая граница подотрезка;
- , — границы отрезка, содержащего возможный результат арифметического кодирования.
struct Segment:
double left
double right
Segment[m] defineSegments(letters: char[m], probability: double[m]):
Segment[m] segment
double l = 0
for i = 0 to m - 1
segment[letters[i]].left = l
segment[letters[i]].right = l + probability[i]
l = segment[letters[i]].right
return segment
double arithmeticCoding(letters: char[m], probability: double[m], s: char[n]):
Segment[m] segment = defineSegments(letters, probability)
double left = 0
double right = 1
for i = 0 to n - 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
Замечание: для оптимизации размера кода можно выбрать из полученного на последнем шаге диапазона число, содержащее наименьшее количество знаков в двоичной записи.
Декодирование
Алгоритм по вещественному числу восстанавливает исходный текст.
- Выберем на отрезке , разделенном на части, длины которых равны вероятностям появления символов в тексте, подотрезок, содержащий входное вещественное число. Символ, соответствующий этому подотрезку, дописываем в ответ.
- Нормируем подотрезок и вещественное число.
- Повторим пункты — до тех пор, пока не получим ответ.
Псевдокод
- — вещественное число, подаваемое на вход;
- — длина восстанавливаемого текста;
- — мощность алфавита исходного текста;
- — массив символов, составляющих алфавит исходного текста;
- — массив вероятностей обнаружения символа в тексте;
- — структура, задающая подотрезок отрезка , соответствующего конкретному символу на основе частотного анализа. Имеет поля:
- — левая граница подотрезка;
- — правая граница подотрезка;
- — значение символа.
struct Segment:
double left
double right
char character
Segment[m] defineSegments(letters: char[n], probability: double[n]):
Segment[m] segment
double l = 0
for i = 0 to m - 1
segment[i].left = l
segment[i].right = l + probability[i]
segment[i].character = letters[i]
l = segment[i].right
return segment
string arithmeticDecoding(letters: char[m], probability: double[m], code: double, n: int):
Segment[m] segment = defineSegments(letters, probability)
string s = ""
for i = 0 to n - 1
for j = 0 to m - 1
if code >= segment[j].left and code < segment[j].right
s += segment[j].character
code = (code – segment[j].left) / (segment[j].right – segment[j].left)
break
return s
Замечание: кодировщику и декодировщику должно быть известно, когда завершать работу. Для этого можно передавать в качестве аргумента длину текста или символ конца файла, после которого процесс должен быть остановлен.
Замечание: Несмотря на преимущества арифметического кодирования, существует проблема при его практическом применении из-за несовершенства представления чисел с плавающей точкой в памяти компьютера — поскольку некоторые дробные числа не могут быть точно представлены в двоичном коде, используемом современными процессорами (например, ), границы символов будут округлены, что может повлечь за собой неверную работу алгоритма при больших объёмах данных. В общем случае, алгоритм можно модифицировать так, чтобы результатом было дробное число. В такой реализации вероятность встречи символа представляется в виде рационального числа. Поскольку в каждой итерации будет переход из текущего отрезка в один из его подотрезков, кратных по длине , а всего итераций , в конечном результате знаменатель дроби не превысит , а поскольку сумма всех вероятностей встречи символов равна , полученная дробь будет находиться в промежутке .
Пример работы
Рассмотрим в качестве примера строку :
Кодирование
| Символ | Частота появления |
|---|---|
| |
| |
|
| Считанный символ | Левая граница отрезка | Правая граница отрезка |
|---|---|---|
| ||
| ||
| ||
| ||
| ||
| ||
| ||
|
Код:
Декодирование
Код:
| Декодируемый символ | Код |
|---|---|
| |
| |
| |
| |
| |
| |
|
Замечание: при декодировании текста можно не только нормализовывать рабочий отрезок и текущий код, но и уменьшать рабочий отрезок (аналогично кодированию), не изменяя значение кода.
Декодирование (второй способ)
Код:
| Декодируемый символ | Границы отрезка | |||
|---|---|---|---|---|
| ||||
| ||||
| ||||
| ||||
| ||||
| ||||
| ||||
Оценка длины кодового слова
| Теорема: |
При арифметическом кодировании длина кодового слова не превышает энтропии исходного текста. |
| Доказательство: |
|
Введём следующие обозначения:
Размер сообщения можно найти по формуле: Число бит в закодированном тексте: |