fix: улучшил объяснения и исправил несколько опечаток
This commit is contained in:
@ -14,7 +14,7 @@ source:
|
|||||||
### Форматы для хранения графических данных
|
### Форматы для хранения графических данных
|
||||||
### Краткая характеристика наиболее распространенных растровых форматов
|
### Краткая характеристика наиболее распространенных растровых форматов
|
||||||
|
|
||||||
### Алгоритмы сжатия
|
### Алгоритмы сжатия без потерь
|
||||||
|
|
||||||
Неплохое видео: https://www.youtube.com/watch?v=CJFUN6BrkGE
|
Неплохое видео: https://www.youtube.com/watch?v=CJFUN6BrkGE
|
||||||
|
|
||||||
@ -41,15 +41,15 @@ source:
|
|||||||
|
|
||||||
Видно, что буква "о" упоминается существенно чаще, чем буквы вроде "ф" или многострадальной "ё", (к которой у русских большая нелюбовь из-за неудобства ее печатного набора).
|
Видно, что буква "о" упоминается существенно чаще, чем буквы вроде "ф" или многострадальной "ё", (к которой у русских большая нелюбовь из-за неудобства ее печатного набора).
|
||||||
|
|
||||||
Эта особенность, которая несколько веков портила жизнь людям и не давала зашифровать сообщение, можно обернуть и себе на пользу, а именно: логично использовать для шифрования буквы "о" символ с наименьшей длинной, чуть длиннее сделать "е", еще чуть длиннее "а" и так далее вплоть до "ё", которую зашифруем уже как получится.
|
Эта особенность, которая несколько веков портила жизнь людям и не давала зашифровать сообщение, но ее можно обернуть и себе на пользу: при шифровании текста логично использовать для шифрования буквы "о" символ с наименьшей длинной, чуть длиннее сделать "е", еще чуть длиннее "а" и так далее вплоть до "ё", которую зашифруем уже как получится.
|
||||||
|
|
||||||
Ну и вот перед нами знакомая задачка из ЕГЭ - подобрать коды для символов так, чтобы их можно было однозначно декодировать, а длина сообщения была наименьшей. Здесь же предлагаю вспомнить, что такое прямое условие Фано - ни одно кодовое слово не может быть началом другого кодового слова.
|
Ну и вот перед нами знакомая задачка из ЕГЭ - подобрать коды для символов так, чтобы их можно было однозначно декодировать, а длина сообщения была наименьшей. Здесь же предлагаю вспомнить, что такое прямое условие Фано - ни одно кодовое слово не может быть началом другого кодового слова. Это является условием, чтобы наши сжатые данные можно было однозначно декодировать
|
||||||
|
|
||||||
После этого мы алгоритмически реализуем подбор таких символов.
|
После этого мы алгоритмически реализуем подбор таких символов.
|
||||||
|
|
||||||
**У этого метода есть одна проблема технического характера**: частотности символов для языка в целом и для конкретного файла в частности могут весьма сильно различаться, а мы ведь этот алгоритм не только к текстам хотим применять (У нас-то в голове все с байтами работает). Построить такую таблицу - пол беды, это займет O(n), что вполне допустима даже для гигабайтных данных. А вот как ее при разжимании получить? В классической реализации алгоритма Хаффмана она хранится в начале файла, но она, как вы можете догадаться, сама прибавляет размера нашим данным
|
**У этого метода есть одна проблема технического характера**: частотности символов для языка в целом и для конкретного файла в частности могут весьма сильно различаться, а мы ведь этот алгоритм не только к текстам хотим применять (У нас-то в голове все с байтами работает). Построить таблицу частотностей для конкретного файла - пол беды, это займет O(n), что вполне допустима даже для гигабайтных данных. А вот как ее при разжимании получить? В классической реализации алгоритма Хаффмана она хранится в начале файла, но она, как вы можете догадаться, сама прибавляет размера нашим данным.
|
||||||
|
|
||||||
**У этой проблемы есть решение** - на практике мы можем не генерировать таблицу частотностей, а производить кодирование и построение дерева кодирования символов параллельно. Алгоритм Маслаковым не описан, просто знайте что он есть. Если кому интересно, он разбирался в этом [видео](https://youtu.be/CJFUN6BrkGE?si=AX_-n_2Hn1NQAD0k&t=571) (или если ютуб не грузит: https://vk.com/video-209186427_456239058 на 9:41). Этот алгоритм называется "адаптивный метод Хаффмана"
|
**У этой проблемы есть решение** - на практике мы можем не генерировать таблицу частотностей, а производить кодирование и построение дерева кодирования символов параллельно. Алгоритм Молодяковым не описан, просто знайте что он есть. Если кому интересно, он разбирался в этом [видео](https://youtu.be/CJFUN6BrkGE?si=AX_-n_2Hn1NQAD0k&t=571) (или если ютуб не грузит: https://vk.com/video-209186427_456239058 на 9:41). Этот алгоритм называется "адаптивный метод Хаффмана"
|
||||||
|
|
||||||
[^substitution-cipher]: Шифр простой подстановки - шифр, где каждому символу исходного алфавита (например русского), ставится в соответствии символ другого алфавита. Классический пример - замена одних букв на другие: а -> в, г -> а и т.д. И тогда сообщение "Бегите, глупцы" станет "Гжакфж, анхсшэ" (тут поменяны и другие буквы, можете попытаться прикинуть, какой алфавит тут использовался). Символом другого алфавита может быть и просто другой символ. Ярким примером может стать например шифр Стенфорда Пайнса из Гравити Фолз (кстати рекомендую попробовать его расшифровать, мне это доставило некоторое удовольствие)
|
[^substitution-cipher]: Шифр простой подстановки - шифр, где каждому символу исходного алфавита (например русского), ставится в соответствии символ другого алфавита. Классический пример - замена одних букв на другие: а -> в, г -> а и т.д. И тогда сообщение "Бегите, глупцы" станет "Гжакфж, анхсшэ" (тут поменяны и другие буквы, можете попытаться прикинуть, какой алфавит тут использовался). Символом другого алфавита может быть и просто другой символ. Ярким примером может стать например шифр Стенфорда Пайнса из Гравити Фолз (кстати рекомендую попробовать его расшифровать, мне это доставило некоторое удовольствие)
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ source:
|
|||||||
|
|
||||||
**Идея**: кодируемый текст мы помещаем в буффер и в процессе обработки смещаем его в словарь, а внутри словаря потом в процессе кодирования ищем совпадения. Если совпадение нашлось, мы просто указываем в словаре где это совпадение можно отыскать
|
**Идея**: кодируемый текст мы помещаем в буффер и в процессе обработки смещаем его в словарь, а внутри словаря потом в процессе кодирования ищем совпадения. Если совпадение нашлось, мы просто указываем в словаре где это совпадение можно отыскать
|
||||||
|
|
||||||
**Реализация (в общих чертах)**: загоняем весь текст в буффер, после чего начинаем искать сопадение для первого символа в словаре (в самом начале кодирования совпадений не будет). Пока совпадений нет, сдвигаем всю цепочку на 1 влево (если есть еще символы, которых нет в буфере, попутно доставляем их туда, а все что вылезает за пределы буффера забываем начисто). Как только есть совпадение на 1 символ, пытаемся найти совпадения на 2 символа, потом на 3 и так далее. То есть пытаемся найти как можно более длинное совпадение с содержимым словаря. Если такое есть - даем на него ссылку в закодированном сообщении словаре (в классической реализации - смещение от конца влево и длину совпавшего куска)
|
**Реализация (в общих чертах)**: загоняем весь текст в буффер, после чего начинаем искать сопадение для первого символа в словаре (в самом начале кодирования совпадений не будет). Пока совпадений нет, сдвигаем всю цепочку на 1 влево, если есть еще символы, которых нет в буффере, попутно доставляем их туда *(сдвинулись на 1 влево, в буффере освободилось место под один символ, дописываем в это место следующий в файле символ)*. Словарь и буффер - одна область памяти, называемая скользящим окном (см примечание выше), поэтому при сдвиге влево на один символ, самый левый символ из буффера появится в словаре, вытеснив самый старый символ уже словаря. Вытесненный символ нигде не хранится и просто исчезает из оперативной памяти. Как только есть совпадение на 1 символ, пытаемся найти совпадения на 2 символа, потом на 3 и так далее. То есть пытаемся найти как можно более длинное совпадение с содержимым словаря. Отмечу, что совпадения мы ищем даже тогда, когда словарь пуст. Если такое есть - даем на него ссылку в закодированном сообщении словаре (в классической реализации - смещение от конца влево и длину совпавшего куска)
|
||||||
|
|
||||||
**Проблема:** Если сначала мы ищем совпадение в один символ, потом в 2 символа, потом в 3 и так далее, то оценка сложности покажет сложность алгоритма по времени $O(n!)$. Не очень хорошо. Чем больше будет размер скользящего окна, тем больше потенциальных длинных цепочек мы сможем найти и тем сильнее, соответственно, сжать данные, но тем дольше будет процесс кодирования
|
**Проблема:** Если сначала мы ищем совпадение в один символ, потом в 2 символа, потом в 3 и так далее, то оценка сложности покажет сложность алгоритма по времени $O(n!)$. Не очень хорошо. Чем больше будет размер скользящего окна, тем больше потенциальных длинных цепочек мы сможем найти и тем сильнее, соответственно, сжать данные, но тем дольше будет процесс кодирования
|
||||||
|
|
||||||
@ -85,8 +85,22 @@ source:
|
|||||||
>
|
>
|
||||||
> Каждая новая добавленная последовательность создается из совпавшей последовательности + одна буква справа от нее. То есть если "abba" уже есть в словаре, а совпадение случилось в состоянии буфера "**abba**helloworld", то следующей в словарь добавится последовательность "abbah", потому что слева стояла буква "h"
|
> Каждая новая добавленная последовательность создается из совпавшей последовательности + одна буква справа от нее. То есть если "abba" уже есть в словаре, а совпадение случилось в состоянии буфера "**abba**helloworld", то следующей в словарь добавится последовательность "abbah", потому что слева стояла буква "h"
|
||||||
|
|
||||||
**Реализация**: теперь вместо символа или последовательности символов мы пишем номер, под которым соответствующая последовательность хранится в словаре (напоминаю, что нумеруем элементы массива в этом примере мы с **единицы**), после чего добавляем следующий за этой последовательностью символ. Если символ в словаре не найден, пишем в качестве числа 0. То есть в "abracadabra" первые 3 символа у нас во время прохода встретятся первый раз и в сжатый текст пойдет 0a0b0r, а вот потом встретится "a", которая есть в словаре, поэтому мы напишем ее порядковый номер, а затем следующий за совпавшей последовательностью символ: 1c, потом в ход пойдет "ad", который закодируется соответственно в 1d и так далее после кодирования получим следующий результат: 0a0b0r1c1d1b3a (заметим, что в данном случае мы ничего не сжали. Это нормально, потому что данные коротковаты, а алгоритмы сжатия дописывают довольно много информации. Порой сжатие не эффективно, тут ничего не поделать)
|
**Реализация**: теперь вместо символа или последовательности символов мы пишем номер, под которым соответствующая последовательность хранится в словаре (напоминаю, что нумеруем элементы массива в этом примере мы с **единицы**), после чего добавляем следующий за этой последовательностью символ. Если символ в словаре не найден, пишем в качестве числа 0. `{'a', 'b', 'r'}`)*, а вот потом встретится "a", которая есть в словаре, поэтому мы напишем ее порядковый номер, а затем следующий за совпавшей последовательностью символ: 1c (словарь: {'a', 'b', 'r', 'ac'})*, потом в ход пойдет "ad", который закодируется соответственно в 1d и так далее после кодирования получим следующий результат: 0a0b0r1c1d1b3a *(словарь: {'a', 'b', 'r', 'ac', 'ad', 'ab', 'ra'})* (заметим, что в данном случае мы ничего не сжали. Это нормально, потому что данные коротковаты, а алгоритмы сжатия дописывают довольно много информации. Порой сжатие не эффективно, тут ничего не поделать)
|
||||||
|
|
||||||
**Проблемы**: пусть здесь мы и поправили скорость сжатия (теперь на поиск совпадений уходит $O(n)$), а также мы убрали ограничения на размер скользящего окна и теоретически можем искать совпадения большой длины, можно увидеть, что в этом варианте алгоритма мы не заметили, что "abra" встречается 2 раза без изменений. То есть чтобы алгоритм был эффективен, желательно данные иметь побольше. Также не трудно понять, что если длинных цепочек повторяющихся данных у нас нет, то сжатие пойдет во вред размеру файла и это надо учитывать, поэтому если в файле нечему повторяться, то этот алгоритм не только не эффективен, но подчас и вреден, как мы могли убедиться
|
**Проблемы**: пусть здесь мы и поправили скорость сжатия (теперь на поиск совпадений уходит $O(n)$), а также мы убрали ограничения на размер скользящего окна и теоретически можем искать совпадения большой длины, можно увидеть, что в этом варианте алгоритма мы не заметили, что "abra" встречается 2 раза без изменений. То есть чтобы алгоритм был эффективен, желательно данные иметь побольше. Также не трудно понять, что если длинных цепочек повторяющихся данных у нас нет, то сжатие пойдет во вред размеру файла и это надо учитывать, поэтому если в файле нечему повторяться, то этот алгоритм не только не эффективен, но подчас и вреден, как мы могли убедиться
|
||||||
|
|
||||||
### Сжатие с потерями
|
### Алгоритмы сжатия с потерями
|
||||||
|
|
||||||
|
*Когда мы говорим о сжатии какого-нибудь текстового документа, потери данных оче ид о н прив дут ни к ч му хор шему, как можете видеть, читать такое не то чтобы очень приятно. Но вот если дело касается изображений - другой разговор. Наш глаз, вообще говоря, штука крайне не точная и вполне может не заметить каких-то деталей. Так что для фото и видео сжатия с потерями - отличный выход (потому что весят эти картиночки они будь здоров)*
|
||||||
|
|
||||||
|
### JPEG
|
||||||
|
|
||||||
|
JPEG - формат хранения изображения с потерями. Само по себе его обоснование довольно сложно и требует некоторых знаний в области математики. Тем не менее основывается он на некоторых вполне понятных идеях:
|
||||||
|
|
||||||
|
1. Если мы сжимаем фотографию или художественное полотно, то как правило 2 соседних пикселя будут близки по цвету
|
||||||
|
2. Человеческий глаз построен таким образом, что он гораздо восприимчивее к перепадам яркости, чем к изменению цвета (кому интересно, это связано с тем, что в [нашей сетчатке глаза](https://ru.wikipedia.org/wiki/Сетчатка) больше [палочек чем колбочек](https://ru.wikipedia.org/wiki/Фоторецептор)). То есть человеческий глаз намного чувствительнее к изменению яркости нежели к изменению цвета
|
||||||
|
|
||||||
|
Процесс сжатия в этом формате довольно длинный. Подробно все шаги пояснять не буду, потому что это ну прям совсем хана
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user