Шукаєте реалізацію набору з невеликим розміром пам’яті


9

Я шукаю реалізацію заданого типу даних. Тобто ми повинні

  • підтримувати динамічну підмножину (розміром ) з Всесвіту U = \ {0, 1, 2, 3, \ крапки, u - 1 \} розміром u зSnU={0,1,2,3,,u1}u
  • операції insert(x)(додавання елемента xдо S ) та find(x)(перевірка, чи елемент xє членом S ).

Мене не цікавлять інші операції. Для орієнтації в додатках, з якими я працюю, ми маємо u1010 .

Я знаю про реалізації, які забезпечують обидві операції в часі O(1) , тому я переживаю здебільшого за розмір структури даних. Я очікую мільярди записів, але хочу максимально уникати заміни.

Я готовий принести в жертву час виконання, якщо це необхідно. Амортизований час виконання O(logn) - це те, що я можу визнати; очікувані тривалість виконання або час виконання в ω(logn) не допускаються.

Я маю одну ідею: якщо S може бути представлений у вигляді об'єднання діапазонів [xmin, xmax], ми зможемо заощадити на розмірі пам’яті з ціною деякого зниження продуктивності. Також можливі деякі інші шаблони даних, наприклад [0, 2, 4, 6].

Не могли б ви вказати мені структури даних, які можуть зробити щось подібне?



Як число елементів входить у малюнок? Тобто, що станеться, якщо елемент вставлений і вже є ? nn
vonbrand

@vonbrand - nрозмір набору S. Він може збільшуватися з кожним insert, або він може залишатися однаковим, якщо елемент xвже є в наборі.
HEKTO

3
Чи можете ви прийняти малу ймовірність помилкових позитивних результатів? Якщо так, фільтр цвітіння може бути ідеальним: en.wikipedia.org/wiki/Bloom_filter
Джо

1
@AlekseyYakovlev, хибнопозитивна швидкість фільтра розцвітання не має нічого спільного з розміром Всесвіту (лише з кількістю хеш-функцій , розміром структури даних та кількістю елементів ), але якщо насправді близький до (скажімо, для невеликої константи ), вам буде важко натиснути, щоб зробити це краще, ніж простий векторний біт, я думаю, лише з сумарними бітами простору. kmnnuu=ncccn
Джо

Відповіді:


8

Відповідь Джо надзвичайно хороша і дає вам усі важливі ключові слова.

Вам слід пам’ятати, що лаконічні дослідження структури даних все ще знаходяться на ранній стадії, і багато результатів є в основному теоретичними. Багато запропонованих структур даних є досить складними для реалізації, але більша частина складності пов'язана з тим, що потрібно підтримувати асимптотичну складність як за розміром Всесвіту, так і за кількістю збережених елементів. Якщо будь-яке з них відносно постійне, багато складності проходить.

