Алгоритм сжатия для отсортированных целых чисел

У меня есть большая последовательность случайных целых чисел, отсортированных от самого низкого до самого высокого. Числа начинаются с 1 бита и заканчиваются около 45 бит. В начале списка у меня есть числа, очень близкие друг к другу: 4, 20, 23, 40, 66. Но когда числа начинают становиться выше, расстояние между ними также становится немного больше (фактически расстояние между ними является отрицательным). Дублированных номеров нет.

Я использую битовую упаковку, чтобы сэкономить место. Тем не менее, этот файл может стать действительно большим.

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

Спасибо.

Ответ 1

Вы можете сжимать оптимально, если знаете истинное распределение данных. Если вы можете предоставить распределение вероятности для каждого целого, вы можете использовать арифметическое кодирование или другие методы энтропийного кодирования для сжатия до теоретического минимального размера.

Трюк заключается в точном предсказании.

Во-первых, вы должны, вероятно, сжать расстояния между числами, поскольку это позволяет делать статистические утверждения. Если вам нужно было сжать номера напрямую, вам будет сложно их моделировать, потому что они встречаются только один раз.

Затем вы можете попытаться создать очень простую модель для прогнозирования следующего расстояния. Держите гистограмму всех ранее увиденных расстояний и вычислите вероятности с частот.

Вероятно, вам нужно учитывать отсутствующие значения (вы явно не можете назначить им вероятность 0, потому что это не выражается), но вы можете использовать эвристику для этого, например, кодирование следующего расстояния по битам и прогнозирования каждый бит индивидуально. Вы почти ничего не будете платить за старшие биты, потому что они почти всегда равны 0, а энтропийное кодирование оптимизирует их.

Все это намного проще, если вы знаете дистрибутив. Пример. Вы сжимаете список всех простых чисел, которые вы знаете, теоретическое распределение расстояний, потому что для этого есть формулы. Итак, у вас уже есть идеальная модель.

Ответ 2

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

Это тип дельта-кодирования (т.е. каждое число представлено расстоянием от предыдущего), состоящим из вектора кодов, которые либо

  • один 1-бит, представляющий собой дельта 2 k который добавляется к дельтам в следующем коде или

  • 0-бит, за которым следует k-разрядная дельта, указывающая, что следующее число является указанной дельта от предыдущей.

Например, если k равно 4, последовательность:

0011 1 1 0000 1 0001

кодирует три числа. Первая четырехбитовая кодировка (3) является первой дельта, взятой из начального значения 0, поэтому первое число равно 3. Следующие два одиночных 1 накапливаются до дельта 2 & centerdot; * 2 ​​ 4 или 32, который добавляется к следующей дельтам 0000, в общей сложности 32. Таким образом, второе число равно 3 + 32 = 35. Наконец, последняя дельта представляет собой один 2 4 плюс 1, всего 17, а третье число равно 35 + 17 = 52.

1-бит указывает, что следующая дельта должна быть увеличена на 2 k (или, в более общем случае, каждая дельта увеличивается на 2 k раз число сразу предшествующих 1-битам.)

Другим, возможно, лучшим способом мышления является то, что каждая дельта кодируется как последовательность бит переменной длины: 1 i 0 (1 | 0) k представляющий собой дельта я & centerdot; 2 k + [k-bit suffix]. Но первая презентация лучше согласуется с доказательством оптимальности.

Поскольку каждый "1" код представляет собой приращение 2 k их не может быть больше m/2 k где m - наибольшее число в установленный для сжатия. Остальные коды соответствуют номерам и имеют общую длину n * (2 k + 1), где n - размер множества. Оптимальное значение k составляет примерно log 2 m/n, что в вашем случае будет 7 или 8.

