Преобразование Барроуза-Уилера — различия между версиями
(→Доказательство корректности) |
(→Доказательство корректности) |
||
Строка 349: | Строка 349: | ||
<tex>B[i] = S_{p(i)}T[N], (</tex>или <tex>B[i] = S_{p(i) - 1}T[0] = T[(p(i) - 1) (mod\ N + 1)]) \ \ (2)</tex> | <tex>B[i] = S_{p(i)}T[N], (</tex>или <tex>B[i] = S_{p(i) - 1}T[0] = T[(p(i) - 1) (mod\ N + 1)]) \ \ (2)</tex> | ||
|} | |} | ||
+ | |||
+ | {{Теорема | ||
+ | |statement= | ||
+ | Для любого префиксного кода <tex>C</tex>, отображающего произвольный алфавит <tex>A</tex> на двоичный алфавит <tex> \{0,1\} </tex> , длины кодовых слов должны удовлетворять неравенству: | ||
+ | |||
+ | <center><tex> \sum\limits_{i = 1}^{I} 2^{-l_i} \le 1 , </tex></center> | ||
+ | где <tex>|A| = I</tex> , а <tex>l_i</tex> {{---}} длины кодовых слов. | ||
+ | |||
+ | |proof= | ||
+ | |||
+ | Рассмотрим отрезок <tex>[0;1]</tex> на числовой прямой. | ||
+ | |||
+ | Разделим его пополам, причем левую половину обозначим <tex>M_0</tex>, а правую <tex>M_1</tex>. | ||
+ | |||
+ | Затем поделим <tex>M_0</tex> пополам и обозначим его левую половину <tex>M_{00}</tex>, а правую <tex>M_{01}</tex>, и, проделав то же самое с <tex>M_1</tex>, получим <tex>M_{10}</tex>, а левую <tex>M_{11}</tex>. | ||
+ | |||
+ | Будем выполнять эти действия, пока длина индекса полученного отрезка <tex>M_j</tex> не превосходит <tex>max(l_1, l_2,\ldots,l_I)</tex>. | ||
+ | |||
+ | Заметим, что: | ||
+ | *любому кодовому слову <tex>C_j</tex> сопоставлен свой отрезок <tex>M_{C_j}</tex> (Например, кодовому слову <tex>1011</tex> соответствует отрезок <tex>M_{1011}</tex>); | ||
+ | *длина отрезка <tex>M_{C_i}</tex> равна <tex>2^{-l_i}</tex> (Например, <tex>M_0</tex> имеет длину <tex>\frac12</tex>, а <tex>M_{00}</tex> соответственно <tex>\frac14</tex>); | ||
+ | *Если кодовое слово <tex>x</tex> является префиксом кодового слова <tex>y</tex>, то отрезок <tex>M_x</tex> содержит <tex>M_y</tex> (Например, кодовое слово <tex>01</tex> является префиксом <tex>0111</tex>, а отрезок<tex>M_{01}</tex> содержит <tex>M_{0111}</tex>, это его самая правая четверть); | ||
+ | |||
+ | Рассмотрим префиксный код <tex>C</tex>: так как ни одно из кодовых слов не является префиксом никакого другого кодового слова, то никакие два отрезка не пересекаются. | ||
+ | |||
+ | Если на отрезке <tex>[0;1]</tex> выбрать некоторое количество непересекающихся отрезков, то очевидно, что сумма их длин не превзойдет <tex>1</tex>, то есть <tex> \sum\limits_{i = 1}^{I} M_{C_i} \le 1</tex>. | ||
+ | |||
+ | Отсюда следует, что <tex>\sum\limits_{i = 1}^{I} M_{C_i} = \sum\limits_{i = 1}^{I} 2^{-l_i} \le 1</tex>. | ||
+ | }} | ||
+ | |||
{{Теорема 1 | {{Теорема 1 |
Версия 01:03, 24 октября 2013
Содержание
Определение
Преобразование Барроуза — Уилера — алгоритм, используемый для предварительной обработки данных перед сжатием, разработанный для улучшения эффективности последующего кодирования. Преобразование Барроуза — Уилера меняет порядок символов во входной строке таким образом, что повторяющиеся подстроки образуют на выходе идущие подряд последовательности одинаковых символов.
Описание алгоритма
Преобразование выполняется в три этапа.
- Cоставляется таблица всех циклических сдвигов входной строки.
- Производится лексикографическая (в алфавитном порядке) сортирова строк таблицы.
- В качестве выходной строки выбрается последний столбец таблицы преобразования и номер строки, совпадающей с исходной.
Пример работы алгоритма
Пусть нам дана исходная строка
"ABACABA".Трансформация | |||
---|---|---|---|
Вход | Все Перестановки |
Сортировка Строк |
Выход |
ABACABA |
ABACABA BACABAA ACABAAB CABAABA ABAABAC BAABACA AABACAB |
AABACAB ABAABAC ABACABA ACABAAB BAABACA BACABAA CABAABA |
BCABAAA |
Результат можно записать так:
("BCABAAA", 3), где 3 — это номер исходной строки в отсортированной матрице, так как он нужен для обратного преобразования.
Следует заметить, что иногда в исходной строке приводится, так называемый, символ конца строки $, который в преобразовании будет считаться последним (максимальным) символом, тогда сохранение номера исходной строки не требуется.
Пусть нам дана исходная строка "ABACABA$".
Трансформация | |||
---|---|---|---|
Вход | Все Перестановки |
Сортировка Строк |
Выход |
ABACABA$ |
ABACABA$ BACABA$A ACABA$AB CABA$ABA ABA$ABAC BA$ABACA A$ABACAB $ABACABA |
ABACABA$ ABA$ABAC ACABA$AB A$ABACAB BACABA$A BA$ABACA CABA$ABA $ABACABA |
$CBBAAAA |
При аналогичном вышеприведённом преобразовании та строчка в матрице, которая будет заканчиваться на символ конца строки и будет исходной: ("ABACABA$"). Тогда результат можно записать так:
"$CBBAAAA".Обратное преобразование
Наивный алгоритм
Пусть нам дано:
("BCABAAA", 3). Тогда выпишем в столбик нашу преобразованную последовательность символов "BCABAAA". Запишем её как последний столбик предыдущей матрицы (при прямом преобразовании Барроуза — Уилера), при этом все предыдущие столбцы оставляем пустыми. Далее построчно отсортируем матрицу, затем в предыдущий столбец запишем "BCABAAA". Опять построчно отсортируем матрицу. Продолжая таким образом, можно восстановить полный список всех циклических перестановок строки, которую нам надо найти. Выстроив полный отсортированный список перестановок, выберем строку с номером, который нам был изначально дан. В итоге мы получим искомую строку. Алгоритм обратного преобразования описан в таблице ниже:Обратное преобразование | |||||||
---|---|---|---|---|---|---|---|
Вход | |||||||
BCABAAA | |||||||
Добавление 1 | Сортировка 1 | Добавление 2 | Сортировка 2 | Добавление 3 | Сортировка 3 | Добавление 4 | |
B C A B A A A |
A A A A B B C |
BA CA AA BA AB AB AC |
AA AB AB AC BA BA CA |
BAA CAB AAB BAC ABA ABA ACA |
AAB ABA ABA ACA BAA BAC CAB |
BAAB CABA AABA BACA ABAA ABAC ACAB | |
Сортировка 4 | Добавление 5 | Сортировка 5 | Добавление 6 | Сортировка 6 | Добавление 7 | Сортировка 7 | |
AABA ABAA ABAC ACAB BAAB BACA CABA |
BAABA CABAA AABAC BACAB ABAAB ABACA ACABA |
AABAC ABAAB ABACA ACABA BAABA BACAB CABAA |
BAABAC CABAAB AABACA BACABA ABAABA ABACAB ACABAA |
AABACA ABAABA ABACAB ACABAA BAABAC BACABA CABAAB |
BAABACA CABAABA AABACAB BACABAA ABAABAC ABACABA ACABAAB |
AABACAB ABAABAC ABACABA ACABAAB BAABACA BACABAA CABAABA | |
Результат | |||||||
ABACABA |
Следует также заметить, что если нам было бы дано
"$CBBAAAA", то мы также получили бы нашу исходную строку, только с символом конца строки $ на конце: ABACABA$.Как несложно посчитать сложность данного алгоритма
, также он требует памяти.Оптимизация
Однако, данный алгоритм можно оптимизировать. Заметим, что при каждом проявлении неизвестного столбца выполнялись одни и те же действия. Мы приписывали новый столбец и сортировали имеющиеся данные. На каждом шагу мы к строке, которая находилась на
-ом месте приписываем в начало -ый элемент столбца входных данных. Пусть изначально мы знаем каким по порядку является приписанный нами в начало символ (то есть каким по порядку в столбце). И конечно же мы знаем исходя из предыдущего шага какое место занимала наша строка без этого первого символа ( -ое). Тогда несложно заметить, что при выполнении такой операции строка с номером всегда будет перемещаться на позицию с номером .0 | а | р | 9 | |
1 | а | д | 7 | |
2 | а | a | 0 | |
3 | а | к | 8 | |
4 | а | р | 10 | |
5 | б | a | 1 | |
6 | б | a | 2 | |
7 | д | a | 3 | |
8 | к | a | 4 | |
9 | р | б | 5 | |
10 | р | б | 6 |
Здесь слева это отсортированный данный столбец, чтобы мы знали какое место в лексикографическом порядке занимает приписываемый нами символ среди всех элементов данного нам изначально столбца. Справа - изначально данный столбец и соответствующее ему число. Поскольку мы в нашем алгоритме новый столбец приписываем в начало, то мы из состояния
(левый столбец) переходим в состояние (правый). Для того, чтобы восстановить строку, нам необходимо от последней такой цифры по пути из в восстановить строку.
Сложность оптимизированного алгоритмаДанный алгоритм работает за действий и требует памяти. Однако, если размер алфавита не очень большой, то для выяснения первого столбца матрицы можно использовать сортировку подсчетом, в этом случае алгоритм работает за действий и требует памяти, где — размер алфавита.Псевдокод оптимизированного алгоритмаПусть — количество символов во входной строке, — количество символов в алфавите, — номер исходной строки в матрице перестановок, — входящая строка, — массив для сортировки подсчетом, — вектор обратного преобразования, — номер данной нам строки в таблице.// Cчитаем частоты символов for i = 0 .. M count[i] = 0 for i = 0 .. N count[s[i]]++ // Упорядочиваем символы, чтобы получить первый столбец исходной матрицы // count[i] указывает на первую позицию символа i в первом столбце sum = 0 for i = 0 .. M sum = sum + count[i] count[i] = sum - count[i] // Cоздаем вектор обратного преобразования for i = 0 .. N t[count[s[i]]] = i count[s[i]]++ // И восстанавливаем исходный текст j = t[x] for i = 0 .. N print(s[j]) j = t[j] Доказательство корректностиПусть текст состоит из символов, занумерованных с нуля: . Буквы принадлежат некоторому алфавиту . Лексикографический порядок (строгий) на строках из алфавита будем обозначать . Обозначим через циклический сдвиг текста на символов влево:Существует перестановка чисел , которая удовлетворяет условию:Преобразование Барроуза-Уилера текста есть текст , буквы которого заданы соотношением:
Дополнительно
Ссылки |