Преобразование MTF — различия между версиями
Строка 7: | Строка 7: | ||
Изначально каждое возможное значение байта записывается в список (алфавит), в ячейку с номером, равным значению байта, т.е. (0, 1, 2, 3, …, 255). В процессе обработки данных этот список изменяется. По мере поступления очередного символа на выход подается номер элемента, содержащего его значение. После чего этот символ перемещается в начало списка, смещая остальные элементы вправо. | Изначально каждое возможное значение байта записывается в список (алфавит), в ячейку с номером, равным значению байта, т.е. (0, 1, 2, 3, …, 255). В процессе обработки данных этот список изменяется. По мере поступления очередного символа на выход подается номер элемента, содержащего его значение. После чего этот символ перемещается в начало списка, смещая остальные элементы вправо. | ||
− | Современные алгоритмы (например, [http://ru.wikipedia.org/wiki/Bzip2 bzip2]) перед алгоритмом MTF используют [[преобразование Барроуза-Уиллера|алгоритм BWT]], поэтому в качестве примера рассмотрим строку <tex> | + | Современные алгоритмы (например, [http://ru.wikipedia.org/wiki/Bzip2 bzip2]) перед алгоритмом MTF используют [[преобразование Барроуза-Уиллера|алгоритм BWT]], поэтому в качестве примера рассмотрим строку <tex>\mathtt{S} = </tex>''"BCABAAA"'', полученную из строки ''"ABACABA"'' в результате [[Преобразование Барроуза-Уиллера|преобразования Барроуза-Уиллера]]. Первый символ строки <tex>\mathtt{S}</tex> 'B' является вторым элементом алфавита ''"ABC"'', поэтому на вывод подаётся <tex>1</tex>. После перемещения 'B' в начало алфавита тот принимает вид ''"BAC"''. Дальнейшая работа алгоритма показана в таблице: |
{| class="wikitable" | {| class="wikitable" | ||
Строка 27: | Строка 27: | ||
|} | |} | ||
− | Таким образом, результат работы алгоритма: <tex>MTF( | + | Таким образом, результат работы алгоритма: <tex>MTF(\mathtt{S}) = </tex> ''"1222100"''. |
− | Данный алгоритм работает за <tex>O(N \cdot M)</tex>, где <tex>N</tex> {{---}} размер алфавита, <tex>M</tex> {{---}} длина строки, что не очень быстро. Этот алгоритм можно реализовать за <tex>O(N | + | Данный алгоритм работает за <tex>O(\mathtt{N} \cdot \mathtt{M})</tex>, где <tex>\mathtt{N}</tex> {{---}} размер алфавита, <tex>\mathtt{M}</tex> {{---}} длина строки, что не очень быстро. Этот алгоритм можно реализовать за <tex>O(\mathtt{N}\log(\mathtt{N+M}))</tex>. |
== Описание алгоритма за O(N log(N+M)) == | == Описание алгоритма за O(N log(N+M)) == | ||
Строка 35: | Строка 35: | ||
=== Идея === | === Идея === | ||
− | Пусть дан алфавит размером <tex>M</tex> и строка <tex>S</tex> длиной <tex>N</tex>. Заведем массив <tex>used[1..N+M]</tex> и последние <tex>M</tex> ячеек заполним единицами. Запомним для каждого символа алфавита позицию в нашем массиве. Например, <tex>alphabet['a'] = N+1</tex>, <tex>alphabet['b'] = N+2</tex>, ... , <tex>alphabet['z'] = N+M</tex>. | + | Пусть дан алфавит размером <tex>\mathtt{M}</tex> и строка <tex>\mathtt{S}</tex> длиной <tex>\mathtt{N}</tex>. Заведем массив <tex>\mathtt{used}[1..\mathtt{N+M}]</tex> и последние <tex>\mathtt{M}</tex> ячеек заполним единицами. Запомним для каждого символа алфавита позицию в нашем массиве. Например, <tex>\mathtt{alphabet}['a'] = \mathtt{N}+1</tex>, <tex>\mathtt{alphabet}['b'] = \mathtt{N}+2</tex>, ... , <tex>\mathtt{alphabet}['z'] = \mathtt{N+M}</tex>. |
− | При обработке <tex>i</tex>-го символа посчитаем и выпишем сумму на отрезке <tex>[1, alphabet[S[i]] - 1]</tex>, поменяем значения ячеек <tex>used[N-i+1]</tex> и <tex>used[alphabet[S[i]]]</tex> местами, также стоит поменять значение в ячейке <tex>alphabet[S[i]]</tex> на <tex>N-i+1</tex>. | + | При обработке <tex>\mathtt{i}</tex>-го символа посчитаем и выпишем сумму на отрезке <tex>[1, \mathtt{alphabet}[\mathtt{S}[\mathtt{i}]] - 1]</tex>, поменяем значения ячеек <tex>\mathtt{used}[\mathtt{N-i}+1]</tex> и <tex>\mathtt{used}[\mathtt{alphabet}[\mathtt{S}[\mathtt{i}]]]</tex> местами, также стоит поменять значение в ячейке <tex>\mathtt{alphabet}[\mathtt{S}[\mathtt{i}]]</tex> на <tex>\mathtt{N-i}+1</tex>. |
− | Чтобы добиться сложности алгоритма <tex>O(N</tex><tex>\log</tex><tex>(N+M))</tex> нужно использовать дерево отрезков или подобное. | + | Чтобы добиться сложности алгоритма <tex>O(\mathtt{N}</tex><tex>\log</tex><tex>(\mathtt{N+M}))</tex> нужно использовать дерево отрезков или подобное. |
=== Псевдокод === | === Псевдокод === | ||
Строка 58: | Строка 58: | ||
== Обратное преобразование == | == Обратное преобразование == | ||
− | Пусть даны строка <tex>s = </tex>''"1222100"'' и исходный алфавит ''"ABC"''. Символ с номером 1 в алфавите {{---}} это 'B'. На вывод подаётся 'B', и этот символ перемещается в начало алфавита. Символ с номером 2 в алфавите {{---}} это 'A', поэтому 'A' подается на вывод и перемещается в начало алфавита. Дальнейшее преобразование происходит аналогично. | + | Пусть даны строка <tex>\mathtt{s} = </tex>''"1222100"'' и исходный алфавит ''"ABC"''. Символ с номером <tex>1</tex> в алфавите {{---}} это 'B'. На вывод подаётся 'B', и этот символ перемещается в начало алфавита. Символ с номером <tex>2</tex> в алфавите {{---}} это 'A', поэтому 'A' подается на вывод и перемещается в начало алфавита. Дальнейшее преобразование происходит аналогично. |
{| class ="wikitable" | {| class ="wikitable" | ||
Строка 78: | Строка 78: | ||
|} | |} | ||
− | Значит, исходная строка <tex>MTF^{-1}( | + | Значит, исходная строка <tex>MTF^{-1}(\mathtt{S}) = </tex>''"BCABAAA"''. |
== Применение == | == Применение == | ||
Строка 84: | Строка 84: | ||
Этот метод позволяет легко преобразовать данные, насыщенные длинными повторами разных символов в блок данных, самыми частыми символами которого будут нули. Без MTF нас подстерегают разного рода трудности в решении проблемы адаптации к данным, поскольку в разных местах данных, полученных на выходе [[Преобразование Барроуза-Уиллера|BWT-преобразования]], разные символы являются преобладающими. Зачастую мы встречаемся с последовательностями типа "bbbbbcccccdddddaaaaa". | Этот метод позволяет легко преобразовать данные, насыщенные длинными повторами разных символов в блок данных, самыми частыми символами которого будут нули. Без MTF нас подстерегают разного рода трудности в решении проблемы адаптации к данным, поскольку в разных местах данных, полученных на выходе [[Преобразование Барроуза-Уиллера|BWT-преобразования]], разные символы являются преобладающими. Зачастую мы встречаемся с последовательностями типа "bbbbbcccccdddddaaaaa". | ||
− | Попробуем сжать эту последовательность при помощи, например, [[Алгоритм Хаффмана|метода Хаффмана]]. Вероятности всех четырех символов в данном примере равны 1/4. Легко посчитать, что в результате кодирования мы получим последовательность длиной <tex>20\cdot2 = 40</tex> бит. | + | Попробуем сжать эту последовательность при помощи, например, [[Алгоритм Хаффмана|метода Хаффмана]]. Вероятности всех четырех символов в данном примере равны <tex>1/4</tex>. Легко посчитать, что в результате кодирования мы получим последовательность длиной <tex>20\cdot2 = 40</tex> бит. |
Теперь проделаем то же самое со строкой, подвергнутой MTF-преобразованию (предположим, начальный алфавит выглядит как ''"abcd"''). | Теперь проделаем то же самое со строкой, подвергнутой MTF-преобразованию (предположим, начальный алфавит выглядит как ''"abcd"''). |
Версия 15:56, 15 января 2015
Определение: |
Преобразование MTF (англ. move-to-front, движение к началу) — алгоритм кодирования, используемый для предварительной обработки данных (обычно потока байтов) перед сжатием, разработанный для улучшения эффективности последующего кодирования. |
Содержание
Описание алгоритма
Изначально каждое возможное значение байта записывается в список (алфавит), в ячейку с номером, равным значению байта, т.е. (0, 1, 2, 3, …, 255). В процессе обработки данных этот список изменяется. По мере поступления очередного символа на выход подается номер элемента, содержащего его значение. После чего этот символ перемещается в начало списка, смещая остальные элементы вправо.
Современные алгоритмы (например, bzip2) перед алгоритмом MTF используют алгоритм BWT, поэтому в качестве примера рассмотрим строку "BCABAAA", полученную из строки "ABACABA" в результате преобразования Барроуза-Уиллера. Первый символ строки 'B' является вторым элементом алфавита "ABC", поэтому на вывод подаётся . После перемещения 'B' в начало алфавита тот принимает вид "BAC". Дальнейшая работа алгоритма показана в таблице:
Символ | Список | Вывод |
---|---|---|
B | ABC | 1 |
C | BAC | 2 |
A | CBA | 2 |
B | ACB | 2 |
A | BAC | 1 |
A | ABC | 0 |
A | ABC | 0 |
Таким образом, результат работы алгоритма:
"1222100".Данный алгоритм работает за
, где — размер алфавита, — длина строки, что не очень быстро. Этот алгоритм можно реализовать за .Описание алгоритма за O(N log(N+M))
Идея
Пусть дан алфавит размером
и строка длиной . Заведем массив и последние ячеек заполним единицами. Запомним для каждого символа алфавита позицию в нашем массиве. Например, , , ... , .При обработке
-го символа посчитаем и выпишем сумму на отрезке , поменяем значения ячеек и местами, также стоит поменять значение в ячейке на .Чтобы добиться сложности алгоритма
нужно использовать дерево отрезков или подобное.Псевдокод
list<int> mtf(N): list<int> result(N) list<int> used(N+M) for i = 1 to M //Заполняем последние M ячеек единицами used[i+N] = 1 for i = 1 to N result.append(sum(1, alphabet[S[i]] - 1)) //Запоминаем ответ swap(used[N-i+1], used[alphabet[S[i]]]) //Меняем значения alphabet[S[i]] = N-i+1 //Изменяем позицию символа в массиве return result
Обратное преобразование
Пусть даны строка
"1222100" и исходный алфавит "ABC". Символ с номером в алфавите — это 'B'. На вывод подаётся 'B', и этот символ перемещается в начало алфавита. Символ с номером в алфавите — это 'A', поэтому 'A' подается на вывод и перемещается в начало алфавита. Дальнейшее преобразование происходит аналогично.Символ | Список | Вывод |
---|---|---|
1 | ABC | B |
2 | BAC | C |
2 | CBA | A |
2 | ACB | B |
1 | BAC | A |
0 | ABC | A |
0 | ABC | A |
Значит, исходная строка
"BCABAAA".Применение
Этот метод позволяет легко преобразовать данные, насыщенные длинными повторами разных символов в блок данных, самыми частыми символами которого будут нули. Без MTF нас подстерегают разного рода трудности в решении проблемы адаптации к данным, поскольку в разных местах данных, полученных на выходе BWT-преобразования, разные символы являются преобладающими. Зачастую мы встречаемся с последовательностями типа "bbbbbcccccdddddaaaaa".
Попробуем сжать эту последовательность при помощи, например, метода Хаффмана. Вероятности всех четырех символов в данном примере равны . Легко посчитать, что в результате кодирования мы получим последовательность длиной бит.
Теперь проделаем то же самое со строкой, подвергнутой MTF-преобразованию (предположим, начальный алфавит выглядит как "abcd").
"bbbbbcccccdddddaaaaa" — исходная строка
"10000200003000030000" — строка после MTF
Символ | Частота | Вероятность | Код Хаффмана |
---|---|---|---|
0 | 16 | 4/5 | 0 |
1 | 2 | 1/10 | 10 |
2 | 1 | 1/20 | 110 |
3 | 1 | 1/20 | 111 |
В результате сжатия получаем последовательность длиной арифметического кодирования для данного примера будет еще значительней.
бит. Стоит заметить, что выигрыш от применения