Изменения

Перейти к: навигация, поиск

Алгоритм LZW

5595 байт добавлено, 19:17, 4 сентября 2022
м
rollbackEdits.php mass rollback
Опубликование алгоритма LZW произвело большое впечатление на всех специалистов по сжатию информации. За этим последовало большое количество программ и приложений с различными вариантами этого метода.
 Этот метод позволяет достичь одну из наилучших степеней сжатия среди других существующих методов сжатия графических данных, при полном отсутствии потерь или искажений в исходных файлах. В настоящее время испольуется используется в файлах формата TIFF, PDF, GIF, PostScript и других, а также отчасти во многих популярных программах сжатия данных (ZIP, ARJ, LHA). 
== Описание ==
Процесс сжатия выглядит следующим образом. Последовательно : последовательно считываются символы входного потока и происходит проверка, существует ли в созданной таблице строк такая строка. Если такая строка существует, считывается следующий символ, а если строка не существует, в поток заносится код для предыдущей найденной строки, строка заносится в таблицу, а поиск начинается снова. <br>
Например, если сжимают байтовые данные (текст), то строк в таблице окажется <tex>256 </tex> (от <tex>"0" </tex> до <tex>"255"</tex>). Если используется <tex>10</tex>-битный код, то под коды для строк остаются значения в диапазоне от <tex>256 </tex> до <tex>1023</tex>. Новые строки формируют таблицу последовательно, т. е. можно считать индекс строки ее кодом. <br>
Алгоритму Для декодирования на входе требуется вход подается только закодированный текст, поскольку он алгоритм LZW может воссоздать соответствующую таблицу преобразования непосредственно по закодированному тексту.
Алгоритм генерирует однозначно декодируемый код за счет того, что каждый раз, когда генерируется новый код, новая строка добавляется в таблицу строк. LZW постоянно проверяет, является ли строка уже известной, и, если так, выводит существующий код без генерации нового.
Таким образом, каждая строка будет храниться в единственном экземпляре и иметь свой уникальный номер. Следовательно, при дешифровании при получении декодировании во время получения нового кода генерируется новая строка, а при получении уже известного, строка ивлекается извлекается из словаря.
== Алгоритм ==
=== Кодирование ===
# Инициализация словаря всеми возможными односимвольными фразами* Начало. Инициализация входной фразы * ''' Шаг 1. ''' Все возможные символы заносятся в словарь. Во входную фразу <tex>X первым символом </tex> заносится первый символ сообщения.# * ''' Шаг 2. ''' Считать очередной символ <tex>Y </tex> из кодируемого сообщения.# * ''' Шаг 3. ''' Если КОНЕЦ_СООБЩЕНИЯ<tex>Y</tex> {{---}} это символ конца сообщения, то выдать код для <tex>X</tex>, иначе: ** Если фраза <tex>XY </tex> уже есть имеется в словаре, то присвоить входной фразе значение <tex>XY </tex> и перейти к ''' Шагу 2''', иначе ** Иначе выдать код для входной фразы <tex>X</tex>, добавить <tex>XY </tex> в словарь, и присвоить входной фразе значение <tex>Y и перейти </tex>. Перейти к ''' Шагу 2. '''* Конец.
=== Декодирование ===
# Инициализация словаря всеми возможными односимвольными фразами* Начало. Инициализация входной фразы * ''' Шаг 1. ''' Все возможные символы заносятся в словарь. Во входную фразу <tex>X первым кодом </tex> заносится первый код декодируемого сообщения.# * ''' Шаг 2. ''' Считать очередной код <tex>Y </tex> из декодируемого сообщения.# * ''' Шаг 3. ''' Если КОНЕЦ_СООБЩЕНИЯ<tex>Y</tex> {{---}} это конец сообщения, то выдать символ для кода , соответствующий коду <tex>X</tex>, иначе: ** Если фразы под кодом <tex>XY </tex> нет в словаре, вывести фразу с кодом , соответствующую коду <tex>X</tex>, а фразу с кодом <tex>XY </tex> занести в словарь. ** Иначе присвоить входной фразе код <tex>XY </tex> и перейти к шагу ''' Шагу 2'''.* Конец.
== Пример ==
Рассмотрим пример сжатия и декодирования изображениясообщения.  Сначала создадим начальный словарь единичных символов. В стандартной кодировке ASCII имеется <tex>256 </tex> различных символов, поэтому, для того, чтобы все они были корректно закодированы (если нам неизвестно, какие символы будут присутствовать в исходном файле, а какие - нет), начальный размер кода будет равен <tex>8 </tex> битам. Если нам заранее известно, что в исходном файле будет меньшее количество различных символов, то вполне разумно уменьшить количество бит. Чтобы инициализировать таблицу, мы установим соответствие кода <tex>0 </tex> соответствующему символу с битовым кодом <tex>00000000</tex>, тогда <tex>1</tex>соответствует символу с кодом <tex>00000001</tex>, и т.д., до кода <tex>255. На самом деле мы указали, что каждый код от 0 до 255 является корневым</tex>.
{| class="wikitable" border = 1, style="float:right; text-align: right; margin-left: auto; margin-right: auto;"
|- bgcolor=#EEEEEE
! Символ !! Битовый код!! Код
|-
| a || 000 || 000
|-
| b || 001 || 001
|-
| c || 010|| 2
|-
| d || 011|| 3
|-
| e || 100|| 4
|}
Больше в таблице не будет других кодов, обладающих этим свойством.<br>
По мере роста словаря, размер групп должен расти, с тем, чтобы учесть новые элементы. <tex>8</tex>-битные группы дают <tex>256 </tex> возможных комбинации бит, поэтому, когда в словаре появится <tex>256</tex>-е слово, алгоритм должен перейти к <tex>9</tex>-битным группам. При появлении <tex>512</tex>-ого слова произойдет переход к <tex>10</tex>-битным группам, что дает возможность запоминать уже <tex>1024 </tex> слова и т.д.  В нашем примере алгоритму заранее известно о том, что будет использоваться всего 5 различных символов, следовательно, для их хранения будет использоваться минимальное количество бит, позволяющее нам их запомнить, то есть 3 ( 8 различных комбинаций ). 
В нашем примере алгоритму заранее известно о том, что будет использоваться всего <tex>5</tex> различных символов, следовательно, для их хранения будет использоваться минимальное количество бит, позволяющее нам их запомнить, то есть <tex>3</tex> (<tex>8</tex> различных комбинаций).
=== Кодирование ===
Пусть мы сжимаем последовательность "<tex>abacabadabacabae"</tex>.
* '''Шаг 1: '''Тогда, согласно изложенному выше алгоритму, мы добавим к изначально пустой строке “a” <tex>a</tex> и проверим, есть ли строка “a” <tex>a</tex> в таблице. Поскольку мы при инициализации занесли в таблицу все строки из одного символа, то строка “a” <tex>a</tex> есть в таблице. * '''Шаг 2: '''Далее мы читаем следующий символ "<tex>b" </tex> из входного потока и проверяем, есть ли строка “ab” <tex>ab</tex> в таблице. Такой строки в таблице пока нет.Добавляем в таблицу <5tex>\langle5\rangle</tex> <tex>ab</tex> “ab”. В поток: <0tex>\langle0\rangle</tex>;* '''Шаг 3: '''“ba” <tex>ba</tex> — нет. В таблицу: <6tex>\langle6\rangle</tex> <tex>ba</tex> “ba”. В поток: <1tex>\langle1\rangle</tex>;* '''Шаг 4: '''“ac” <tex>ac</tex> — нет. В таблицу: <7tex>\langle7\rangle</tex> <tex>ac</tex> “ac”. В поток: <0tex>\langle0\rangle</tex>;* '''Шаг 5: '''“ca” <tex>ca</tex> — нет. В таблицу: <8tex>\langle8\rangle</tex> <tex>ca</tex> “ca”. В поток: <2tex>\langle2\rangle</tex>;* '''Шаг 6: '''“ab” <tex>ab</tex> — есть в таблице; “aba” <tex>aba</tex> — нет. В таблицу: <9tex>\langle9\rangle</tex> <tex>aba</tex> “aba”. В поток: <5tex>\langle5\rangle</tex>;* '''Шаг 7: '''“ad” <tex>ad</tex> — нет. В таблицу: <10tex>\langle10\rangle</tex> <tex>ad</tex> “ad”. В поток: <0tex>\langle0\rangle</tex>;* '''Шаг 8: '''“da” <tex>da</tex> — нет. В таблицу: <11tex>\langle11\rangle</tex> <tex>da</tex> “da”. В поток: <3tex>\langle3\rangle</tex>;* '''Шаг 9: '''“aba” <tex>aba</tex> — есть в таблице; “abac” <tex>abac</tex> — нет. В таблицу: <12tex>\langle12\rangle</tex> <tex>abac</tex> “abac”. В поток: <9tex>\langle9\rangle</tex>;* '''Шаг 10: '''“ca” <tex>ca</tex> — есть в таблице; “cab” <tex>cab</tex> — нет. В таблицу: <13tex>\langle13\rangle</tex> <tex>cab</tex> “cab”. В поток: <8tex>\langle8\rangle</tex>;* '''Шаг 11: '''“ba” <tex>ba</tex> — есть в таблице; “bae” <tex>bae</tex> — нет. В таблицу: <14tex>\langle14\rangle</tex> <tex>bae</tex> “bae”. В поток: <6tex>\langle6\rangle</tex>;* '''Шаг 12: '''И, наконец последняя строка “e”<tex>e</tex>, за ней идет конец сообщения, поэтому мы просто выводим в поток <4tex>\langle4\rangle</tex>.
{| class="wikitable" border =1, style="text-align: center; margin-left: auto; margin-right: auto;"
| style="text-align: center;" | b
| style="text-align: center;" | a
| 5 || 1010101
| style="border-right: none;" | 9:
| style="border-left: none;" | aba
| style="text-align: center;" | a
| style="text-align: center;" | d
| 0 || 0000000
| style="border-right: none;" | 10:
| style="border-left: none;" | ad
| style="text-align: center;" | d
| style="text-align: center;" | a
| 3 || 0110011
| style="border-right: none;" | 11:
| style="border-left: none;" | da
|}
Итак, мы получаем закодированное сообщение "<tex>0 1 0 2 5 0 3 9 8 6 4"</tex> и его битовый эквивалент <tex>000 001 000 010 0101 0000 0011 1001 1000 0110 0100</tex>. Каждый символ исходного сообщения был закодирован группой из трех бит, сообщение содержало <tex>16 </tex> символов, следовательно длина сообщения составляла <tex>3 * \cdot 16 = 48 </tex> бит.
Закодированное же сообщение так же сначала кодировалось трехбитными группами, а про при появлении в словаре восьмого слова - четырехбитными, итого длина сообщения составила 7 * <tex>4 \cdot 3 + 4 * 7 \cdot 4 = 37 40</tex> бит, что на 11 <tex>8</tex> бит короче исходного.
=== Декодирование ===
Особенность LZW заключается в том, что для декомпрессии нам не надо сохранять таблицу строк в файл для распаковки. Алгоритм построен таким образом, что мы в состоянии восстановить таблицу строк, пользуясь только потоком кодов.
Теперь представим ,что мы получили закодированное сообщение, приведённое выше, и нам нужно его декодировать. Прежде всего, нам нужно знать начальный словарь, а последующие записи словаря мы можем реконструировать уже на ходу, поскольку они являются просто конкатенацией предыдущих записей. Кроме того, в процессе кодировании и декодировании коды в словарь добавляются во время обработки одного и того же символа, т.е. это происходит “синхронно”.
| style="border-left: none;" | c?
|-
| 101 0101 || 5
| style="text-align: center;" | ab
| style="border-right: none;" | 8:
| style="border-left: none;" | ab?
|-
| 000 0000 || 0
| style="text-align: center;" | a
| style="border-right: none;" | 9:
| style="border-left: none;" | a?
|-
| 011 0011 || 3
| style="text-align: center;" | d
| style="border-right: none;" | 10:
=== Примечание ===
Для повышения степени сжатия изображений данным методом часто используется одна «хитрость» “хитрость” реализации этого алгоритма. Некоторые файлы, подвергаемые сжатию с помощью LZW, имеют часто встречающиеся цепочки одинаковых символов, например aaaaaa … <tex>aaaaaaaaaaaaa... </tex> или 30, 30, 30 <tex>303030</tex> … и т. п. Их непосредственное сжатие будет генерировать выходной код 0, 0, 5 и т<tex>005000600007...д</tex>. Спрашивается, можно ли в этом частном случае повысить степень сжатия?
Оказывается, это возможно, если оговорить некоторые действия:
 
