LINUX.ORG.RU

Теория почему сжатие данных дальше сокращения избыточности возможно

 , ,


1

2

В своей основе вопрос сжатия это принцип Дирихле: вот у нас строки большого размера, их больше, вот у нас строки меньшего размера, их соответственно меньше. количество клеток не совпадает с количеством голубей, ничего не получается, все расходимся? А вот и нет, можно добиться равенства, причем двумя путями: или меньше голубей, или больше клеток.

1)меньше голубей: Вот у нас множество всех строк, или файлов - вопрос интерпретации, длинны N. Мощность такого множество соответственно 2^N. Теперь посмотрим на множество всех строк длин (N-1) и (N-2) и…(1). Мощность этого множество будет ∑2^N, где N от (N-1) до (1), и равно это 2^N-2. Сравниваем мощности множеств, первое больше на два элемента. Ну а теперь финт ушами - мы просто берем и выкидываем два элемента из первого множества, для удобства это строки состоящие только из 0 или 1. И опаньки, биекция, если не верите можете прям на бумажке нарисовать и сравнить. И что получили в итоге? Получили что любую, кроме двух, строк можно переписать в виде минимум на один бит короче. Убрали бы больше элементов, сократили бы еще сильнее, это просто самый простой вариант.

2)больше клеток: Возьмем множество всех строк длинны от N до 1. Зададим функцию F(n), и обратную ей G(n), которая берет на вход один элемент и выдает другой. Во общем то все. F(n) функция абсолютно арбитрарная, просто применяешь F(n) несколько раз, пока нужный результат не получишь и все. Другое дело что хотелось бы побыстрее, поэтому -

2.1)столько же клеток, но в другом порядке: Вот возьмем множество всех строк размера N и упорядочим его следующим образом. Для этого мы берем функцию X(n), делает она следующие - берет бинарную строку и возвращает натуральное число от 1 до 2^N. Как она должна его считать честно не знаю, но она должна делать что-то приблизительно следующие: строим бинарное дерево(?) сравнений, сначала сравниваем половинки строки друг с другом. потом четвертинки, и вот тут уже не знаю, нужно ли сравнивать каждую с каждой или хватит сравнений 1 к 2, и 3 к 4. потом осьмушки итд. в конце сравниваем соседние биты, вот тут каждый с каждым точно лишнее. Потом переводим эти сравнения в значения с весами, например строки в которых есть повторения половинок должны быть ниже чем те у которых повторяются четвертинки, а те у которых нет повторений ни четвертинок ни половинок должны стоять выше, ближе к началу, к числу 1. Потом делаем над этими значениями некие операции и ВЖУХ! получаем натуральное число, он же порядковый номер. К примеру если N=8, то строка 11111111 должна иметь номер 256, строка 00000000 номер 255, строка 11111110 номер 254 итд. Вот что это за ВЖУХ! и нужно выяснить. Сначала я думал что можно просто обойтись тем что можно представить строку как число сочетаний из количества единиц по N, и все сочетания, где количество единиц приближается к половине длинны, просто поставить спереди, но потом прикинул что например строка 00001111 при таком подходе будет стоять выше чем строка 00101001, а это фатальный недостаток. Ну вот, допустим выясняли как ВЖУХ! выглядит, нашли порядковый номер в упорядоченном по повторениям списке. А теперь просто подставляем вместо этой строки другую строку с тем же номером, но в списке упорядоченном просто лексикографически. Ну а дальше например отправляешь данную строку в метод 1).

Остается только реализовать это все в софте. А вот это я не умею. Может кто-нибудь напишет?


Ответ на: комментарий от unC0Rr

Отлично, предлагаю архиватор: берём файл и разбиваем его на меньшие файлы на каждом предопределённом символе, например из «1234» будем делать файлы «1» и «34». Как собрать исходный - очевидно. Осталось только подсчитать экономию места на диска.

ну считай, что, ко мне то какие претензии? могу только предположить что мы не получим выгоду. выгодно большой файл целиком представлять в виде одного поменьше.

