Преобразование Барроуза - Уилера
Как уже указывалось, преобразование Барроуза - Уилера предназначено
для того, чтобы преобразовать входной блок в более удобный для сжатия вид. Причем, как показывает практика, полученный в результате преобразования блок обычные методы сжимают не так эффективно, как методы, специально для этого разработанные.
Поэтому нельзя рассматривать описываемый алгоритм отдельно от соответствующих специфических методов кодирования данных.
В оригинальной статье была предложена как одна из возможных реализаций сжатия на основе BWT совокупность из трех алгоритмов:
■ преобразования Барроуза - Уилера;
■ преобразования Move-To-Front (известного в русскоязычной литературе как перемещение стопки книг);
■ статистического кодера для сжатия данных, полученных в результате последовательного применения двух вышеупомянутых преобразований.
Дальнейшие исследования показали, что второе из перечисленных преобразований не является необходимым и может быть заменено альтернативным. Или даже исключено вовсе за счет усложнения кодирующей фазы. Впрочем, еще сами авторы BWT упоминали о такой возможности.
Итак, начнем с описания главной составляющей части процесса сжатия данных при помощи методов на основе BWT - с самого преобразования.
Прежде всего следует отметить одну из его особенностей. Он оперирует сразу целым блоком данных. То есть ему заранее известны сразу все элементы входного потока или по крайней мере достаточно большого блока. Это делает затруднительным использование алгоритма в тех областях применения, где требуется сжатие данных "на лету", символ за символом. В этом отношении BWT даже более требователен, чем методы семейства LZ77, использующие для сжатия скользящее окно.
Следует отметить, что возможна реализация сжатия данных на основе BWT, обрабатывающая данные последовательно по символам, а не по блокам. Но скоростные характеристики программ, использующих такую реализацию, будут очень далеки от совершенства.
Таким образом, мы пришли к первой и самой легкой из фаз преобразования - к выделению из непрерывного потока блока данных.
Де-факто описывать BWT стало принято с помощью примера преобразования строки символов "абракадабра".
Далее нужно из полученного блока данных создать матрицу всех возможных его циклических перестановок. Первой строкой матрицы будет исходная последовательность, второй строкой - она же, сдвинутая на один символ влево, и т. д. Таким образом, получим матрицу, изображенную на рис. 5.1.
абракадабра бракадабраа ракадабрааб акадабраабр кадабраабра адабраабрак дабраабрака абраабракад браабракада раабракадаб аабракадабр
Рис. 5.1. Множество циклических перестановок строки "абракадабра "
Пометим в этой матрице исходную строку и отсортируем все строки в соответствии с лексикографическим порядком символов. Будем считать, что одна строка должна находиться в матрице выше другой в том случае, если в самой левой из позиций, начиная с которой строки отличаются, в этой строке находится символ лексикографически меньший, чем у другой строки. Другими словами, следует отсортировать символы сначала по первому символу, затем строки, у которых первые символы равны, - по второму и т. д. (рис. 5.2).
|
0 |
аабракадабр |
|
1 |
абраабракад |
|
2 |
абракадабра - исходная строка |
|
3 |
адабраабрак |
|
4 |
акадабраабр |
|
5 |
браабракада |
|
б |
бракадабраа |
|
7 |
дабраабрака |
|
8 |
кадабраабра |
|
9 |
раабракадаб |
|
10 |
ракадабрааб |
Рис. 5.2. Матрица циклических перестановок строки "абракадабра", отсортированная слева направо в соответствии с лексикографическим порядком символов ее строк
Теперь остался последний шаг - выписать символы последнего столбца и запомнить номер исходной строки среди отсортированных. Итак, "рда-краааабб",2 - это результат, полученный в результате преобразования Бар-роуза - Уилера.
Теперь нам осталось сделать "всего" 3 вещи:
1) доказать, что преобразование обратимо;
2) показать, что оно не требует огромного количества ресурсов;
3) показать, что оно полезно для последующего сжатия.
Упражнение. Проделайте преобразование Барроуза - Уилера строки "карабас".
Доказательство обратимости преобразования Барроуза -Уилера
Возможно, Дэвид Уилер не опубликовал описание алгоритма в 1983 г. потому, что ему самому показалось странным, что можно восстановить начальную строку из столь сильно перемешанных между собой символов. Но так или иначе, это не фокус и обратное преобразование действительно возможно.
Пусть нам известны только результат преобразования, т. е. последний столбец матрицы, и номер исходной строки. Рассмотрим процесс восстановления исходной матрицы. Для этого отсортируем все символы последнего столбца (рис. 5.3).
Нам известно, что строки матрицы были отсортированы по порядку, начиная с первого символа. Поэтому, очевидно, в результате такой сортировки мы получили первый столбец исходной матрицы.
Поскольку последний столбец по условию задачи нам известен, добавим его в полученную матрицу (рис. 5.4).
|
|
|
0 |
а |
|
1 |
а |
|
2 |
а |
|
3 |
а |
|
4 |
а |
|
5 |
б |
|
6 |
б |
|
7 |
д |
|
8 |
к |
|
9 |
р |
|
10 |
р |
Рис. 5.3. Отсортированные символы исходной строки
Рис. 5.4. Первый и последний столбцы матрицы циклических перестановок
Теперь самое время вспомнить, что строки матрицы были получены в результате циклического сдвига исходной строки. То есть, символы последнего и первого столбцов образуют друг с другом пары. И нам ничто не может помешать отсортировать эти пары, поскольку обязательно существуют такие строки в матрице, которые начинаются с этих пар. Заодно допишем в матрицу и известный нам последний столбец (рис. 5.5).
I 2 3 4 5 6 7 8 9 10
аа..... р
аб..... д
аб..... а
ад..... к
ак...... р
бр...... а
бр...... а
да..... а
ка...... а
ра...... б
Ра....... б
Рис. 5.5. Первый, второй и последний столбцы матрицы
Таким образом, два столбца нам уже известны. Легко заметить, что отсортированные пары вместе с символами последнего столбца составляют тройки. Аналогично восстанавливается вся матрица. А на основании записанного заранее номера исходной строки в матрице - и сама исходная строка (рис. 5.6).
|
0 |
ааб... |
...р |
аабр... |
...р |
аабракада.р |
аабракадабр |
|
1 |
абр... |
-Д |
абра... |
..Л |
абраабрак.д |
абраабракад |
|
2 |
авр- |
...а |
абра... |
...а |
абракадаб.а |
абракадабра |
|
3 |
ала... |
...к |
адаб... |
...к |
адабраабр.к |
адабраабрак |
|
4 |
ака... |
...р |
акад... |
...р |
акадабраа.р |
акадабраабр |
|
5 |
бра... |
...а |
браа... |
...а |
браабрака.а |
браабракада |
|
6 |
бра- |
...а |
|
|
бракадабр.а |
бракадабраа |
|
7 |
дев... |
...а |
дабр.. |
...а |
дабраабра.а |
дабраабрака |
|
8 |
кад... |
...а |
када... |
...а |
кадабрааб.а |
кадабраабра |
|
9 |
раа... |
6 |
рааб... |
...б |
раабракад.б |
раабракадаб |
|
10 |
рак... |
б |
рака... |
...б |
ракадабра.б |
ракадабрааб |
Рис. 5.6. Процесс определения всех столбцов матрицы
Упражнение. В результате преобразования Барроуза - Уилера получена строка "тпрооппо". Номер исходной строки в матрице преобразований - 5 (считая с нуля). Восстановите исходную строку.
Вектор обратного преобразования
После того как мы доказали принципиальную возможность обратного преобразования, можно показать, что для его осуществления нет необходимости посимвольно выписывать все строки матрицы перестановок. Если бы мы хранили в памяти всю матрицу, требуемую для мегабайтового блока данных, нам потребовалось бы... В общем, видно, что затея была бы бесполезной.
Для обратного преобразования нам дополнительно к собственно данным нужен только вектор обратного преобразования, представляющий собой массив чисел, размер которого равен числу символов в блоке. Поэтому затраты памяти при выполнении обратного преобразования линейно зависят от размера блока.
Обратите внимание, что в процессе выявления очередного столбца матрицы мы совершали одни и те же действия. А именно получали новую строку, сливая символ из последнего столбца старой строки с известными символами первых столбцов этой же строки. И новая строка после сортировки перемещалась в другую позицию в матрице.
Из строки 0 мы получали строку 9, из первой - седьмую и т. п., (рис. 5.7).
|
Номер строки |
|
|
Номер |
новой строки |
■......................................................................................................................... "1 |
|
0 |
а.... |
....р |
|
9 |
|
|
1 |
|
....д |
|
7 |
* |
|
2 |
а.... |
....а |
|
0 |
- исходная строка |
|
3 |
а.... |
....к |
|
8 |
; |
|
4 |
а.... |
....р |
|
10 |
1 1 |
|
5 |
б |
....а |
|
1 |
J |
|
6 |
б |
....а |
|
2 |
|
|
7 |
д.... |
....а |
|
3 |
|
|
8 |
к.... |
....а |
|
4 |
i |
|
9 |
р.... |
....б |
|
5 |
I i |
|
10 |
р.... |
....б |
|
6 |
t |
Рис. 5.7. Способ получения первого столбца матрицы из последнего
Важно, что при добавлении любого столбца перемещения строк на новую позицию были одинаковы. Нулевая строка перемещалась в девятую позицию, первая - в седьмую и т. д.
Чтобы получить вектор обратного преобразования, следует определить порядок получения символов первого столбца из символов последнего. То есть, отсортировать матрицу по номерам новых строк (рис. 5.8).
|
Номер строки |
|
|
Номер |
новой строки |
Переносим последний |
|
|
|
|
|
|
|
столбец |
в начало |
|
2 |
а.... |
....а |
|
0 |
|
.... Р |
|
5 |
б, |
|
|
1 |
аб... |
.... д |
|
6 |
б, |
|
|
2 |
аб.. |
.... а |
|
7 |
д.... |
....а |
|
3 |
ад... |
|
|
8 |
к.... |
....а |
|
4 |
ак... |
.... Р |
|
9 |
р.... |
б |
|
5 |
бр.. |
.... а |
|
10 |
р.... |
б |
|
6 |
бр.. |
.... а |
|
1 |
а.... |
....д |
|
7 |
Да- |
.... а |
|
3 |
а.... |
....к |
|
8 |
ка- |
.... а |
|
0 |
а.... |
....р |
|
9 |
ра... |
.... б |
|
4 |
а.... |
•-Р |
|
10 |
............ Pa:v |
.... б |
Рис. 5.8. Получение вектора обратного преобразования
Полученные значения номеров строк Т ={ 2, 5, 6, 7, 8, 9, 10, 1, 3, 0, 4 } и есть искомый вектор, содержащий номера позиций символов в строке, которую нам надо декодировать (символы упорядочены в соответствии с положением в алфавите).
Теперь получить исходную строку совсем просто. Первым делом возьмем элемент вектора обратного преобразования, соответствующий номеру исходной строки в матрице циклических перестановок, Т[2] = 6. Иначе говоря, в качестве первого символа в исходной строке следует взять шестой символ из строки "рдакраааабб". Это символ "а".
Затем нужно определить, какой символ заставил опуститься найденный символ "а" на вторую позицию среди равных. Искомый символ находится в последнем столбце шестой строки матрицы, изображенной на рис. 5.8. А поскольку Т[6] = 10, в преобразованной строке он находится в десятой позиции. Это символ "б". И т. д. (рис. 5.9).
6=^ 10 => Л=> 8=> 3=> 7=> 1=> 5=> 9=> 0=> 2
аб ракадабра
Рис. 5.9. Декодирование исходной строки при помощи вектора обратного преобразования
Упражнение. В результате преобразования Барроуза - Уилера получена строка "тпрооппо". Номер исходной строки в матрице преобразований - 5 (считая с нуля). Постройте вектор обратного преобразования.
Реализация обратного преобразования
Получение исходной строки из преобразованной можно проиллюстрировать при помощи небольшой программы. Введем следующие обозначения: и - количество символов в блоке входного потока; N - количество символов в алфавите; pos - номер исходной строки в матрице перестановок; in - входной блок;
count - массив частот каждого символа алфавита во входном блоке; Т - вектор обратного преобразования, размер вектора равен л.
Первым делом следует посчитать частоты символов и пронумеровать все исходные символы в порядке их появления в алфавите. По сути, это эквивалентно построению первого столбца матрицы циклических перестановок.
for( i-0; i<N; i++) count[i]=0; for( i=0; i<n; i++) count [in[i]]++; sum = 0; for( i=0; i<n; i++) {
sum += count[i];
count[i] = sum - count[i]; }
После выполнения этих действий countf/] указывает на первую позицию символа с кодом i в первом столбце матрицы. Следующий шаг - создание вектора обратного преобразования.
for( i=0; i<n; i++) T [count[in[i]]++]]=i;
Далее при помощи полученного вектора восстановим исходный текст.
for( i=0; i<n; i++) {
putc( in[pos], output );
pos = T[pos]; }
Использование BWT в сжатии данных
Теперь, после того как выяснилось, что наши действия вполне обратимы и данные мы не исказим, можно вернуться к вопросу рассмотрения полезности преобразования. Как уже отмечалось выше, главная задача преобразования Барроуза - Уилера заключается в том, чтобы ловко переставить символы. Переставить так, чтобы их можно было легко сжать, не ломая голову над их взаимосвязями. Потому что преобразование как раз тем и занимается: "вытаскивает" все взаимосвязи наружу. Точнее, очень многие.
Для понимания этого процесса достаточно представить поток данных состоящим из набора некоторых стабильных сочетаний символов. Например, как этот текст, состоящим из слов. Сочетание символов, позволяющее предсказать некоторый неизвестный доселе символ, называется контекстом. Например, если нам известна последовательность символов "реобразова-ние", то скорее всего ей предшествует символ "п". Назовем устойчивым (стабильным) такой контекст, для которого распределение частот символов, непосредственно примыкающих к нему слева или справа, меняется незначительно в пределах блока.
Если нам потребуется подвергнуть преобразованию данную главу, можно с уверенностью сказать, что строки, начинающиеся с символов "реобра-зование", будут располагаться рядом в отсортированной матрице. И в соответствующих этим строкам позициях последнего столбца матрицы будет находиться символ "п".
Таким образом, главное свойство преобразования в том, что оно собирает вместе символы, соответствующие похожим контекстам. Чем больше стабильных контекстов в блоке данных, тем лучше будет сжиматься полученный в результате преобразования блок. Практика показывает, что в результате преобразования обычных текстов более половины из всех символов следует за такими же.
Упражнение. Какие еще символы помимо "п" могут оказаться в конце строк, начинающихся с последовательности символов "реобразование", в результате преобразования данной главы?
Частичное сортирующее преобразование
Некоторое время спустя после появления первых архиваторов, использующих преобразование Барроуза - Уилера, было опубликовано описание еще одного алгоритма, также основанного на сортировке блока данных [33, I]. Отличие от BWT заключается в том, что сортировка строк матрицы осуществляется не по всей длине строк, а только по некоторому фиксированному количеству символов. В том случае, если у нескольких строк эти символы одинаковы, выше в списке помещается строка, первый символ которой встретился во входном блоке раньше первых символов остальных рассматриваемых строк.
Можно сказать, что позиция первого символа строки во входном блоке участвует в сортировке. Число символов строки, участвующих в преобразовании, называется порядком сортирующего преобразования. Легко заметить, что в результате преобразования нулевого порядка, ST(0), получается исходная строка. В качестве примера выполним преобразования 1-го и 2-го порядка строки "абракадабра" (рис. 5.10).
|
трок| |
1 ST(1) |
ST(2) |
BWT |
|
|
Первый шаг. Постро |
ение матрицы преобразо |
вания |
|
0 |
а< Обракадабра |
аб< 0>ракадабра |
абракадабра |
|
1 |
б< 1>ракадабраа |
бр< 1>акадабраа |
бракадабраа |
|
2 |
р< 2>акадабрааб |
ра< 2>кадабрааб |
ракадабрааб |
|
3 |
а< 3>кадабраабр |
ак< 3>адабраабр |
акадабраабр |
|
4 |
к< 4>адабраабра |
ка< 4>дабраабра |
кадабраабра |
|
5 |
а< 5>дабраабрак |
ад< 5>абраабрак |
адабраабрак |
|
6 |
д< 6>абраабрака |
да< Обраабрака |
дабраабрака |
|
7 |
а< 7>6раабракад |
аб< 7>раабракад |
абраабракад |
|
8 |
б< 8>раабракада |
бр< 8>аабракада |
браабракада |
|
9 |
р< 9>аабракадаб |
ра< 9>абракадаб |
раабракадаб |
|
10 |
а<10>а6ракадабр |
аа< 10>бракадабр |
аабракадабр |
|
|
Второй |
шаг. Сортировка |
|
|
0 |
а< Обракадабра |
аа<10>бракадабр |
аабракадабр |
|
1 |
• а< 3>кадабраабр |
аб< 0>ракадабра |
абраабракад |
|
2 |
а< 5>дабраабрак . |
аб< 7>раабракад |
абракадабра |
|
3 |
а< 7>браабракад |
ад< 5>абраабрак |
адабраабрак |
|
4 |
а<10>абракадабр |
ак< 3>адабраабр |
акадабраабр |
|
5 |
б< 1>ракадабраа |
бр< 1>акадабраа |
браабракада |
|
6 |
б< 8>раабракада |
бр< 8>аабракада |
бракадабраа |
|
7 |
д< 6>абраабрака |
да< 6>браабрака |
дабраабрака |
|
8 |
к< 4>адабраабра |
ка< 4>дабраабра |
кадабраабра |
|
9 |
р< 2>акадабрааб |
ра< 2>кадабрааб |
раабракадаб |
|
10 |
р< 9>аабракадаб |
ра< 9>абракадаб Результат |
ракадабрааб |
|
|
аркдраааабб,5 |
радкраааабб,5 |
рдакраааабб,2 |
Рис. 5.10. Частичные сортирующие преобразования 1-го и 2-го порядка
Так же как и в BWT, результатом преобразования являются последний столбец матрицы и номер строки, последний символ которой является начальным символом исходной строки.
Упражнение. Выполните преобразование ST(3) строки "абракадабра".
Легко заметить, что различие между частичным сортирующим преобразованием и преобразованием Барроуза - Уилера можно увидеть только при сортировке устойчивых контекстов длиннее порядка преобразования ST. В методе BWT в этом случае продолжается процесс сравнения символов, а в ST- сравнение символов прекращается и выше располагается та строка, первый символ которой встретился во входном блоке раньше. Следовательно, те данные, в которых встречаются длинные повторы, более эффективно сжимаются преобразованием Барроуза - Уилера. К примеру, типичные текстовые файлы на английском языке теряют в сжатии около 5% при выполнении сортировки по четырем символам.
С другой стороны, частичное сортирующее преобразование не требует полной сортировки всех строк матрицы и свободно от тех проблем, которые возникают при преобразовании очень избыточных данных с использованием полной сортировки. Как правило, данное преобразование выполняется быстрее, чем BWT.
Но при выполнении обратного преобразования наблюдается иная картина. Если в случае BWT оно выполняется легко и просто, то для восстановления исходных данных после частичного сортирующего преобразования необходимо приложить дополнительные усилия. А именно вести учет количества одинаковых контекстов. И чем порядок преобразования больше, тем требуется больше времени на подсчет.
- Теги:
- 485 просмотров