Я быстро доказал концепцию алгоритма, не заботясь об оптимизации. Это все еще много быстро; сортировка случайной выборки занимает намного больше времени, чем сжатие/декомпрессия. Я попробовал его с несколькими различными размерами семян и векторов от 16 400 000 до 31 000 000 с диапазоном значений [0, 4 000 000 000]. Биты, используемые для каждого значения данных, варьировались от 8.59 (n = 31000000) до 9.45 (n = 16400000). Все тесты проводились с 7-битными суффиксами; log 2 m/n варьируется от 7.01 (n = 31000000) до 7,93 (n = 16400000). Я пробовал с 6-битными и 8-битными суффиксами; за исключением случая n = 31000000, где 6-битные суффиксы были немного меньше, 7-битный суффикс всегда был лучшим. Поэтому я предполагаю, что оптимальный k не является ровным полом (log 2 m/n), но он не за горами.

Код сжатия:

void Compress(std::ostream& os,
              const std::vector<unsigned long>& v,
              unsigned long k = 0) {
  BitOut out(os);
  out.put(v.size(), 64);
  if (v.size()) {
    unsigned long twok;
    if (k == 0) {
      unsigned long ratio = v.back() / v.size();
      for (twok = 1; twok <= ratio / 2; ++k, twok *= 2) { }
    } else {
      twok = 1 << k;
    }
    out.put(k, 32);

    unsigned long prev = 0;
    for (unsigned long val : v) {
      while (val - prev >= twok) { out.put(1); prev += twok; }
      out.put(0);
      out.put(val - prev, k);
      prev = val;
    }
  }
  out.flush(1);
}

Декомпрессия:

std::vector<unsigned long> Decompress(std::istream& is) {
  BitIn in(is);
  unsigned long size = in.get(64);
  if (size) {
    unsigned long k = in.get(32);
    unsigned long twok = 1 << k;

    std::vector<unsigned long> v;
    v.reserve(size);
    unsigned long prev = 0;
    for (; size; --size) {
      while (in.get()) prev += twok;
      prev += in.get(k);
      v.push_back(prev);
    }
  }
  return v;
}

Может быть немного неудобно использовать кодировки переменной длины; альтернативой является сохранение первого бита каждого кода (1 или 0) в битовом векторе и k-битовых суффиксов в отдельном векторе. Это было бы особенно удобно, если k равно 8.

Вариант, который приводит к небольшим более длинным файлам, но для них легче создавать индексы, - это использовать только 1-бит в качестве дельт. Тогда дельта всегда является * 2 k для некоторого a, возможно, 0, где a - число последовательных 1 бит, предшествующих суффиксному коду. Затем индекс состоит из локаций каждого N th 1-бит в битовом векторе и соответствующего индекса в вектор суффикса (т.е. Индекс суффикса, соответствующий следующему 0 в битовом векторе).


Ответ 3

Я хочу добавить еще один ответ с самым простым решением:

  • Преобразование чисел в дельтах, как обсуждалось ранее
  • Запустите его через 7-zip-алгоритм LZMA2. Он даже готов к работе с несколькими ядрами.

Я думаю, что это даст почти идеальные результаты в вашем случае, потому что расстояния имеют простое распределение. 7-zip сможет его поднять.

Ответ 4

Один из вариантов, который работал у меня в прошлом, состоял в том, чтобы сохранить список из 64-битных целых чисел в виде 8 разных списков 8-битных значений. Вы храните 8 разрядов чисел, затем следующие 8 бит и т.д. Например, скажем, у вас есть следующие 32-битные номера:

0x12345678
0x12349785
0x13111111
0x13444444

Сохраненные данные будут (в шестнадцатеричном формате):

12,12,13,13
34,34,11,44
56,97,11,44
78,85,11,44

Затем я запускал это через компрессор дефлята.

Я не помню, какие коэффициенты сжатия мне удалось достичь, но это было значительно лучше, чем сжатие самих чисел.

Ответ 5

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

Если ваша последовательность состоит из действительно случайных чисел, сгенерированных каким-то недетерминированным способом, то другие ответы, уже опубликованные, предлагают множество хороших советов.