Каков наиболее эффективный способ хранения числового диапазона?

29

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

Представьте, что мы хотим сохранить поддиапазон в диапазоне 0-255.

Так например 45-74.

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

Я подозреваю, что любой метод сжатия дал бы предельный результат, поэтому лучше задать вопрос: «Каков максимальный диапазон, который может быть сохранен в одном байте?». Это должно быть больше, чем то, что достижимо, если хранить два числа отдельно.

Существуют ли стандартные алгоритмы для такого рода вещей?

rghome
источник
Вы также должны хранить начало диапазона?
Эван
@ Иван, я не очень понимаю. В приведенном выше примере 45 - это начало (минимум), а 74 - конец (максимум), и оба должны быть сохранены.
rghome
2
так же возникает вопрос, сколько места требует тип, который может хранить любой диапазон. или сколько места требуется типу, который может хранить 45-74?
Эван
1
Хотя думать об этом, безусловно, хорошо, я надеюсь, что вы не сделаете этого в реальных приложениях. Причина в том, что сложность реальных приложений настолько велика, что нам приходится принимать оптимизированный код менее чем на 100% ... Вот почему существовали компиляторы.
NoChance
3
@ rghome, я согласен, даже самое простое требование создает сотни строк кода. Каждый подвержен ошибкам. Лично я бы заплатил за оборудование, а не за сложность программного обеспечения.
NoChance

Ответы:

58

Просто посчитайте количество возможных диапазонов. Есть 256 диапазонов с нижней границей 0 (0-0, 0-1, ... 0-254, 0-255), 255 диапазонов с нижней границей 1, ... и, наконец, 1 диапазон с нижней границей 255 (255- 255). Таким образом, общее число (256 + 255 + ... + 1) = 257 * 128 = 32 896. Поскольку это немного больше, чем 2 15 = 32 768, вам все равно понадобится как минимум 16 бит (2 байта) для хранения этой информации.

Как правило, для чисел от 0 до n-1 число возможных диапазонов равно n * (n + 1) / 2. Это меньше 256, если n равно 22 или меньше: n = 22 дает 22 * ​​23/2 = 253 возможностей. Таким образом, одного байта достаточно для поддиапазонов 0-21 .

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

Глорфиндел
источник
Спасибо. Число битов, необходимых для n диапазонов, равно log (n) / log2. Подача всего этого в Wolfram Alpha дала мне следующую совместимую с Excel формулу для расчета максимального значения для поддиапазона для заданного числа битов: = INT ((SQRT (POWER (2, N + 3) + 1) - 1) / 2 )
rghome
9
TLDR заключается в том, что вы получаете примерно половину, поэтому в общем случае сжатие не стоит.
rghome
Да, для большого N это имеет тенденцию к одному, но это действительно не стоит хлопот.
Глорфиндель
К вашему сведению, N + 3 в уравнении выглядит странно, но одна степень 2 исходит из вашего уравнения, а две другие - из 4ac части квадратичной формулы.
rghome
1
Кстати, ваш подсчет дисконтирует пустой диапазон, для которого стоят все не пересчитанные комбинации. Так, n * (n + 1) / 2 + 1! Крошечная перемена.
дедупликатор
17

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

Предположим, что домен - это целые числа, поэтому 32 бита. При наивном подходе вам нужно 64 бита (начало, конец) для хранения диапазона.

Если мы переключимся на кодировку (start, delta), мы можем построить конец диапазона из этого. Мы знаем, что в худшем случае начало равно 0, а дельта имеет 32 бита.

2 ^ 5 равно 32, поэтому мы кодируем длину дельты в пять битов (без нулевой длины, всегда добавляем 1), и кодирование становится (начало, длина, дельта). В худшем случае это стоит 32 * 2 + 5 бит, то есть 69 бит. Таким образом, в худшем случае, если все диапазоны длинные, это хуже, чем простое кодирование.

В лучшем случае это стоит 32 + 5 + 1 = 38 бит.

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

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

Допустим, у вас есть 10000 диапазонов. Диапазоны сгруппированы вокруг определенного значения. Вы кодируете смещение с 32 битами.

Используя наивный подход, вам потребуется 32 * 2 * 10 000 = 640 000 бит для хранения всех этих диапазонов.

Кодирование смещения занимает 32 бита, а в лучшем случае кодирование каждого диапазона занимает 5 + 1 + 5 + 1 = 12 бит, что в сумме составляет 120 000 + 32 = 120 032 бита. В худшем случае вам нужно 5 + 32 + 5 + 32 бита, то есть 74 бита, всего 740 032 бита.

Это означает, что для 10 000 значений в домене, для кодирования которого требуется 32 бита, мы получаем

  • 120 032 бит с умным дельта-кодированием в лучшем случае
  • 640 000 бит с начальным, конечным кодированием, всегда (ни в лучшем, ни в худшем случае)
  • 740 032 бит с умным дельта-кодированием в худшем случае