Якщо колекція є напівстатичною (тобто вставки рідкісні або, принаймні, малооб'ємні), то, безумовно, варто розглянути просту у впровадженні статичну структуру даних (sdarray Sadakane - прекрасний вибір) у поєднанні з оновленням кеш. В основному ви записуєте оновлення в традиційну структуру даних (наприклад, B-дерево, трие, хеш-таблиця) і періодично масово оновлюєте "основну" структуру даних. Це дуже популярна методика пошуку інформації, оскільки перевернуті індекси мають багато переваг для пошуку, але їх важко оновити на місці. Якщо це так, будь ласка, повідомте мене про це в коментарі, і я внесу зміни в цю відповідь, щоб дати вам кілька покажчиків.

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

Отже, основним теоретичним результатом інформації є те, що якщо ви зберігаєте елементів із всесвіту елементів, а іншої інформації немає (наприклад, немає кореляції між елементами), то вам знадобиться біти для його зберігання. (Усі логарифми є базовою-2, якщо не вказано інше.) Вам потрібно стільки бітів. Не обійтися.nulog(un)+O(1)

Тепер трохи термінології:

  • Якщо у вас є структура даних, яка може зберігати дані та підтримувати ваші операції у бітах простору , ми називаємо це неявною структурою даних.log(un)+O(1)
  • Якщо у вас є структура даних, яка може зберігати дані та підтримувати ваші операції в біт простору, ми називаємо це компактною структурою даних. Зауважимо, що на практиці це означає, що відносна накладність (відносно теоретичного мінімуму) знаходиться в межах постійної. Це може бути 5% накладних витрат, або 10% накладних витрат, або 10 разів накладних витрат.log(un)+O(log(un))=(1+O(1))log(un)
  • Якщо у вас є структура даних, яка може зберігати дані та підтримувати ваші операції в біт простору, ми називаємо це лаконічною структурою даних.log(un)+o(log(un))=(1+o(1))log(un)

Різниця між стислим і компактним - це різниця між маленьким-ой і великим-ой. На мить ігноруючи річ з абсолютною цінністю ...

  • g(n)=O(f(n)) означає, що існує константа і число така що для всіх , .cn0n>n0g(n)<cf(n)
  • g(n)=o(f(n)) означає, що для всіх констант існує число таке, що для всіх , .cn0n>n0g(n)<cf(n)

Неофіційно великі-о-о і малі-ої є "в межах постійного коефіцієнта", але при великому-ой для вас вибирається константа (дизайнером алгоритму, виробником процесора, законами фізики чи будь-яким іншим), але мало -о, ви вибираєте постійну себе, і вона може бути такою ж маленькою, як вам подобається . Інакше кажучи, з лаконічними структурами даних відносні накладні витрати стають довільно невеликими, оскільки розмір проблеми збільшується.

Звичайно, розмір проблеми може бути величезним, щоб усвідомити відносні накладні витрати, які ви хочете, але ви не можете мати все.

Гаразд, маючи це під нашими поясами, давайте поставимо деякі цифри на проблему. Припустимо, що ключі бітних цілих чисел (тому розмір Всесвіту дорівнює ), і ми хочемо зберегти цих цілих чисел. Припустимо, що ми можемо магічно влаштувати ідеалізовану хеш-таблицю з повною заповненістю та без зайвих витрат, так що нам потрібно точно хеш-слотів.n2n2m2m

Операція пошуку дозволить хешувати бітний ключ, замаскувати бітів, щоб знайти хеш-слоти, а потім перевірити, чи відповідає значення таблиці в таблиці. Все йде нормально.nm

Така хеш-таблиця використовує біт. Чи можемо ми зробити краще, ніж це?n2m

Припустимо, що хеш-функція є зворотною. Тоді нам не потрібно зберігати весь ключ у кожному хеш-слоті. Розташування слота хеша дає вам біт хеш-значення, тому якщо ви зберегли лише решти бітів, ви можете реконструювати ключ із цих двох відомостей (місце розташування хеш-слота та значення, яке зберігається там). Тож вам знадобиться лише біт зберігання.hmnm(nm)2m

Якщо невеликий порівняно з , наближення Стірлінга і трохи арифметики (доказ - вправа!) Показує, що:2m2n

(nm)2m=log(2n2m)+o(log(2n2m))

Тож ця структура даних є лаконічною.

Однак є два улови.

Перший улов - це побудова "хороших" обертових хеш-функцій. На щастя, це набагато простіше, ніж це виглядає; криптографи весь час виконують незворотні функції, лише вони називають їх "циферами". Наприклад, ви можете базувати хеш-функцію в мережі Feistel, що є простим способом побудови інвертованих хеш-функцій з неперевернутих хеш-функцій.

Друга увага полягає в тому, що справжні хеш-таблиці не є ідеальними, завдяки парадоксу Дня народження. Тож ви хочете використовувати більш складний тип хеш-таблиці, який наближає вас до повної зайнятості без розливу. Хеширование зозулі ідеально підходить для цього, оскільки дозволяє теоретично наблизитись до ідеалу і досить близько на практиці.

Для хешування зозулі потрібно декілька хеш-функцій, і вона вимагає, щоб значення в хеш-слотах були позначені тегами, за допомогою якої використовувалася хеш-функція. Отже, якщо ви використовуєте чотири хеш-функції, наприклад, вам потрібно зберегти додаткові два біта у кожному хеш-слоті. Це все ще є лаконічним у міру зростання , тому це не є проблемою на практиці, і все ще б'є зберігання цілих клавіш.m

О, ви також можете подивитися на дерева Емде Боаса.

БІЛЬШЕ МИСЛІВ

Якщо десь навколо , то приблизно , тож (ще раз) припускаючи, що немає ніякої подальшої кореляції між величинами, ви в основному не можете зробити жодне краще, ніж бітний вектор. Ви зауважите, що вищевказане рішення хешування ефективно вироджується в цьому випадку (ви в кінцевому підсумку зберігаєте один біт на хеш-слот), але дешевше просто використовувати ключ як адресу, а не використовувати хеш-функцію.nu2log(un)u

Якщо дуже близький до , вся література про стислі структури даних радить вам інвертувати сенс словника. Зберігайте значення, які не зустрічаються у наборі. Однак тепер вам ефективно потрібно підтримувати операцію видалення, а для збереження лаконічної поведінки також потрібно мати змогу скоротити структуру даних, оскільки «додається більше елементів». Розширення хеш-таблиці - це добре зрозуміла операція, але укладання контрактів - це не так.nu


Привіт, що стосується другого абзацу вашої відповіді - я очікую, що кожен дзвінок до insertбуде супроводжуватися закликом до findтого ж аргументу. Отже, якщо findповертається true, ми просто пропускаємо insert. Отже, частота findдзвінків частіше за частоту insertдзвінків, також коли вона nстає близькою u, тоді insertдзвінки стають дуже рідкісними.
HEKTO

Але ви розраховуєте, що зрештою наблизиться до ? un
Псевдонім

У реальному світі п росте, поки не досягне u, проте ми не можемо передбачити, відбудеться це чи ні. Структура даних повинна добре працювати для будь-якогоn <= u
HEKTO

Правильно. Тоді справедливо сказати, що ми не знаємо єдиної структури даних, яка є лаконічною (у вищезгаданому сенсі) і яка досягає цього у всьому діапазоні . Я думаю, що вам потрібно буде розріджену структуру даних, коли , потім переключитесь на щільну (наприклад, бітовий вектор), коли навколо , то розріджена структура даних із перевернутим сенс, коли близький до . nun<unu2nu
Псевдонім

5

Це здається, що ви хочете мати стисну структуру даних для проблеми динамічного членства .

Нагадаємо, що лаконічна структура даних - це така, для якої потреба в просторі "близька" до інформаційно-теоретичної нижньої межі, але на відміну від стислої структури даних все ще дозволяє виконувати ефективні запити.

Проблема членства - це саме те, що ви описуєте у своєму запитанні:

підтримувати підмножину (розміру ) з Всесвіту розміром з операціями:SnU={0,1,2,3,,u1}u

  • find(x)(перевіряє, чи елемент xє членом ).S
  • insert(x)(додати елемент xдо )S
  • delete(x)(видаліть елемент xіз )S

Якщо підтримується лише findоперація, то це проблема статичного членства. Якщо будь-яка insertабо deleteпідтримується, але не обидві, це називається напівдинамічним , і якщо всі три операції підтримуються, то це називається проблемою динамічного членства.

Фактично, я думаю, ви лише попросили структуру даних для напівдинамічної проблеми членства, але я не знаю жодної структури даних, яка б скористалася цим обмеженням, а також відповідала б іншим вашим вимогам. Однак у мене є така довідка:

У теоремі 5.1 статті " Членство в постійному часі та майже мінімальному просторі" Бродник та Манро дають такий результат:

Існує структура даних, що вимагає біт який підтримує пошук у постійному часі та вставки та вилучення в постійному очікуваному амортизованому часі.O(B)

де - мінімально необхідна теоретична кількість бітів.B=log(un)

Основна ідея полягає в тому, що вони рекурсивно розбивають Всесвіт на діапазони ретельно підібраних розмірів, тому це навіть звучить так, що методи можуть бути уздовж тих думок, про які ви думаєте.

Однак якщо ви шукаєте щось, що можете реально реалізувати, я не знаю, чи стане це найкращим варіантом. Я лише прокинув папери, і намагаючись пояснити деталі, це далеко поза рамками цієї відповіді. Вони параметризують своє рішення, використовуючи різні стратегії залежно від відносних розмірів та . А динамічна версія структури даних лише замальована на папері.un


1
Конспект паперу Brodnik & Munro нічого не говорить про вставки. Але їх результат - це те, що ми можемо очікувати, правда? Якщо n = u/2, тоді необхідний простір максимальний.
HEKTO

@AlekseyYakovlev Вони не реально згадують динамічний випадок у рефераті, але теорема, яка стосується динамічного випадку, цитується у моїй відповіді (з розділу 5).
Джо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.