Потоковое кодирование

Недостатки посимвольного кодирования

  • не допускает адаптации к контексту (вероятность “шы” крайне мала, но посимвольно это никак не учесть)
  • кодирование диграмм, триграмм и пр – ведёт к “раздуванию” кодовых таблиц
  • модификация кодов “на лету” – сильно усложняет кодирование и декодирование
  • Плюс, передача кодовых таблиц вместе с сообщением может сильно увеличивать избыточность
  • \(H(X) \le L(C,X) < H(X)+1\) – кодирование имеет от 0 до 1 бит избыточности
  • Если \(H(X)\) велико – не проблема
  • в реальности, \(H(X) \le 1\) – не редка, избыточность кода будет доминировать.

Арифметическое кодирование

Код Элиаса: \[F_i = \frac{p_i}{2} + \sum_{j=1}^{i-1} p_j,\] \[|C(x)| = \ceil{-\log_2 P(x)} + 1\]

Неэффективно.

Идея: кодировать всю строку \(\vect s\) целиком, используя для каждого символа \(P(x_i | \vect s)\).

Арифметическое кодирование
алгоритм сжатия информации без потерь, который при кодировании ставит в соответствие тексту вещественное число из отрезка [0;1).
  • Используется вероятностная модель источника.

  • Отделена от алгоритма кодирования.

  • Произвольный выбор вероятностной модели.

Источник \(X=\{x_1, \ldots, x_n, x_I\}\)

  • \(x_I\) – “конец сообщения”.
  • вероятностная модель, \(P(x_i|\vect s)\)

Алгоритм:

  1. \(\forall x_i\) ставим в соответствие отрезок длины \(P(x_i)\).
  2. Читаем \(x_i\) из источника и рассматриваем соответствующий отрезок. \(\forall x_j\) ставим в соответствие подотрезок длины \(P(x_j | x_i) P(x_i)\).
  3. Повторим пункт (2) до конца входного потока.
  4. Выберем любое число из получившегося отрезка, которое и будет результатом арифметического кодирования.

Формально \[Q(x_k | \vect s) \equiv \sum_{i=1}^{k-1} P(x_i | \vect s),\] \[R(x_k | \vect s) \equiv Q(x_k | \vect s) + P(x_k | \vect s).\]

l := 0.0
h := 1.0
w := h-l
for n = 1 to N {
  h := l + w * R(x_n | s_n)
  l := l + w * Q(x_n | s_n)
  w := h - l
}

l := 0.0
h := 1.0
w := h-l
for n = 1 to N {
  h := l + w * R(x_n | s_n)
  l := l + w * Q(x_n | s_n)
  w := h - l
}
  • N – длина сообщения
  • s_n – строка ранее прочитанных символов
  • x_n – очередной символ источника

Может быть реализован потоково:

если \(l = 0.10101101\ldots_2\), \(h = 0.10101110\ldots_2\), то код будет начинаться на 10101101.

Декодирование

  1. Выберем отрезок, содержащий число, соответствующее коду. Символ, соответствующий отрезку, дописываем в ответ.
  2. Нормируем отрезок и вещественное число.
  3. Повторим пункты 1—2 до тех пор, пока не получим ответ.

Допускает потоковую реализацию. Аналогично коду Элиаса, по первым \(\ceil{\log_2 |X| + 1}\) битам кода можно однозначно определить интервал, соответствующий первому символу и т.д.

Эффективность

Лучше Хаффмана с большой длиной блока?

  • Да!
  • Арифметическое кодирование – вычисляем \(N |X|\) вероятностей
  • Алгоритм Хаффмана – вычисляем все \(|X|^N\) вероятностей.

Ожидаемая длина кода

Энтропия строки \(\vect s\) длины \(N\)

\[H = - \sum_{\vect s \in X^N} P(\vect s) \log_2 P(\vect s)\]

Та же энтропия, что у выбора отрезка длиной \(P(\vect s)\) на интервале \([0,1)\).

Избыточность арифметического кода – только из-за конечной точности.

Пусть кодируется интервал \([R_l, R_h)\), который “почти полностью” заполняет некий двоичный интервал \([B_l, B_h)\), \(0 < R_l - B_l < \varepsilon,\) \(0 < B_h - R_h < \varepsilon\).

Интервалы \([B_l, B_l + (B_h-B_l)/2)\), \([B_l+ (B_h-B_l)/2, B_h)\) не подходят, т.к. \(R_l > B_l,\) \(R_h < B_h\).

Интервал \([B_l + (B_h-B_l)/4, B_l + 3(B_h-B_l)/4)\) – подходит. Требует 2 дополнительных бит.

\[L(C,X^N) < H(X^N)+2\]

Другие применения арифметического кодирования

  • Эффективная генерация случайных значений

  • Эффективный ввод

Кодирование Лемпеля-Зива

Авраам Лемпель и Яаков Зив, 1977-1978

  • Резко другой подход
  • Универсальный
  • Менее эффективный
  • LZW, LZMA – популярные модификации

Идея: посимвольно читать источник, каждую новую подстроку сохранять в словарь, последующие вхождения заменять на индекс в словаре.