[[Файл:LZW-img.jpg|right|Работа алгоритма LZW]]
Мы знаем, что для каждого кода надо добавлять в таблицу строку, состоящую из уже присутствующей там строки и символа, с которого начинается следующая строка в потоке.
*''' ''' Пусть словарь состоит из слов : <tex>a, b, c, d, e</tex>. Будем кодировать строку <tex> aaaaaaaaaa </tex>
*''' ''' Итак, кодировщик заносит первую <tex>a</tex> в строку, ищет и находит <tex>a</tex> в словаре под номером <tex>\langle0\rangle</tex>. Добавляет в строку следующую <tex>a</tex>, находит, что <tex>aa</tex> нет в словаре. Тогда он добавляет запись <tex>\langle5\rangle</tex>: <tex>aa</tex> в словарь и выводит метку <tex>\langle0\rangle</tex> (<tex>a</tex>) в выходной поток.
*''' '''Далее строка инициализируется второй <tex>a</tex>, то есть принимает вид <tex>a?</tex> вводится третья <tex>a</tex>, строка вновь равна <tex>aa</tex>, которая теперь имеется в словаре.
*''' '''Если появляется четвертая <tex>a</tex>, то строка <tex>aa?</tex> равна <tex>aaa</tex>, которой нет в словаре. Словарь пополняется этой строкой, а на выход идет метка <tex>\langle5\rangle</tex> (<tex>aa</tex>).
*''' '''После этого строка инициализируется третьей <tex>a</tex>, и т.д. и т.п. Дальнейший процесс вполне ясен.
[[Файл:LZW-img.jpg|center|Работа алгоритма LZW]]
{| class="wikitable" border = 1, style="text-align: center; margin-left: auto; margin-right: auto;"
|- bgcolor=#EEEEEE
! Слово !! Номер в словаре
|-
| a || <tex>\langle0\rangle</tex>
|-
| b || <tex>\langle1\rangle</tex>
|-
| c || <tex>\langle2\rangle</tex>
|-
| d || <tex>\langle3\rangle</tex>
|-
| e || <tex>\langle4\rangle</tex>
|}
{| class="wikitable" border =1, style="text-align: center; margin-left: auto; margin-right: auto;"
|- bgcolor =#EEEEEE
! scope="col" width="6em" rowspan="2" | Текущая строка
! scope="col" width="6em" rowspan="2" | Текущий символ
! scope="col" width="4em" rowspan="2" | Следующий символ
! colspan="2" | Вывод
! scope="col" width="7em" rowspan="2" colspan="2" | Словарь
|- bgcolor =#EEEEEE
! Код || Биты
|-
| style="text-align: center;" | aa
| style="text-align: center;" | a
| style="text-align: center;" | a
| 0 || 000
| style="border-right: none;" | 5:
| style="border-left: none;" | aa
|-
| style="text-align: center;" | aa
| style="text-align: center;" | a
| style="text-align: center;" | a
| - || -
| style="border-right: none;" | -
| style="border-left: none;" | -
|-
| style="text-align: center;" | aaa
| style="text-align: center;" | a
| style="text-align: center;" | a
| 5 || 101
| style="border-right: none;" | 6:
| style="border-left: none;" | aaa
|-
| style="text-align: center;" | a
| style="text-align: center;" | a
| style="text-align: center;" | a
| - || -
| style="border-right: none;" | -
| style="border-left: none;" | -
|-
| style="text-align: center;" | aa
| style="text-align: center;" | a
| style="text-align: center;" | a
| - || -
| style="border-right: none;" | -
| style="border-left: none;" | -
|-
| style="text-align: center;" | aaa
| style="text-align: center;" | a
| style="text-align: center;" | a
| - || -
| style="border-right: none;" | -
| style="border-left: none;" | -
|-
| style="text-align: center;" | aaaa
| style="text-align: center;" | a
| style="text-align: center;" | a
| 6 || 110
| style="border-right: none;" | 7:
| style="border-left: none;" | aaaa
|-
| style="text-align: center;" | a
| style="text-align: center;" | a
| style="text-align: center;" | a
| - || -
| style="border-right: none;" | -
| style="border-left: none;" | -
|-
| style="text-align: center;" | aa
| style="text-align: center;" | a
| style="text-align: center;" | a
| - || -
| style="border-right: none;" | -
| style="border-left: none;" | -
|-
| style="text-align: center;" | aaa
| style="text-align: center;" | a
| style="text-align: center;" | a
| - || -
| style="border-right: none;" | -
| style="border-left: none;" | -
|-
| style="text-align: center;" | aaaa
| style="text-align: center;" | a
| style="text-align: center;" | a
| 7 || 111
| style="border-right: none;" | 8:
| style="border-left: none;" | aaaaa
|}
*''' ''' ИтакВ результате на выходе получаем последовательность <tex>0567</tex>. При кодировании использовались только трехбитные группы. Длина закодированного сообщения составила <tex> 4 \cdot 3 = 12 </tex> бит, кодировщик заносит первую «а» в строкучто на <tex> 7 \cdot 3 - 12 = 9</tex> бит короче кодирования стандартным методом LZW.Можно показать, ищет и находит "а" в словаречто такая последовательность будет корректно восстановлена. Добавляет в строку следующую "а"Декодировщик сначала читает первый код – это <tex>\langle0\rangle</tex>, находит, что "аа" нет в словарекоторому соответствует символ <tex>a</tex>. Тогда он добавляет запись Затем читает код <5tex>: "аа" в словарь и выводит метку \langle5\rangle<0/tex> ("а") , но этого кода в выходной потокего таблице нет. *''' '''Далее строка инициализируется второй «а»Но мы уже знаем, что такая ситуация возможна только в том случае, когда добавляемый символ равен первому символу только что считанной последовательности, то есть принимает вид "<tex>a?" вводится третья «а», строка вновь равна "аа", которая теперь имеется </tex>. Поэтому он добавит в словаре. *''' '''Если появляется четвертая «а», то строка "свою таблицу строку <tex>aa?" равна "ааа"</tex> с кодом <tex>\langle5\rangle</tex>, которой нет а в словаре. Словарь пополняется этой строкой, а на выход идет метка выходной поток поместит <5tex> ("aa"). *''' '''После этого строка инициализируется третьей «а», и т.д. и т.п</tex>. Дальнейший процесс вполне ясенИ так может быть раскодирована вся цепочка кодов.
В результате на выходе получаем последовательность 0Мало того, 5описанное выше правило кодирования мы можем применять в общем случае не только к подряд идущим одинаковым символам, 6 …но и к последовательностям, которая короче прямого кодирования стандартным методом LZWу которых очередной добавляемый символ равен первому символу цепочки.
=== Преимущества алгоритма LZW ===
Можно показать, что такая последовательность будет корректно восстановлена* Алгоритм является однопроходным. Декодировщик сначала читает первый код – это <0>, которому соответствует символ "a". Затем читает код <5>, но этого кода в его таблице нет. Но мы уже знаем, что такая ситуация возможна только в том случае, когда добавляемый символ равен первому символу только что считанной последовательности, то есть "a". Поэтому он добавит в свою таблицу строку "aa" с кодом <5>, а в выходной поток поместит "aa". И так может быть раскодирована вся цепочка кодов. Мало того, описанное выше правило кодирования мы можем применять в общем случае не только к подряд идущим одинаковым символам, но и к последовательностям, у которых очередной добавляемый символ равен первому символу цепочки. == Достоинства и недостатки ==
+ * Для декомпрессии не надо сохранять таблицу строк в файл для распаковки. Алгоритм является однопроходнымпостроен таким образом, что мы в состоянии восстановить таблицу строк, пользуясь только потоком кодов.
+ Для декомпрессии не надо сохранять таблицу строк в файл для распаковки. Алгоритм построен таким образом, что мы в состоянии восстановить таблицу строк, пользуясь только потоком кодов.=== Недостатки алгоритма LZW ===
- * Алгоритм не проводит анализ входных данных.
==Источникиинформации==
* [http://ru.wikipedia.org/wiki/LZW Алгоритм Лемпеля%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%BC%D0%BF%D0%B5%D0%BB%D1%8F_%E2%80%94_%D0%97%D0%B8%D0%B2%D0%B0_%E2%80%94_%D0%92%D0%B5%D0%BB%D1%87%D0%B0 Википедия {{---}}ЗиваАлгоритм Лемпеля {{---}}Велча Зива {{---}} ВикипедияВелча]
* [http://en.wikipedia.org/wiki/LZW Lempel%E2%80%93Ziv%E2%80%93Welch Wikipedia {{---}}ZivLempel {{---}}Welch Ziv {{---}} WikipediaWelch]
* [http://compression.ru/download/articles/rev_univ/semenyuk_2001_econom_encoding.pdf Семенюк В.В. {{---}} Экономное кодирование дискретной информации]
* [http://algolist.manual.ru/compress/standard/lzw.php Метод LZW{{---}}сжатия данных {{---}} алгоритмы и методы]
* [http://www.compression-pointers.ru/category_42.html Алгоритмы сжатия и компрессии]
1632
правки

Навигация