TL:DR.

так ты почитай, понятнее станет о чем речь.

q250
() автор топика
Ответ на: комментарий от q250

Как только мы переходим к последовательностям произвольной длины, возникает необходимость дополнительно кодировать признак конца этих последовательностей.

pasquale
()
Ответ на: комментарий от q250

да хоть на машкоде

да хоть на bat файлах

да хоть на машине Поста (али Тьюринга)

да хоть на немоймозг (язык программирования с расширением bf)

у тя не видно пока алго о свойствах которого ты утверждаешь :)

qulinxao3 ★★
()
Ответ на: комментарий от hobbit

блок-схему на бумаже нарисовал

тут чуть повыше картинки с таблицами для случаев N = 8, N = 5, N = 4. из них понятно какая строка переводится в какую.

anonymous
()

1)меньше голубей:

Алгоритм компрессии должен уметь находить «одинаковых голубей».

Да и нахождение «одинаковых голубей» это всего лишь один из тысяч способов компрессии.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 3)
3 февраля 2024 г.
Ответ на: комментарий от anonymous

что нейтрализуемое то? то что 2^n - 2 = 2^(n-1) + 2^(n-2) + …+2^1 это просто математический факт - тот что и позволяет организовать сжатие. единственное только что нужно сделать схему перевода строк более эффективной чем напрямую, ибо по сути схема выше почти что просто убирает нули в начале строки, в реальных данных такое встречается редко. нужно еще строки переставить в эффективный порядок. самую эффективную, скорее всего даже идеальную, такую схему, с поиском локальных повторений, «ВЖУХ!»-схему, я так и не сделал, да честно говоря и не сильно-то и пытался. рассказываю про ту которую придумал, она не самая эффективная, но довольно простая для понимания - хотя бы что-то.

суть, мы смотрим на бинарную строку не как последовательность нулей и единиц, а как на конкретное сочетание количества единиц из общего количества бит. вводим вот такую функцию: на вход подаем бинарную строку, на выходе получаем кортеж из трех чисел общее количество бит, количество единиц, номер сочетания. примеры: строка «00000001» перейдет в кортеж (8,1,1), строка «10000000» перейдет в кортеж (8,1,8), строка «01010101» перейдет в кортеж (8,4,21). как считать номер сочетания, на примере строки «01010101». сначала выделяем количество переменных равное количеству единиц и смотрим на каком месте стоит единичка. нумерацию единичек проводим от большего к меньшему, в нашем случае от 4 до 1; порядковое число единички присваивается с право налево, нумерацию начинаем от нуля. тогда в нашем частном случае получаем список

  • b4 = 6
  • b3 = 4
  • b2 = 2
  • b1 = 0

теперь находим вот такие сочетания: С из (6-4) по 6 = 15, С из (4-3) по 4 = 4, С из (2-2) по 2 = 1 и С из (0-1) по 0 = 0 <- заранее договариваемся что все подобные сочетания равны нулю. в общем виде для каждой позиции списка находим сочетание из n по k, где k это порядковое число единички в строке, а n это разность между порядковым числом единички и её номером. а потом просто все складываем и добавляем еще единичку, в смысле сумма + 1. для обратной операции, кортеж в число, лучшее что я придумал это просто подбирать числа, пока они не станут отрицательными. на примере того же кортежа (8,4,21), сначала убираем единичку из 21, начинаем подбирать позицию самой левой единички, дающей самый большой вклад в сумму, 20 - С из (х-4) по х, подставляем значения чтобы (х-4)>=0 но х<=8, ищем первую отрицательную сумму, предыдущие значение будет то что нам нужно. 20 - С из (4-4) по 4 = 19, 20 - С из (5-4) по 5 = 5, 20 - С из (6-4) по 6 = 5, 20 - С из (7-4) по 7 = -15 - первая отрицательная сумма, значит нужное значение это предыдущие, значит самая левая, четвертая единичка стоит на шестом месте, остальные места ищутся аналогично.

