Вот улучшение ответа Экс .Рассмотрите возможность использования трех «слоев» для структуры данных: первый является константой для первых пяти цифр (17 бит);поэтому с этого момента на каждом номере телефона остаются только пять оставшихся цифр.Мы рассматриваем эти оставшиеся пять цифр как 17-разрядные двоичные целые числа и сохраняем k этих битов одним методом, а 17 - k = m другим методом,определение k в конце, чтобы минимизировать требуемое пространство.
Сначала мы сортируем телефонные номера (все сокращены до 5 десятичных цифр).Затем мы подсчитываем, сколько существует телефонных номеров, для которых двоичное число, состоящее из первых m битов, равно 0, для скольких телефонных номеров первые m биты не больше 0...01, для скольких телефонных номеров первые m битов не более 0 ... 10 и т. Д., Вплоть до количества телефонных номеров, для которых первые m битов1 ... 11 - этот последний счет равен 1000 (десятичному).Есть 2 ^ m таких подсчетов, и каждый отсчет не более 1000. Если мы опускаем последний (потому что мы все равно знаем, что это 1000), мы можем сохранить все эти числа в непрерывном блоке (2 ^ м - 1) * 10 бит.(10 битов достаточно для хранения номера менее 1024.)
Последние k битов всех (сокращенных) телефонных номеров хранятся в памяти непрерывно;так что если k , скажем, 7, то первые 7 битов этого блока памяти (биты с 0 по 6) соответствуют последним 7 битам первого (сокращенного) номера телефона, биты с 7 по 13соответствуют последним 7 битам второго (уменьшенного) номера телефона и так далее.Для этого требуется 1000 * k битов в общей сложности 17 + (2 ^ (17 - k ) - 1) * 10 + 1000 * k , что достигаетего минимум 11287 для k = 10. Таким образом, мы можем хранить все телефонные номера в ceil (11287/8) = 1411 байт.
Дополнительное пространство можно сэкономить, заметив, что ни один из наших номеровможет начинаться, например, с 1111111 (двоичное), потому что наименьшее число, которое начинается с этого, - 130048, и у нас есть только пять десятичных цифр.Это позволяет нам убрать несколько записей из первого блока памяти: вместо 2 ^ m - 1 отсчет нам нужен только ceil (99999/2 ^ k ).Это означает, что формула становится
17 + ceil (99999/2 ^ k ) * 10 + 1000 * k
, что удивительным образом достигает своегоминимум 10997 для k = 9 и k = 10, или ceil (10997/8) = 1375 байт.
Если мы хотим знать, является ли определенный телефончисло находится в нашем наборе, мы сначала проверяем, соответствуют ли первые пять двоичных цифр пяти сохраненных нами цифр.Затем мы разбиваем оставшиеся пять цифр на его верхние m = 7 бит (скажем, m -битное число M ) и его нижние k = 10 бит (число K ).Теперь мы находим номер a [M-1] сокращенных телефонных номеров, для которых первые m цифры не более M - 1, и номер a [M] сокращенных телефонных номеров, для которых первые m цифр не более M , оба из первого блока битов.Теперь мы проверяем между a [M-1] -й и a [M] -ой последовательностью k бит во втором блоке памяти, чтобы увидеть,найти К ;в худшем случае существует 1000 таких последовательностей, поэтому, если мы используем двоичный поиск, мы можем завершить операции O (log 1000).
Далее следует псевдокод для печати всех 1000 чисел, где я получаю доступ к K '* k -битная запись первого блока памяти как a [K] и M ' th m -битовая запись второго блока памяти как b [M] (обе эти операции потребуют нескольких битовых операций, которые утомительны для записи).Первые пять цифр находятся в числе c .
i := 0;
for K from 0 to ceil(99999 / 2^k) do
while i < a[K] do
print(c * 10^5 + K * 2^k + b[i]);
i := i + 1;
end do;
end do;
Возможно, что-то не так с граничным регистром для K = ceil (99999/2 ^ k ), но это достаточно легко исправить.
Наконец, с точки зрения энтропии, невозможно сохранить подмножество из 10 ^ 3 натуральных чисел, все из которых меньше, чем 10 ^ 5, меньше чем ceil (log [2] (binomial (10 ^ 5, 10 ^) 3))) = 8073. Включая 17, которые нам нужны для первых 5 цифр, все еще остается разрыв в 10997 - 8090 = 2907 бит. Это интересная задача, чтобы увидеть, есть ли лучшие решения, где вы все еще можете получить доступ к номерам относительно эффективно!