Ось покращення відповіді Екса . Розглянемо використання трьох "шарів" для структури даних: перший - константа для перших п'яти цифр (17 біт); тож звідси у кожного номера телефону залишилося лише п’ять цифр, що залишилися. Ми розглядаємо ці п'ять цифр як 17-бітні двійкові цілі числа і зберігаємо k цих бітів одним методом, а 17 - k = m іншим методом, визначаючи k в кінці для мінімізації необхідного простору.
Спочатку ми сортуємо номери телефонів (усі зменшуються до 5 десяткових цифр). Тоді ми підраховуємо, скільки є телефонних номерів, для яких двійкове число, що складається з перших m бітів, дорівнює 0, на скільки телефонних номерів перші m біти становлять максимум 0 ... 01, на скільки телефонних номерів перший m біт - максимум 0 ... 10, і т.д., до числа телефонних номерів, для яких перші m бітів дорівнюють 1 ... 11 - останній підрахунок 1000 (десятковий). Таких підрахунків є 2 ^ м , і кожен рахунок становить максимум 1000. Якщо опустити останній (оскільки ми все-таки знаємо, що це 1000), ми можемо зберігати всі ці числа у суміжному блоці (2 ^ m - 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 ^ м - 1 підрахунок, нам потрібен лише ceil (99999/2 ^ k ). Це означає, що формула стає
17 + стеля (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 , як з першого блоку бітів. Тепер перевіряємо між а[М-1] й і [М] е послідовність K біт в другому блоці пам'яті , щоб побачити , якщо ми знаходимо K ; в гіршому випадку є 1000 таких послідовностей, тож якщо ми використовуємо двійковий пошук, ми можемо закінчити операції O (log 1000).
Псевдокод для друку всіх 1000 номерів слід, де доступ до До -му до -бітний введення першого блоку пам'яті , як в [K] і М 'я м записи бітової другого блоку пам'яті в якості Ь [М] (і те й інше потребує декількох бітних операцій, які нудно виписувати). Перші п’ять цифр знаходяться в числі 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] (двочлен (10 ^ 5, 10 ^ 3)) ) = 8073. Включаючи 17, які нам потрібні для перших 5 цифр, все ще залишається пробіл 10997 - 8090 = 2907 біт. Цікавим завданням є те, чи є кращі рішення, де ти можеш відносно ефективно отримувати доступ до номерів!