anonymous
()
Ответ на: комментарий от anonymous

ну вот мы ввели две функции, нашли кортеж соответствующий нашей строке, дальше что? ну а дальше пользуемся тем фактом что сумма n-ого ряда треугольника паскаля равна 2^n делаем перестановку всех строк, получаем что-то типа этого : https://imgur.com/a/tM9Tmzl. чтобы из правого столбика, размещения, на самом деле сочетания, перевести в левый, просто номер, находим данной строке её соответствующий кортеж, для строк с количеством единиц равным ровно половине размера строки просто отнимаем от номера сочетания единичку и добавляем нулей слева столько чтобы размер новой строки совпадал со старой; для строк с количеством единиц меньше половины размера берем номер сочетания и отнимаем от него 0,5 и умножаем получившееся на два, далее добавляем к данному все остальные сочетания, имея в виду что они идут парами, пока не дойдем до сочетания из половины размера, и его тоже добавляем, ну а дальше также просто отнимаем от номера сочетания единичку и добавляем нулей слева столько чтобы размер новой строки совпадал со старой; для строк с количеством единиц больше половины размера зеркально меняем все 1 на 0 и 0 на 1 а дальше все тоже самое, только от номера сочетания мы не отнимаем 0,5 а умножаем сразу. для обратной операции добавляем единичку и начинаем отнимать сочетания начиная с половины размера и далее, только уже парами, пока не наткнемся на отрицательное, как только наткнемся значит в предыдущем куске находится нужное нам количество единиц, за исключением если отняли сочетание половины размера из размера и получили отрицательное, значит сразу попали куда нужно. знаем размер, узнали количество единиц, для номера сочетания не равным половине, для половины тривиально значение + 1 = номер сочетания, если до добавления единички значение было четным тогда от значения отнимаем сочетания начиная с половины итд, находим предыдущее от отрицательного, делим пополам и добавляем 0,5 - это и будет номером сочетания, из получившегося кортежа находим строку, если до добавления единички значение было нечетным также тогда от значения отнимаем сочетания начиная с половины итд, находим предыдущее от отрицательного, просто делим пополам, находим строку соответствующую кортежу, а теперь просто зеркалим. понятно хоть описал?

ну вот, получили функцию энтропии, переставляющую строки. сначала применяем её а потом функцию сжатия, описанную на предыдущих страницах. то что современные машины способны работать только в байтах, но не в битах не проблема, как это решить я уже описывал. размер оригинала таки придется таскать с собой. в общем виде к хранимому файлу предлагается добавить 8 байт дополнительной информации, 4 на размер, т.е. максимум 4 Гб, и 4 на номер сжатия, хотя возможно даже одного будет много. чтоб было веселее предлагаю менять расположение размера, сначала в начале файла, а на следующем сжатии уже в конце. выглядеть должно примерно так:

  • оригинальный файл
  • № сжатия 4 байта, всегда в начале, 0000…0000 + размер файла на предыдущем шаге, 4 байта + сжатый файл
  • № сжатия, 0000…0001 + пересжатый файл + размер файла на предыдущем шаге
  • № сжатия, 0000…0010 + размер файла на предыдущем шаге + перепересжатый файл

итд. без понятия как все данное хозяйство поведет себя на реальных данных. в принципе очевидно что начиная с какого-то размера программа начнет делать то что и задумывалось, но какое это число я не знаю, знаю только что это не 8 Кбайт, их она сожмет только на 6 бит. еще вопрос вызывает функция соответствия строки кортежу, если со строкой в кортеж еще вроде не каких проблем, то вот преобразование кортежа в строку это удар по скорости: данное преобразование ЛИНЕЙНОЕ, и походу это принципиально, есть одно уравнение и куча переменных, и решить то его можно только потому переменные находятся в положении от большего к меньшему. ну т.е. заархивировать то можно а вот обратно можно и не дождаться. если вдруг кто-то знает как решить данную проблему - пишите сюда обязательно, а лучше сразу на arxiv.org.

anonymous
()