Mam dwóch dużych zestawów liczb całkowitych i B . Każdy zestaw ma około miliona wpisów, a każdy wpis jest dodatnią liczbą całkowitą o długości co najwyżej 10 cyfr.
Jaki jest najlepszy algorytm do obliczania i B ∖ A ? Innymi słowy, jak mogę skutecznie obliczyć listę pozycji A , które nie są w B i odwrotnie? Jaka byłaby najlepsza struktura danych reprezentująca te dwa zestawy, aby te operacje były wydajne?
Najlepszym podejściem, jakie mogę wymyślić, jest przechowywanie tych dwóch zestawów jako posortowanych list i porównywanie każdego elementu z każdym elementem B w sposób liniowy. Czy możemy zrobić lepiej?
algorithms
data-structures
sets
użytkownik917279
źródło
źródło
Odpowiedzi:
Jeśli chcesz przechowywać zestawy w specjalnej strukturze danych, możesz uzyskać ciekawe komplikacje.
NiechI=O(min(|A|,|B|,|AΔB|))
Następnie możesz ustawić operacje i A Δ B , każda w O ( I ⋅ log | A | + | B |A∪B,A∩B,A∖B AΔB oczekiwany czas. Zasadniczo otrzymujesz minimalny rozmiar dwóch zestawów lub rozmiar różnicy symetrycznej, w zależności od tego, która jest mniejsza. Jest to lepsze niż liniowe, jeśli różnica symetryczna jest niewielka; to znaczy. jeśli mają duże skrzyżowanie. W rzeczywistości dla dwóch żądanych operacji ustawiania różnicy jest to praktycznie zależne od wyników, ponieważ razem stanowią one wielkość różnicy symetrycznej.O(I⋅log|A|+|B|I)
Aby uzyskać więcej informacji, zobacz Zestawy i mapy o trwałej spójności autorstwa Olle Liljenzin (2013).
źródło
Skan liniowy jest najlepszym, co wiem, jak to zrobić, jeśli zestawy są reprezentowane jako posortowane listy połączone. Czas działania to .O(|A|+|B|)
Zauważ, że nie musisz porównywać każdego elementu z każdym elementem B , parami. Prowadziłoby to do czasu wykonania O ( | A | × | B | ) , co jest znacznie gorsze. Zamiast tego, aby obliczyć różnicę symetryczną tych dwóch zestawów, można użyć techniki podobnej do operacji „scalania” w trybie scalania, odpowiednio zmodyfikowanej w celu pominięcia wartości wspólnych dla obu zestawów.A B O(|A|×|B|)
Bardziej szczegółowo, możesz zbudować algorytm rekurencyjny, taki jak poniżej, aby obliczyć , zakładając, że A i B są reprezentowane jako listy połączone z ich wartościami w uporządkowanej kolejności:A∖B A B
Reprezentowałem to w pseudo-Pythonie. Jeśli nie czytasz Pythona,
A[0]
jest on głową połączonej listyA
,A[1:]
jest resztą listy i+
reprezentuje konkatenację list. Ze względu na wydajność, jeśli pracujesz w Pythonie, prawdopodobnie nie chciałbyś go wdrożyć dokładnie tak, jak powyżej - na przykład lepiej byłoby użyć generatorów, aby uniknąć tworzenia wielu list tymczasowych - ale chciałem pokażę Ci pomysły w najprostszej możliwej formie. Celem tego pseudokodu jest jedynie zilustrowanie algorytmu, a nie zaproponowanie konkretnej implementacji.Nie sądzę, aby można było zrobić coś lepszego, jeśli twoje zbiory są reprezentowane jako listy posortowane i chcesz, aby dane wyjściowe były dostarczane jako lista posortowana. Ci zasadniczo trzeba patrzeć na każdy element i B . Nieformalny szkic uzasadnienia: jeśli istnieje jakikolwiek element, którego nie obejrzałeś, nie możesz go wydrukować, więc jedynym przypadkiem, w którym możesz pominąć patrzenie na element, jest to, że wiesz, że jest on obecny zarówno w A, jak i B , ale skąd możesz wiedzieć, że jest obecny, jeśli nie spojrzałeś na jego wartość?A B A B
źródło
Jeśli A i B są równej wielkości, rozłączne i przeplatane (np. Liczby nieparzyste w A i liczby parzyste w B), to porównanie par elementów w czasie liniowym jest prawdopodobnie optymalne.
Jeśli A i B zawierają bloki elementów, które znajdują się dokładnie w jednym z A lub B, lub w obu, można obliczyć ustawioną różnicę, sumę i przecięcie w czasie subliniowym. Na przykład, jeśli A i B różnią się dokładnie jednym przedmiotem, różnicę można obliczyć w O (log n).
http://arxiv.org/abs/1301.3388
źródło
źródło
long
może pomieścić 32 elementy lub 1byte
, 8 elementów. więc wpisy 1M mogą być przechowywane tylko w ~ 125K pamięci RAM! pamięć może być znacznie wydajniejsza niż inne reprezentacje, w zależności od sposobu zaimplementowania problemu ...