Как устроены беспрефиксные коды: лекция Стэнфорда о сжатии данных

Stanford Online 4 тыс. 1 ч 16 мин 8 мин 18.04.2024
Главное

В лекции Стэнфордского университета по сжатию данных рассматриваются фундаментальные принципы беспрефиксного кодирования и однозначной декодируемости информации. Преподаватель подробно разбирает механизмы оптимизации длины кода, устройство кодовых деревьев и пошаговый алгоритм построения кодов Шеннона. Особое внимание уделяется математическому доказательству применимости жадных алгоритмов в теории информации и анализу исторических систем передачи данных.

🧠 Разминка: анализ фиксированной длины и блочного кодирования 0:18

Лекция начинается с разбора практических задач, закладывающих основу для понимания неоптимальности стандартных подходов. В качестве примера рассматривается алфавит из 9 символов, для которого необходимо спроектировать код фиксированной длины. Согласно классической формуле теории информации, количество бит на символ определяется как округленный в большую сторону двоичный логарифм от размера алфавита: $\lceil \log_2 |X| \rceil$. Ближайшая степень двойки, превосходящая 9, — это 16 ($\log_2 16 = 4$), следовательно, для кодирования фиксированной длиной потребуется 4 бита на символ.

Такой подход очевидно избыточен, поскольку число 9 лишь незначительно превышает 8 ($2^3 = 8$), и идеальная длина должна находиться в диапазоне между 3 и 4 битами. Чтобы преодолеть это ограничение, лектор предлагает использовать блочное кодирование — объединять символы в пары перед отправкой. При кодировании блоками по два символа размер нового эффективного алфавита возрастает до $9 \times 9 = 81$.

Для кодирования 81 комбинации фиксированной длиной требуется найти следующую степень двойки. Поскольку 81 находится между 64 ($2^6$) и 128 ($2^7$), для кодирования одного блока необходимо 7 бит. В пересчете на один символ средняя длина составляет:

$$7 / 2 = 3.5 \text{ бита}$$

Это демонстрирует явное улучшение компрессии по сравнению с исходными 4 битами.

По словам преподавателя, теоретический предел для алфавита из 9 символов равен $\log_2 9 \approx 3.17$ бита. Приблизиться к этому значению еще ближе можно за счет увеличения размера блоков — например, кодируя по три символа за раз. Однако ключевым недостатком этой стратегии является то, что размер алфавита растет экспоненциально по мере увеличения размера блока, что усложняет вычисления.

🔍 Однозначное декодирование и уроки истории 10:44

Для построения надежных систем связи необходимо четко определить критерии сжатия без потерь. Центральным понятием здесь выступает однозначно декодируемый код (uniquely decodable code). По определению лектора, код является однозначно декодируемым, если никакие две различные входные последовательности символов $x^n$ и $y^m$ (где $n, m \ge 1$) не могут быть закодированы в одну и ту же выходную битовую строку.

Простые примеры наглядно иллюстрируют неочевидность этого свойства:

В повседневной человеческой речи для разделения слов используются паузы и пробелы. В компьютерных системах, оперирующих бинарным алфавитом (0 и 1), выделение отдельного символа под «пробел» является непозволительной роскошью с точки зрения эффективности.

Историческим примером компромиссного решения является азбука Морзе, активно применявшаяся в телеграфии. Она состоит из точек и тире: например, буква E кодируется как ., S как ..., а Q как --.-. С математической точки зрения азбука Морзе не является однозначно декодируемой. Последовательность из точки и тире (. -) может означать как комбинацию букв ET, так и одиночную букву A. Система успешно функционировала лишь потому, что люди-операторы (а позже — машины) делали физическую временную паузу между буквами, фактически вводя в систему третий символ-разделитель.

При этом создатели азбуки Морзе интуитивно использовали важнейший принцип современного сжатия: наиболее частые буквы английского языка (E и T) получили самые короткие коды, что сокращало общее время передачи сообщений.

⚡ Беспрефиксные коды и алгоритмы мгновенного декодирования 24:28

В реальной практике инженеры не работают со всем спектром однозначно декодируемых кодов, а ограничиваются более узким и удобным классом — беспрефиксными (или мгновенными) кодами. Главное свойство беспрефиксного кода формулируется просто: ни одно кодовое слово не должно являться началом (префиксом) другого кодового слова.