Алгоритм движется по входной строке, держа в памяти “словарь”. Встречая новую подстроку, алгоритм выводит индекс в словаре максимального префикса и суффикс (при двоичном входном алфавите, суффикс всегда имеет длину 1 бит). Для записи префикса используется \(\ceil{log_2 N}\) бит, где \(N\) – текущий размер словаря.

На практике, размер “словаря” ограничивается.

При декодировании, “словарь” восстанавливается “на лету”.

Пример

Рассмотрим алгоритм LZ для двоичных входного и целевого алфавита.

Вход:
‸11010111011101

Буфер:
""

Выход:
""

\(\ceil{log_2 1} = 0\)

Индекс Строка
0 ""

Вход:
1‸1010111011101

Буфер:
1

Выход:
""

\(\ceil{log_2 1} = 0\)

Индекс Строка
0 ""

Вход:
1‸1010111011101

Буфер:
1

Выход:
’1

\(\ceil{log_2 1} = 0\)

Индекс Строка
0 ""

Вход:
1‸1010111011101

Буфер:
""

Выход:
’1

\(\ceil{log_2 2} = 1\)

Индекс Строка
0 ""
1 “1”

Вход:
11‸010111011101

Буфер:
1

Выход:
’1

\(\ceil{log_2 2} = 1\)

Индекс Строка
0 ""
1 “1”

Вход:
110‸10111011101

Буфер:
10

Выход:
’1 1’0

\(\ceil{log_2 2} = 1\)

Индекс Строка
0 ""
1 “1”

Вход:
110‸10111011101

Буфер:
""

Выход:
’1 1’0

\(\ceil{log_2 3} = 2\)

Индекс Строка
0 ""
1 “1”
10 “10”

Вход:
1101‸0111011101

Буфер:
1

Выход:
’1 1’0

\(\ceil{log_2 3} = 2\)

Индекс Строка
0 ""
1 “1”
10 “10”

Вход:
11010‸111011101

Буфер:
10

Выход:
’1 1’0

\(\ceil{log_2 3} = 2\)

Индекс Строка
0 ""
1 “1”
10 “10”

Вход:
110101‸11011101

Буфер:
101

Выход:
’1 1’0 10’1

\(\ceil{log_2 3} = 2\)

Индекс Строка
0 ""
1 “1”
10 “10”

Вход:
110101‸11011101

Буфер:
""

Выход:
’1 1’0 10’1

\(\ceil{log_2 4} = 2\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”

Вход:
1101011‸1011101

Буфер:
1

Выход:
’1 1’0 10’1

\(\ceil{log_2 4} = 2\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”

Вход:
11010111‸011101

Буфер:
11

Выход:
’1 1’0 10’1 01’1

\(\ceil{log_2 4} = 2\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”

Вход:
11010111‸011101

Буфер:
""

Выход:
’1 1’0 10’1 01’1

\(\ceil{log_2 5} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”

Вход:
110101110‸11101

Буфер:
0

Выход:
’1 1’0 10’1 01’1 000’0

\(\ceil{log_2 5} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”

Вход:
110101110‸11101

Буфер:
""

Выход:
’1 1’0 10’1 01’1 000’0

\(\ceil{log_2 6} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”
101 “0”

Вход:
1101011101‸1101

Буфер:
1

Выход:
’1 1’0 10’1 01’1 000’0

\(\ceil{log_2 6} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”
101 “0”

Вход:
11010111011‸101

Буфер:
11

Выход:
’1 1’0 10’1 01’1 000’0

\(\ceil{log_2 6} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”
101 “0”

Вход:
110101110111‸01

Буфер:
111

Выход:
’1 1’0 10’1 01’1 000’0 100’1

\(\ceil{log_2 6} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”
101 “0”

Вход:
110101110111‸01

Буфер:
""

Выход:
’1 1’0 10’1 01’1 000’0 100’1

\(\ceil{log_2 7} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”
101 “0”
110 “111”

Вход:
1101011101110‸1

Буфер:
0

Выход:
’1 1’0 10’1 01’1 000’0 100’1

\(\ceil{log_2 7} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”
101 “0”
110 “111”

Вход:
11010111011101‸

Буфер:
01

Выход:
’1 1’0 10’1 01’1 000’0 100’1 101’1

\(\ceil{log_2 7} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”
101 “0”
110 “111”

Вход:
11010111011101‸

Буфер:
""

Выход:
’1 1’0 10’1 01’1 000’0 100’1 101’1

\(\ceil{log_2 8} = 3\)

Индекс Строка
0 ""
1 “1”
10 “10”
11 “101”
100 “11”
101 “0”
110 “111”
111 “01”

Теоретические свойства

Для эргодического источника LZ с ростом размера сообщения асимптотически приближается к энтропии источника.

Ключевые слова:

  • “асимптотически”
  • “с ростом размера сообщения”

Для многих источников, размер сообщения, при котором длина кода близка к энтропии, непрактично высок.

LZ далёк от эффективности арифметического кодирования.

Для типичных источников, LZ даёт практически хорошие результаты.