Давайте розглянемо дещо інший спосіб мислення про кодування Хаффмана.
Припустимо, у вас є алфавіт із трьох символів, 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 являє собою діапазон і символ C являє собою діапазон .[04,24)[24,34)[34,44)
В основному те, що ми робимо тут, - це множення всього на спільний знаменник. Уявіть, що стан насправді був у базі 4. Кодування символу B справді виводить цифру 2 у цій базі, а кодування символу C - виводить цифру 3 у цій базі.
Однак символ A трохи інший, тому що це не ціла цифра в базі 4.
Натомість ми можемо розглядати алфавіт як набір символів A_0, A_1, B, C з однаковою ймовірністю. Знову ж таки, оптимальний код Хаффмана 00, 01, 10, 11. Або, знову ж таки, ми можемо подумати про це в базі 4. Для кодування символу просто робимо:
encode(s,A0)encode(s,A1)encode(s,B)encode(s,C)=4s+0=4s+1=4s+2=4s+3
Отже, тепер зрозуміло, як кодувати символи B і C, але кодувати символ A, у нас є вибір. Які з та ми повинні використовувати?A 1A0A1
Тепер ось розумна думка: ми крадемо один біт інформації зі стану :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 беремо і , а потім .s′=⌊s3⌋i=smod3encode(s′,Ai)
Це еквівалентно арифметичному кодуванню. Це насправді сімейство методів, відомих як Асиметричні чисельні системи , і було розроблено протягом останніх кількох років Яреком Дудою. Значення імені повинно бути очевидним: щоб кодувати символ з вірогідністю , ви концептуально вкрали цифру базового p із стану, а потім додали цифру базового q. Асиметрія походить від трактування стану як цифри у двох різних основах.pq
Причина, чому це сімейство методів кодування, полягає в тому, що те, що ми бачили тут, непрактично саме по собі; йому потрібні деякі модифікації для вирішення того факту, що ви, мабуть, не маєте безмежно точних цілих чисел для ефективного маніпулювання змінною стану, і є різні способи, яким ви можете цього досягти. Арифметичне кодування, звичайно, має аналогічне питання з точністю до свого стану.
Практичні варіанти включають rANS ("r" означає "співвідношення") і tANS ("керований таблицею").
ANS має кілька цікавих переваг перед арифметичним кодуванням, як практичним, так і теоретичним:
- На відміну від арифметичного кодування, "стан" - це одне слово, а не пара слів.
- Мало того, що кодер ANS та його відповідний декодер мають однакові стани, і їх операції є абсолютно симетричними. Це викликає деякі цікаві можливості, наприклад, ви можете переплутати різні потоки закодованих символів і все ідеально синхронізується.
- Звичайно, для практичних реалізацій потрібно "виводити" інформацію, а не просто збирати її у велике ціле число, яке потрібно записати наприкінці. Однак розмір "виводу" можна налаштувати взамін на (як правило, скромні) втрати на стиснення. Тож, коли арифметичні кодери повинні виводити трохи за один раз, ANS може виводити байт або nybble за один раз. Це дає вам прямий компроміс між швидкістю та стисненням.
- Здається, що на апаратному забезпеченні поточного покоління воно настільки ж швидко, як і двійкове арифметичне кодування, і, отже, є конкурентоспроможною кодування Хаффмана. Це робить його набагато швидшим, ніж арифметичне кодування великого алфавіту та його варіанти (наприклад, кодування діапазону).
- Це, мабуть, не є патентом.
Я не думаю, що коли-небудь знову буду робити арифметичне кодування.