Для таких кодов существует элементарный потоковый алгоритм декодирования с линейным временем работы:

  1. Создается пустая строковая переменная $C$, куда поочередно записываются поступающие биты.
  2. После добавления каждого бита проверяется, совпадает ли текущее значение $C$ с каким-либо элементом в таблице кодов.
  3. Если совпадение найдено, символ успешно декодируется, а переменная $C$ сбрасывается до пустого состояния.
  4. Если совпадения нет, алгоритм просто считывает следующий бит и продолжает накопление.

В качестве примера рассматривается таблица, где A=0, B=10, C=110, D=111. При получении битового потока 011000... алгоритм на первом же шаге видит 0, находит его в таблице и мгновенно декодирует символ A. Затем буфер очищается, считывается 1 (совпадений нет), затем еще 1 (нет), затем 0. Строка 110 успешно распознается как символ C, и процесс циклически повторяется.

Любой беспрефиксный код гарантированно является однозначно декодируемым, поскольку при обнаружении совпадения в таблице полностью исключен риск того, что данная битовая цепочка является лишь частью более длинного кодового слова. Обратное утверждение неверно: существуют однозначно декодируемые коды, не являющиеся беспрефиксными, однако они не представляют практического интереса для потоковой обработки данных.

🌳 Бинарные деревья как модель кодирования 33:33

Любой беспрефиксный код может быть наглядно представлен в виде бинарного дерева. Структура дерева строится от корня, а ветвление определяется битами: левый потомок соответствует нулю (0), правый — единице (1). Кодовые слова в такой модели жестко привязаны к листьям дерева, а путь от корня до конкретного листа в точности формирует битовую последовательность кода.

Процесс декодирования с помощью дерева выглядит как перемещение указателя: считывая биты, мы спускаемся по ветвям вниз, пока не упремся в лист. Как только лист достигнут, система выдает соответствующий символ и мгновенно возвращает указатель в корень дерева для обработки следующих данных.

Если попытаться построить дерево для кода, нарушающего беспрефиксное условие (например, где A=0, B=10, C=100), то символ B окажется промежуточным внутренним узлом, из которого продолжается ветвление к символу C. Это наглядно доказывает эквивалентность: код является беспрефиксным тогда и только тогда, когда все его кодовые слова располагаются строго на терминальных листьях бинарного дерева.

📈 Критерии «хорошего» кода и наследие Клода Шеннона 41:07

Математическое определение «хорошего» беспрефиксного кода сводится к минимизации математического ожидания длины кодового слова при соблюдении условия беспрефиксности. Профессор Томас Ковер в своей классической книге «Elements of Information Theory» детально описывает свойства таких систем.

Первое качественное свойство гласит: если вероятность появления символа $S_1$ строго выше вероятности символа $S_2$, то длина кодового слова для $S_1$ должна быть меньше или равна длине кода для $S_2$. Если в каком-то коде это правило нарушено, его можно мгновенно улучшить, просто поменяв кодовые слова символов местами, что снизит общее математическое ожидание длины.

Второе, более строгое свойство утверждает, что идеальная длина кода для символа $X$ должна быть примерно равна отрицательному двоичному логарифму его вероятности:

$$L(X) \approx \log_2 \frac{1}{P(X)}$$

Для простейшего случая — равномерного распределения среди $k$ элементов — эта формула вырождается в фиксированную длину $\lceil \log_2 k \rceil$, что полностью совпадает со стандартной практикой кодирования. Если же вероятности распределены неравномерно (например, степени двойки: $1/2, 1/4, 1/8, 1/8$), то длины кодовых слов составят 1, 2, 3 и 3 бита соответственно, формируя абсолютно оптимальный код.

Основоположник теории информации Клод Шеннон в своей эпохальной работе 1948 года «A Mathematical Theory of Communication» предложил алгоритм построения префиксного кода, который в явном виде реализует это свойство. Использование именно двоичного логарифма обусловлено физической природой полупроводниковых компьютеров, работающих с бинарными битами.

🛠️ Пошаговое построение кода Шеннона 53:40

Алгоритм создания кода Шеннона представляет собой классический жадный (greedy) алгоритм и состоит из трех шагов:

  1. Для каждого символа алфавита вычисляется целевая длина кодового слова по формуле $L(X) = \lceil \log_2 (1/P(X)) \rceil$.
  2. Символы сортируются по подлежащим длинам в порядке возрастания (что эквивалентно порядку убывания их вероятностей).
  3. Для каждого символа в полученном порядке последовательно подыскивается свободный лист в бинарном дереве на глубине, равной его вычисленной длине $L(X)$, без нарушения беспрефиксного свойства.

