§ Задача сжатия
В это статье речь пойдет об LZ-подобных алгоритмах сжатия без потерь. Но для начала я расскажу что из себя представляет задача сжатия. Исходная задача в том, чтобы уменьшить размер данных. Это можно сделать двумя способами - с потерями и без потерь. Первый способ рассматриваться не будет - он относится к алгоритмам сжатия аудио и видео информации. Второй более интересный. Основная суть, идея и задача любого алгоритма сжатия без потерь в том, чтобы найти повторяющиеся тем или иным образом участки в данных и создать их более короткое представление.§ Алгоритм RLE
Существует один из простейших алгоритмов сжатия - оно называется RLE. Работает оно очень просто. Входящая строка разбивается на фрагменты одинаковых, идущих один за другим байтов и пишется сначала их количество, а потом сам повторяющийся фрагмент.Рассмотрим фрагмент
AAAAAAAAAAABBBBBBB
. Здесь видно, что сначала идут одиннадцать букв A, а потом идут 7 букв B. Если это переписать в виде RLE-кодирования, то тогда будет выглядеть так 11A7B. Очевидно, что такая запись более короткая по сравнению с входящими данными. Но этот алгоритм совершенно неэффективен, если таких повторов немного. Существуют разные модификации метода сжатия RLE, но так или иначе, он неэффективен на обычных данных.§ Метод скользящего окна
Он заключается в том, чтобы находить совпадения текста, которые отстоят на определенном расстоянии до текущего символа. Объясню сразу на примере. Есть строка "A FOX HAS FOX TAIL". Как можно заметить, тут есть одинаковые подстроки " FOX " (включая пробел).A FOX HAS FOX TAIL ^^^^^ ^^^^^Другими словами, тут есть возможность сжать текст. В алгоритмах сжатия со скользящим окном сжатие происходит следующим образом: сначала в поток записываются символы, для которых невозможно найти предыдущие совпадения.
Итак, начнем разбор
"A" - ранее в словаре ничего не было, добавляем ее просто в вывод " " - ранее не было, в вывод "F" - аналогично "O" - пропуск "X" - пропуск " " - было ранее, надо отступить назад на 4 символа и взять 1, но это неэффективно, пропуск "H" - пропуск "A" - было 7 символов назад, но 1 символ лишь, пропуск "S" - пропуск " FOX " - а вот тут как раз и найдено, 8 символов назад, 5 символов длинаПоскольку найдено совпадение более чем 2 символа, то это значит, что можно вместо " FOX " просто вставить ссылку, которая будет указывать на то, что 8 символов назад длиной 5 символов есть слово " FOX ". И поскольку это так, то начинается побайтовое копирование из ранее встреченного места.
Таким образом, получается сжатие за счет вставки более короткой ссылки по сравнению с теми данными, которые будут сжаты этой ссылкой.
§ Особенности метода сжатия
Компрессия методом скользящего окна может предусмотреть копирование и 20 символов, начиная с позиции всего лишь 1 символ назад. Представим ситуацию, когда получена такая строка:"ABABABABABABABAB"Казалось бы, как тут закодировать оптимально? Все очень просто. Сначала выдаем 2 символа "AB", после чего ставим ссылку на 2 символа назад и копируем 14 символов!
Разберем по шагам:
"A" пишем в поток "B" пишем в поток --> А тут находится ссылка -2 символа назад и 14 символов длиной. Там буква "A" Пишем в поток, получаем "ABA" Сдвигаемся +1 вперед, пишем "B", получаем "ABAB" Сдвигаемся +1 вперед, а там уже записана "A", копируем ее, получаем "ABABA" Сдвигаемся +1 вперед, а там уже записана "B", копируем ее, получаем "ABABAB" И так далееВ итоге получается исходная строка. Элегантно!
§ Блоки
Обычно есть 2 типа блоков при компрессии, блок 0 - это несжатые данные, и блок 1 - ссылка на данные и ее длинаНапример, если идет блок несжатых данных, то указывается бит 0, и далее длина, которая обычно не превышает некоторого значения. Потом обязательно идет ссылка, после нее тоже может идти ссылка и так далее. Это определяется типом блока.
Приведу пример.
Допустим, закодировали строку "A FOX HAS FOX TAIL". Первые 9 символов - это несжатые данные:
Т L Строка 0 9 A FOX HAS 1 8 5 0 4 TAIL
- T = 0, бит в самом начале блока, который означает, что это несжатые данные;
- L = 9, длина данных, эти данные будут просто выданы в поток без всякого изменения
- Далее идет T=1, L=8, и 5. Это значит, что начиная с текущей позиции в потоке минус 8, будут копироваться байт за байтом 5 символов " FOX "
- Далее идет T=0, L=4, просто выдает в поток "TAIL"