Szukam implementacji ustawionego typu danych. To znaczy musimy
- utrzymywać dynamiczny podzbiór (wielkościowy ) z wszechświata wielkości u z
- operacje
insert(x)
(dodaj elementx
do ) ifind(x)
(sprawdza, czy elementx
jest członkiem ).
Nie dbam o inne operacje. Dla orientacji, w aplikacjach, z którymi pracuję mamy .
Znam implementacje, które zapewniają obie operacje w czasie , więc martwię się głównie o wielkość struktury danych. Oczekuję miliardów wpisów, ale chcę uniknąć wymiany w jak największym stopniu.
W razie potrzeby jestem gotów poświęcić środowisko wykonawcze. Mogę przyznać, że amortyzowane środowisko wykonawcze ; oczekiwane środowiska wykonawcze lub środowiska uruchomieniowe w są niedopuszczalne.
Jednym z moich pomysłów jest to, że jeśli może być reprezentowane jako połączenie zakresów [xmin, xmax]
, będziemy mogli zaoszczędzić na wielkości pamięci masowej przy cenie spadku wydajności. Możliwe są również inne wzorce danych, takie jak [0, 2, 4, 6]
.
Czy możesz wskazać mi struktury danych, które mogą zrobić coś takiego?
n
rozmiar zestawu S. Może się zwiększać z każdyminsert
lub może pozostać taki sam, jeśli elementx
jest już w zestawie.Odpowiedzi:
Odpowiedź Joe jest bardzo dobra i zawiera wszystkie ważne słowa kluczowe.
Należy pamiętać, że zwięzłe badania struktury danych są wciąż na wczesnym etapie, a wiele wyników jest w dużej mierze teoretycznych. Wiele proponowanych struktur danych jest dość skomplikowanych do wdrożenia, ale większość złożoności wynika z faktu, że musisz zachować asymptotyczną złożoność zarówno w odniesieniu do wielkości wszechświata, jak i liczby przechowywanych elementów. Jeśli którykolwiek z nich jest względnie stały, wówczas znaczna część złożoności zanika.
Jeśli kolekcja jest półstatyczna (to znaczy wstawki są rzadkie lub co najmniej niewielkie), to z pewnością warto rozważyć łatwą do wdrożenia statyczną strukturę danych (sdarray Sadakane'a jest dobrym wyborem) w połączeniu z aktualizacją Pamięć podręczna. Zasadniczo rejestrujesz aktualizacje w tradycyjnej strukturze danych (np. B-drzewo, trie, tablica skrótu) i okresowo aktualizujesz zbiorczo „główną” strukturę danych. Jest to bardzo popularna technika wyszukiwania informacji, ponieważ indeksy odwrócone mają wiele zalet wyszukiwania, ale trudno je zaktualizować w miejscu. Jeśli tak jest, proszę dać mi znać w komentarzu, a ja poprawię tę odpowiedź, aby podać kilka wskazówek.
Jeśli wstawki są częstsze, sugeruję zwięzłe haszowanie. Podstawowy pomysł jest wystarczająco prosty, aby wyjaśnić tutaj, więc zrobię to.
Tak więc podstawowe informacje teoretyczne Powoduje to, że jeśli jesteś przechowywania elementów z uniwersum elementów i nie ma innych informacji (np brak korelacji między elementami), a następnie Need You bity do przechowywania. (Wszystkie logarytmy są oparte na podstawie 2, chyba że podano inaczej). Potrzebujesz tak wielu bitów. Nie można tego obejść.n u log(un)+O(1)
Teraz trochę terminologii:
Różnica między zwięzłą a zwartą polega na różnicy między małą a dużą. Ignorując przez chwilę rzecz o wartości bezwzględnej ...
Nieoficjalnie, zarówno duże, jak i małe, są „w ramach stałego współczynnika”, ale w przypadku dużego „o” stała jest wybierana dla ciebie (przez projektanta algorytmu, producenta procesora, prawa fizyki itp.), Ale z niewielkim -oh sam wybierasz stałą i może być tak mała, jak chcesz . Innymi słowy, w zwięzłych strukturach danych względny narzut staje się arbitralnie mały wraz ze wzrostem wielkości problemu.
Oczywiście rozmiar problemu może być ogromny, aby zrozumieć względny narzut, jaki chcesz, ale nie możesz mieć wszystkiego.
OK, mając to pod naszymi paskami, podajmy numery problemu. Załóżmy, że klucze są bitowymi liczbami całkowitymi (więc rozmiar wszechświata wynosi ), i chcemy przechowywać tych liczb całkowitych. Załóżmy, że możemy magicznie zaaranżować wyidealizowany stół haszujący z pełnym obłożeniem i bez strat, tak że potrzebujemy dokładnie szczelin.n 2n 2m 2m
Operacja wyszukiwania spowoduje haszowanie klawisza bit, maskowanie bitów, aby znaleźć gniazda haszowania, a następnie sprawdzenie, czy wartość w tabeli pasuje do klucza. Na razie w porządku.n m
Taka tablica skrótów wykorzystuje bitów. Czy możemy to zrobić lepiej?n2m
Załóżmy, że funkcja skrótu jest odwracalna. Wówczas nie musimy przechowywać całego klucza w każdym polu mieszania. Lokalizacja szczeliny mieszającej daje bitów wartości skrótu, więc jeśli zachowałeś tylko pozostałe bity , możesz zrekonstruować klucz z tych dwóch informacji (lokalizacja szczeliny skrótu i przechowywana tam wartość). Tak więc potrzebujesz tylko bitów pamięci.h m n−m (n−m)2m
Jeśli jest małe w porównaniu z , przybliżenie Stirlinga i mała arytmetyka (dowód to ćwiczenie!) Ujawniają, że:2m 2n
Tak więc ta struktura danych jest zwięzła.
Istnieją jednak dwa połowy.
Pierwszym haczykiem jest zbudowanie „dobrych” odwracalnych funkcji skrótu. Na szczęście jest to o wiele łatwiejsze niż się wydaje; kryptografowie cały czas tworzą funkcje odwracalne, tylko nazywają je „szyframi”. Możesz na przykład oprzeć funkcję skrótu na sieci Feistel, która jest prostym sposobem na zbudowanie odwracalnych funkcji skrótu na podstawie nieodwracalnych funkcji skrótu.
Drugi haczyk polega na tym, że prawdziwe tabele skrótów nie są idealne dzięki paradoksowi urodzin. Więc chcesz użyć bardziej wyrafinowanego typu tabeli mieszającej, która zbliży Cię do pełnego obłożenia bez rozlewania. Mieszanie kukułki jest do tego idealne, ponieważ pozwala ci dowolnie zbliżyć się do ideału w teorii i całkiem blisko w praktyce.
Mieszanie kukułki wymaga wielu funkcji skrótu i wymaga, aby wartości w gniazdach mieszania były oznaczone przy użyciu funkcji skrótu. Na przykład, jeśli używasz czterech funkcji skrótu, musisz przechowywać dodatkowe dwa bity w każdym gnieździe skrótu. Jest to nadal zwięzłe, gdy rośnie, więc nie jest to problemem w praktyce i wciąż przewyższa przechowywanie całych kluczy.m
Och, możesz też chcieć spojrzeć na drzewa van Emde Boasa.
WIĘCEJ MYŚLI
Jeśli jest gdzieś w pobliżu , to to w przybliżeniu , więc (jeszcze raz) zakładając, że nie ma dalszej korelacji między wartościami, w zasadzie nie możesz nic zrobić lepszy niż nieco wektor. Zauważysz, że powyższe rozwiązanie haszujące skutecznie degeneruje się w tym przypadku (kończy się to zapisywaniem jednego bitu na szczelinę haszującą), ale taniej jest po prostu użyć klucza jako adresu niż funkcji haszującej.n u2 log(un) u
Jeśli jest bardzo bliskie , cała zwięzła literatura na temat struktur danych zaleca odwrócenie sensu słownika. Przechowuj wartości, które nie występują w zestawie. Jednak teraz musisz skutecznie wspierać operację usuwania i aby zachować zwięzłe zachowanie, musisz także być w stanie zmniejszyć strukturę danych w miarę dodawania kolejnych elementów. Rozszerzanie tabeli skrótów jest dobrze zrozumiałą operacją, ale jej nie jest.n u
źródło
insert
będzie towarzyszyć wezwanie dofind
z tym samym argumentem. Więc jeślifind
zwrotytrue
, po prostu pomijamyinsert
. Tak więc częstotliwośćfind
połączeń jest większa niż częstotliwośćinsert
połączeń, również gdyn
zbliża sięu
, wtedyinsert
połączenia stają się bardzo rzadkie.n <= u
Wygląda na to, że potrzebujesz zwięzłej struktury danych dla problemu dynamicznego członkostwa .
Przypomnijmy, że zwięzła struktura danych to taka, dla której zapotrzebowanie na miejsce jest „bliskie” teoretycznej dolnej granicy, ale w przeciwieństwie do skompresowanej struktury danych, nadal pozwala na wydajne zapytania.
Problemem członkostwa jest dokładnie to, co można opisać w swoim pytaniu:
find(x)
(sprawdza, czy elementx
jest członkiem ).insert(x)
(dodaj elementx
do )delete(x)
(usuń elementx
z )Jeśli
find
obsługiwana jest tylko operacja, jest to problem statycznego członkostwa. Jeżeli jednainsert
lubdelete
są obsługiwane, ale nie jednocześnie, nazywa się częściowo dynamiczne , a jeśli wszystkie trzy operacje są obsługiwane, to nazywa się dynamiczną problemem członków.Technicznie rzecz biorąc, myślę, że poprosiłeś tylko o strukturę danych dla pół-dynamicznego problemu członkostwa, ale nie znam żadnych struktur danych, które wykorzystałyby to ograniczenie i spełniałyby twoje inne wymagania. Mam jednak następujące odniesienie:
W Twierdzeniu 5.1 artykułu Członkostwo w stałym czasie i prawie minimalnej przestrzeni Brodnik i Munro dają następujący wynik:
gdzie to teoretyczna minimalna wymagana liczba bitów informacji.B=⌈log(un)⌉
Podstawową ideą jest to, że rekurencyjnie dzielą wszechświat na zakres starannie dobranych rozmiarów, więc nawet brzmi to tak, jakby techniki mogły być zgodne z myślami.
Jeśli jednak szukasz czegoś, co rzeczywiście możesz wdrożyć, nie wiem, czy to będzie najlepszy wybór. Przejrzałem tylko gazetę, a próba wyjaśnienia szczegółów wykracza daleko poza zakres tej odpowiedzi. Sparametryzowali swoje rozwiązanie, stosując różne strategie w zależności od względnych rozmiarów i . A dynamiczna wersja struktury danych jest tylko naszkicowana na papierze.u n
źródło
n = u/2
, to potrzebna przestrzeń jest maksymalna.