Для иллюстрации лектор приводит пример с алфавитом из пяти символов (A, B, C, D, E), чьи рассчитанные длины составляют 2, 2, 3, 3 и 5 бит соответственно. Процесс идет последовательно: символу A присваивается код 00, символу B — код 01. При переходе к символу C (длина 3) ветка 000 недоступна, так как она нарушит беспрефиксность для кодового слова A, поэтому алгоритм выбирает свободную правую ветвь, формируя, например, код 111. За ним символ D получает код 101, а символ E размещается на пятом уровне глубины.

Преподаватель подчеркивает, что коды Шеннона гарантированно близки к теоретическому пределу, но не всегда являются абсолютно лучшими (оптимальными) для конкретного распределения. В рассмотренном примере узел для символа E можно было поднять значительно выше по дереву без ущерба для беспрефиксного свойства, сократив среднюю длину. Истинно оптимальный алгоритм сжатия (код Хаффмана) будет изучен на следующих лекциях.

🧮 Математическое доказательство корректности алгоритма 1:02:08

Для подтверждения работоспособности жадного алгоритма Шеннона необходимо строго доказать, что на каждом шаге итерации на заданной глубине дерева всегда физически найдется хотя бы один свободный лист, не заблокированный предыдущими префиксами. Доказательство строится по методу математической индукции.

Сначала устанавливается базовое ограничение на сумму длин всех потенциальных слов алфавита. Поскольку $L(X) = \lceil \log_2(1/P(X)) \rceil$, из свойств округления следует, что $L(X) \ge \log_2(1/P(X))$. Отсюда выводится неравенство:

$$\sum_{X} 2^{-L(X)} \le \sum_{X} 2^{-\log_2(1/P(X))} = \sum_{X} P(X) = 1$$

Это выражение является частным случаем знаменитого неравенства Крафта.

Предположим, что алгоритм уже успешно распределил $m$ символов (от $X_1$ до $X_m$) и теперь переходит к шагу $m+1$. Поскольку мы рассматриваем лишь частичную сумму элементов, она строго меньше полной суммы, а значит:

$$\sum_{i=1}^{m} 2^{-L(X_i)} < 1$$

Умножив обе части этого неравенства на масштабный коэффициент $2^{L(X_{m+1})}$ и перенеся слагаемые, математики получают ключевое соотношение:

$$\sum_{i=1}^{m} 2^{L(X_{m+1}) - L(X_i)} \le 2^{L(X_{m+1})} - 1$$

Визуальная интерпретация этой формулы делает доказательство кристально прозрачным. Общее число узлов (листьев) в полном бинарном дереве на целевой глубине $L(X_{m+1})$ составляет ровно $2^{L(X_{m+1})}$. Если на некотором предыдущем уровне $L(X_i)$ уже был зафиксирован лист, он безвозвратно блокирует (делает недоступными) всех своих потомков ниже по дереву. Количество таких заблокированных потомков на целевом уровне глубины $L(X_{m+1})$ в точности равно $2^{L(X_{m+1}) - L(X_i)}$.

Левая часть выведенного неравенства представляет собой суммарное количество всех листьев, которые оказались недоступны из-за ранее созданных кодовых слов. Правая часть показывает, что это число гарантированно как минимум на единицу меньше, чем общее количество существующих листьев на данном уровне.

Следовательно, вычитая из общего числа позиций все запрещенные зоны, мы гарантированно имеем в остатке как минимум один свободный узел:

$$2^{L(X_{m+1})} - \text{Заблокированные узлы} \ge 1$$

Это строго доказывает, что алгоритм Шеннона никогда не зайдет в тупик.

💬 Цитаты

«В беспрефиксных кодах ни одно кодовое слово не является префиксом другого.»

Преподаватель Стэнфорда 28:18

«С ростом размера блока ваш алфавит растет экспоненциально.»

Преподаватель Стэнфорда 5:45
👥 Спикер
📚 Упомянутые книги
📖 Термины
Беспрефиксный код
Код, в котором ни одно кодовое слово не совпадает с началом (префиксом) любого другого кодового слова.
Однозначно декодируемый код
Код, для которого любая закодированная битовая последовательность может быть расшифрована единственным корректным способом.
Мгновенный код
Альтернативное название беспрефиксного кода, подчеркивающее возможность декодировать символы сразу по мере их поступления.
📊 Цифры
🗓 Хронология
  1. 1948 Клод Шеннон публикует фундаментальный труд «A Mathematical Theory of Communication», заложив основы теории информации.
⚖️ Другая сторона
Технологии и IT Сжатие данных Код Шеннона Беспрефиксный код Теория информации Stanford Online