Давайте посмотрим на немного другой способ мышления о кодировании Хаффмана.
Предположим, у вас есть алфавит из трех символов, A, B и C, с вероятностями 0,5, 0,25 и 0,25. Поскольку вероятности - все обратные степени двойки, у этого есть код Хаффмана, который является оптимальным (то есть это идентично арифметическому кодированию). Мы будем использовать канонический код 0, 10, 11 для этого примера.
Предположим, что наше состояние - большое целое число, которое мы будем называть . Вы можете думать о кодировании как о функции, которая принимает текущее состояние и символ для кодирования и возвращает новое состояние:s
encode(s,A)encode(s,B)encode(s,C)=2s=4s+2=4s+3
Итак, давайте начнем с состояния 11 (которое 1011 в двоичном виде), закодируем символ B. Новое состояние 46, что в 101110 двоично. Как видите, это «старое» состояние с добавленной в конец последовательностью 10. По сути, мы «вывели» битовую последовательность 10.
Все идет нормально.
Теперь немного подумайте о том, как работает арифметическое кодирование. Если вы положите вероятности над общим знаменателем, символ A фактически представляет диапазон символ B представляет диапазон[2[ 04, 24)и символ C представляет диапазон[3[ 24, 34).[ 34, 44)
По сути, мы здесь умножаем все на общий знаменатель. Представьте, что состояние фактически было в базе 4. Кодирование символа B действительно выводит цифру 2 в этой базе, а кодирование символа C выводит цифру 3 в этой базе.
Тем не менее, символ A немного отличается, потому что это не целая цифра в базе 4.
Вместо этого мы можем думать об алфавите как о множестве символов A_0, A_1, B, C с равной вероятностью. Это, опять же, имеет оптимальный код Хаффмана 00, 01, 10, 11. Или, опять же, мы можем думать об этом в базе 4. Чтобы кодировать символ, мы просто делаем:
кодировать (s, A0)кодировать (s, A1)кодировать (s,B)кодировать (s,C)= 4 с + 0= 4 с + 1= 4 с + 2= 4 с + 3
A0A1
s
s′=⌊s2⌋
i=smod2
а затем .encode(s′,Ai)
Используя наш предыдущий пример, , мы находим, что и , а затем . Новое состояние - 10101 в двоичном виде.s=11s′=5i=1encode(5,A1)=4×5+1=21
Теперь это не дает точно такой же битовый вывод, как при кодировании Хаффмана, но он генерирует вывод такой же длины. И я надеюсь, что вы можете видеть, что это также уникально декодируемый. Чтобы декодировать символ, мы берем остаток от деления на 4. Если значение равно 2 или 3, то символ B или C соответственно. Если это 0 или 1, то символом является A, а затем мы можем вернуть бит информации, умножив состояние на 2 и добавив либо 0, либо 1.
Приятной особенностью этого подхода является то, что он естественным образом распространяется на дробно-битовое кодирование, когда числитель и / или знаменатель вероятностей не являются степенями двух. Предположим, у нас есть два символа, A и B, где вероятность A равна а вероятность B равна . Тогда мы можем закодировать символ с помощью:3525
encode(s,A0)encode(s,A1)encode(s,A2)encode(s,B0)encode(s,B1)=5s+0=5s+1=5s+2=5s+3=5s+4
Для кодирования символа A мы берем и , а затем .i=smod3кодировать(s′,Ai)s′=⌊s3⌋i=smod3encode(s′,Ai)
Это эквивалентно арифметическому кодированию. На самом деле это семейство методов, известных как асимметричные системы счисления , и было разработано за последние несколько лет Яреком Дудой. Значение имени должно быть очевидным: чтобы кодировать символ с вероятностью , вы концептуально крадете цифру base-p из состояния, а затем добавляете цифру base-q. Асимметрия возникает из-за интерпретации состояния как числа в двух разных основах.pq
Причина, по которой это семейство методов кодирования, заключается в том, что то, что мы видели здесь, само по себе нецелесообразно; необходимо несколько модификаций, чтобы учесть тот факт, что у вас, вероятно, нет целых чисел с бесконечной точностью, чтобы эффективно манипулировать переменной состояния, и существуют различные способы, которыми вы можете достичь этого. Конечно, у арифметического кодирования есть аналогичная проблема с точностью для его состояния.
Практические варианты включают RANS («r» означает «соотношение») и tANS («управляемый таблицей»).
У ANS есть несколько интересных преимуществ перед арифметическим кодированием, как практическим, так и теоретическим:
- В отличие от арифметического кодирования, «состояние» - это одно слово, а не пара слов.
- Кроме того, кодер ANS и соответствующий ему декодер имеют идентичные состояния, и их операции полностью симметричны. Это открывает некоторые интересные возможности, такие как то, что вы можете чередовать различные потоки закодированных символов, и все синхронизируется идеально.
- Практические реализации, конечно, должны «выводить» информацию по ходу, а не просто собирать ее в большое целое число, которое будет записано в конце. Тем не менее, размер «выхода» может быть настроен взамен (обычно скромных) потерь на сжатие. Таким образом, когда арифметические кодеры должны выводить бит за раз, ANS может выводить байт или nybble за один раз. Это дает вам прямой компромисс между скоростью и сжатием.
- Похоже, что он примерно такой же быстрый на оборудовании текущего поколения, как двоичное арифметическое кодирование, и, следовательно, конкурентоспособен с кодированием Хаффмана. Это делает его намного быстрее, чем арифметическое кодирование с большим алфавитом и его варианты (например, кодирование по дальности).
- Похоже, без патента.
Я не думаю, что когда-нибудь снова буду заниматься арифметическим кодированием.