Skuteczne usuwanie duplikatów przy niskim obciążeniu pamięci

9

Chcę wydajnie filtrować listę liczb całkowitych dla duplikatów w taki sposób, że tylko wynikowy zestaw musi być przechowywany.

Można to zobaczyć na jeden sposób:

  • mamy szereg liczb całkowitych S={1,,N} z N duży (powiedzmy 240)
  • mamy funkcję f:SS z podobno wieloma kolizjami (obrazy są równomiernie rozmieszczone w S)
  • następnie musimy przechowywać f[S], to jest {f(x)|xS}

Mam dość dokładne (probabilistyczne) oszacowanie tego, co |f[S]| jest i dlatego może wcześniej przydzielić struktury danych (powiedzmy |f[S]|230).

Miałem kilka pomysłów, ale nie jestem pewien, jakie byłoby najlepsze podejście:

  • zestaw bitów nie wchodzi w rachubę, ponieważ zestaw danych wejściowych nie mieści się w pamięci.
  • tablica skrótów, ale (1) wymaga trochę pamięci, powiedzmy 150% z |f[S]| oraz (2) tabela musi zostać zbadana po zbudowaniu, co wymaga dodatkowego czasu z powodu narzutu pamięci.
  • „w locie”, najlepiej z O(N)złożoność (sortowanie nieporównywalne). W związku z tym nie jestem pewien, jaka jest główna różnica między sortowaniem kubełkowym a sortowaniem flash .
  • prosta tablica z binarnym drzewem wyszukiwania, ale to wymaga O(Nlog|f[S]|) czas.
  • być może użycie filtrów Blooma lub podobnej struktury danych może być przydatne w rozluźnieniu (z fałszywymi pozytywami) problemu.

Wydaje się, że niektóre pytania dotyczące stackoverflow dotyczą tego rodzaju rzeczy ( /programming/12240997/sorting-array-in-on-run-time , /programming/3951547/java -array-find-duplicates ), ale żaden nie wydaje się odpowiadać moim wymaganiom.

dok
źródło
2
Czy musisz wyliczyć f [S] (cokolwiek to jest), czy być w stanie szybko stwierdzić, czy jest w nim jakieś x?
Gilles „SO- przestań być zły”
@Gilles: Uważam, że ponieważ w f [S] nie można znaleźć oczywistej struktury, oba rozwiązania są równoważne.
dok.
Twoje liczby się nie sumują. Oczekiwany obraz losowej funkcji w dziedzinie wielkościN jest z grubsza (11/e)N. Inną kwestią jest to, że przechodzi256potrwa zbyt długo, chyba że masz do dyspozycji superkomputer lub duży klaster.
Yuval Filmus,
1
Byłby czas na drzewo wyszukiwania binarnego O(Nlog|f[S]|), które mogą, ale nie muszą, być blisko O(NlogN)w praktyce, ale nadal jest dokładniejszy.
jmad
1
Z N256, czy algorytm czasu liniowego też nie będzie przeszkodą? (Z moich obliczeń, nawet jeśli weźmiesz pod uwagę jeden elementSw 1 nanosekundę zajęłoby ci to dobre 2 lata!).
Aryabhata

Odpowiedzi:

1

Dlaczego nie bin i łańcuch?

Pomysł polega na przechowywaniu dodatnich liczb całkowitych reprezentowanych przez n=k+m bity w tablicy A z 2k wpisy reprezentujące zakresy wartości: wpis A[y], y0, reprezentuje zakres [2my,2m(y+1)1]. Dla każdego1x<2n możemy pisać x=2my+z gdzie y ma k bity i z ma mbitów Spróbuj przechowywaćz (nie x!) na miejscu y:

  • Kiedy A[y]=z już nic nie rób: x jest duplikatem.

  • Kiedy A[y] jest niezainicjowany, zapisz z w A[y].

  • W przeciwnym razie przechowuj indeks w osobnej tablicy używanej do łączenia łańcucha z(które zderzyły się w y) na połączonych listach. Będziesz musiał przeszukiwać liniowo listę kierowaną przezA[y] i, w zależności od tego, co odkryje wyszukiwanie, potencjalnie wstaw z na listę.

Na końcu, f(S) można łatwo odzyskać, zapętlając zainicjowane wpisy A oraz - łącząc tylko dwa ciągi bitów - ponownie je łącząc z znalezione w miejscu y (bezpośrednio lub w obrębie łańcucha tam wymienionego) do oryginalnej wartości x=2my+z.

Gdy rozkład jest zbliżony do jednorodnego i 2k przekracza N, łańcuchów nie będzie dużo (można to ocenić w zwykły sposób), a łańcuchy będą zwykle krótkie. Gdy rozkład jest nierównomierny, algorytm nadal działa, ale może osiągnąć kwadratowe taktowanie. Jeśli to możliwe, użyj czegoś wydajniejszego niż łańcuchy (i zapłać trochę kosztów ogólnych za przechowywanie).

Potrzebne miejsce jest co najwyżej 2n bity dla A i 22k bity dla łańcuchów (przy założeniu mk). To jest dokładnie miejsce potrzebne do przechowywania2k wartości nbitów każdy. Jeśli masz pewność co do jednolitości, możesz zbytnio przydzielić miejsce na łańcuchy. Jeśli niejednolity charakter jest możliwy, możesz chcieć zwiększyćk i w pełni popieramy przechowywanie łańcucha.

Alternatywnym sposobem myślenia o tym rozwiązaniu jest to, że jest to tablica skrótów ze szczególnie przyjemną funkcją skrótu (weźk najbardziej znaczące bity) i dlatego musimy przechowywać tylko najmniej znaczące m=nk bity w tabeli.

Istnieją sposoby nałożenia pamięci dla łańcuchów z pamięcią dla A ale nie wydaje się to warte zawracania głowy, ponieważ nie zaoszczędziłoby wiele (zakładając m jest znacznie mniejszy niż k) i utrudniają tworzenie, debugowanie i konserwację kodu.

whuber
źródło
1
Myślę, że akapit od ostatniego do ostatniego jest tutaj centralny i prawdopodobnie powinien być na górze (jako pomysł). Nie znam terminu „bin and chain” (chociaż ma sens po przeczytaniu postu). Ten pomysł można rozszerzyć na próby .
Raphael
To jest Θ(n2)na źle dystrybuowanych wejściach. Nie rozumiem, jak to jest wydajne.
einpoklum
@einpoklum Ta odpowiedź wyraźnie opisuje warunki, w których rozwiązanie jest wydajne.
whuber