Если вы берете простое кодирование в качестве базового уровня, это означает либо экономию до 81,25%, либо до 15,625% больше затрат.

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

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

Если ваши начальные точки распределены одинаково, эта кодировка не очень хорошо работает.

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

Оценка данных, их группировка и выбор правильного смещения могут быть существенной работой и могут потребовать некоторой корректировки для достижения оптимальных результатов.

Polygnome
источник
8

Проблема такого рода - предмет оригинальной статьи Клода Шеннона « Математическая теория коммуникации» , в которой введено слово «бит» и более или менее изобретенное сжатие данных.

Общая идея заключается в том, что количество битов, используемых для кодирования диапазона, обратно пропорционально вероятности возникновения этого диапазона. Например, предположим, что диапазон 45-74 появляется примерно в 1/4 времени. Вы можете сказать, что последовательность 00 соответствует 45-74. Чтобы кодировать диапазон 45-74, вы выводите «00» и останавливаетесь на этом.

Давайте также предположим, что диапазоны 99-100 и 140-155 каждый появляются примерно в 1/8 времени. Вы можете кодировать каждый из них 3-битной последовательностью. Любые 3 бита будут работать до тех пор, пока они не начнутся с «00», который уже зарезервирован для диапазона 45–74.

00: 45-74
010: 99-100
101: 140-155

Вы можете продолжать таким образом, пока каждый возможный диапазон не будет иметь кодировку. Для наименее вероятного диапазона может потребоваться более 100 бит. Но это нормально, потому что это редко появляется.

Там являются алгоритмы для поиска оптимального кодирования. Я не буду пытаться объяснить их здесь, но вы можете узнать больше, перейдя по ссылке выше или в поисках «Теория информации», «Кодирование Шеннона-Фано» или «Кодирование Хаффмана».

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

Патрик МакЭлхани
источник
1
Да, сфера бизнеса действительно важна. На самом деле мы рассматривали возможность использования кодирования Хаффмана для систематической ошибки для начальной даты, но в итоге решили отказаться от нее после проведения некоторого статистического анализа реальных данных. Простота использования одной и той же кодировки для смещения и дельты была более важной, чем добавление Хаффмана сверху, плюс вам также необходимо отправить все дерево Хаффмана. Это хорошая идея, чтобы помнить кодирование Хаффмана.
Полигном
1

Чтобы расширить ответ от @Glorfindel:

При n → ∞, (n - 1) → n. Таким образом, Ω (диапазоны) → n² / 2 и log (Ω (диапазоны)) → (2n - 1). Поскольку простое кодирование занимает 2n битов, асимптотическое максимальное сжатие сохраняет только 1 бит.

Джаред Гогуэн
источник
1

Есть аналогичный ответ, но для достижения оптимального сжатия вам необходимо:

  1. Оптимальный метод энтропийного кодирования (чтение по арифметическому кодированию и по существу эквивалентный (тот же коэффициент сжатия, немного быстрее, но сложнее понять) ANS )
  2. Как можно больше информации о распределении данных. Важно то, что это не просто «угадывание», как часто может появляться одно число, но вы часто можете точно исключить определенные возможности. Например, вы можете исключить интервалы отрицательного размера и, возможно, 0 размера, в зависимости от того, как вы определяете допустимый интервал. Если у вас есть несколько интервалов для одновременного кодирования, вы можете отсортировать их, например, в порядке уменьшения ширины или увеличения начального / конечного значения, и исключить целый ряд значений (например, если вы гарантируете порядок по уменьшению ширины, предыдущий интервал имеет ширину 100, а начальное значение для следующего - 47, вам нужно только рассмотреть возможности до 147 для конечных значений).

Важно отметить, что число 2 означает, что вы хотите кодировать вещи таким образом, чтобы наиболее информативные значения (для каждого закодированного бита) были первыми. Например, хотя я предложил кодировать отсортированный список «как есть», обычно разумнее было бы кодировать его как «двоичное дерево», т. Е. Если они отсортированы по ширине, а у вас есть lenэлементы, начните с элемента кодирования len/2, Скажем, у него была ширина w. Теперь вы знаете, что все элементы имеют ширину где-то в [0, w], и все элементы имеют ширину где-то в [w, max val, который вы принимаете] Повторяйте рекурсивно (снова подразделяя каждую половину списка пополам и т. Д.) До тех пор, пока вы не закроете lenэлементы (если это не исправлено, вы захотите закодироватьlenво-первых, вам не нужно беспокоиться об окончании токенов). Если «max val you accept» действительно открыт, может быть целесообразно сначала закодировать самое высокое значение, которое фактически появляется в ваших данных, то есть последний элемент, а затем выполнить двоичное разбиение. Опять же, все, что является наиболее информативным в первую очередь.

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

tohoho
источник