Drzewa binarne C # i słowniki

głosy
15

Ja zmagam się z pojęciem kiedy używać binarnych drzew wyszukiwania i kiedy używać słowników.

W mojej aplikacji Zrobiłem mały eksperyment, który korzystał z biblioteki C5 TreeDictionary(co moim zdaniem jest czerwono-czarny przeszukiwania binarnego drzewa) oraz słownika C #. Słownik zawsze był szybszy w dodaj / znaleźć operacji, a także zawsze mniej miejsca w pamięci. Na przykład, przy 16809 <int, float>wpisów, słownik używany 342 KiB podczas gdy drzewa użytego 723 KiB.

Myślałem, że BST były miało być więcej pamięci efektywne, ale wydaje się, że jeden węzeł drzewa wymaga więcej bajtów niż jeden wpis w słowniku. Co daje? Czy istnieje punkt, w którym BST są lepsze niż słowników?

Ponadto, jako pytanie z boku, czy ktoś wie, czy jest szybsza + więcej pamięci efektywne struktury danych do przechowywania <int, float>pary dla słownika dostępu typu niż jeden z wymienionych struktur?

Utwórz 28/01/2010 o 02:46
źródło użytkownik
W innych językach...                            


6 odpowiedzi

głosy
1

Wydaje mi się, że robisz przedwczesne optymalizacji.

Co sugeruję wam jest stworzenie interfejsu do izolacji których struktura jest rzeczywiście używany, a następnie zaimplementować interfejs za pomocą słownika (co wydaje się działać najlepiej).

Jeśli pamięć / wydajność staje się problem (który prawdopodobnie nie będzie dla 20k- numerów), a następnie można tworzyć inne implementacje interfejsu i sprawdzić, który z nich działa najlepszych. Nie będzie trzeba zmienić prawie wszystko w pozostałej części kodu (z wyjątkiem których realizacja używasz).

Odpowiedział 28/01/2010 o 03:26
źródło użytkownik

głosy
1

To bez sensu, że węzeł drzewa musiałby otrzymać więcej miejsca niż słowniku wpisu. Węzeł drzewa binarne potrzebuje do przechowywania wartości i jednocześnie jego lewy i prawy poddrzewa. Generyczny Dictionary<TKey, TValue>jest zaimplementowany jako tabeli mieszania, które - jestem przy założeniu - albo wykorzystuje połączonej listy dla każdego segmentu (wartość plus jeden wskaźnik / odniesienia) lub jakimś remapping (tylko wartości). Musiałbym mieć okiem reflektor, aby upewnić się, ale dla celów tego pytania nie sądzę, że to jest ważne.

Rzadszy tabeli mieszania, tym mniej efektywne pod względem przechowywania / pamięci. Jeśli utworzyć tabelę hash (słownik) i zainicjować jego zdolności do 1 miliona, a tylko wypełnić go z 10000 elementów, to jestem całkiem pewien, że pochłonie dużo więcej pamięci niż BST z 10000 węzłów.

Mimo to, nie będę się martwić o cokolwiek z tego, jeśli ilość węzłów / kluczy jest tylko w tysiącach. Że będzie mierzona w kilobajtach, w porównaniu do gigabajtów pamięci RAM.


Jeśli pytanie brzmi „dlaczego chcesz używać binarne drzewo zamiast tabeli mieszania?” Wtedy najlepszą odpowiedzią IMO jest to drzewo binarne są sortowane natomiast tabele mieszania nie są. Można wyszukiwać tylko stolik hash dla kluczy, które są dokładnie równe czemuś; z drzewa, można wyszukać zakres wartości, najbliższej wartości itp Jest to dość istotna różnica, jeśli tworzysz indeks lub coś podobnego.

Odpowiedział 28/01/2010 o 03:39
źródło użytkownik

głosy
0

Interfejs na drzewo i tabeli mieszania (co zgaduję, co Twój Słownik oparty jest jeden) powinny być bardzo podobne. Zawsze obraca się wokół klinują sprawdzenia.

Zawsze myślałem Dictionary był lepszy do tworzenia rzeczy raz i wtedy robi wiele wyszukiwań na nim. Podczas gdy drzewo było lepiej, jeśli były znacznie modyfikując je. Jednak nie wiem, gdzie wybrałem ten pomysł się od.

(Języki funkcjonalne często korzystają z drzew jako podstawa do ich zbiorów, jak można ponownie korzystać z większości drzewa, jeśli dokonać drobnych zmian do niego).

Odpowiedział 28/01/2010 o 03:40
źródło użytkownik

głosy
0

Nie jesteś porównywanie „jabłka z jabłkami”, BST daje uporządkowaną reprezentację podczas słownika pozwala zrobić odnośnika na parę kluczowych wartości (w danym przypadku).

Nie spodziewałbym się zbyt wiele miejsca w pamięci ślad między 2, ale słownika daje znacznie szybsze wyszukiwanie. Aby znaleźć przedmiot w BST ty (potencjalnie) muszą przechodzić przez całe drzewo. Ale zrobić dictnary wyszukiwanie po prostu odnośnika na podstawie klucza.

Odpowiedział 28/01/2010 o 04:05
źródło użytkownik

głosy
8

Myślałem, że BST były miało być więcej pamięci efektywne, ale wydaje się, że jeden węzeł drzewa wymaga więcej bajtów niż jeden wpis w słowniku. Co daje? Czy istnieje punkt, w którym BST są lepsze niż słowników?

Ja osobiście nigdy nie słyszałem o takiej zasady. Nawet jeszcze jej jedynie ogólną zasadę, a nie kategoryczne fakt wyryte w tkaninie wszechświata.

Generalnie Słowniki są naprawdę tylko wyobraźnia otoki wokół tablicy połączonych listach. Po włożeniu do słownika coś takiego:

LinkedList<Tuple<TKey, TValue>> list =
    internalArray[internalArray % key.GetHashCode()];
if (list.Exists(x => x.Key == key))
    throw new Exception("Key already exists");
list.AddLast(Tuple.Create(key, value));

Tak jej prawie O (1) operacji. Słownika wykorzystuje O (internalArray.Length + n) pamięci, gdzie n jest liczba elementów zbioru.

W ogólnych BSTS mogą być realizowane jako:

  • połączone listach, które wykorzystują O (n) przestrzeń, w której n jest liczba elementów w zbiorze.
  • Macierze , które wykorzystują wyjścia (2 h - n) przestrzeń, gdzie h jest wysokością drzewa, a n jest liczbą elementów zbioru.
    • Od czarnymi drzewa mają ograniczone wysokość 1,44 * O (n), implementacja tablicy powinien mieć ograniczone użycie pamięci o O (2 1.44n - n)

Szanse są, C5 TreeDictionary jest realizowany za pomocą tablic, który jest prawdopodobnie odpowiedzialny za niewykorzystanego miejsca.

Co daje? Czy istnieje punkt, w którym BST są lepsze niż słowników?

Słowniki mają pewne niepożądane właściwości:

  • Nie może nie być wystarczająco continugous bloki pamięci trzymać słownika, nawet jeśli jego wymagania dotyczące pamięci są znacznie mniej niż niż całkowitej dostępnej pamięci RAM.

  • Oceny funkcji skrótu może trwać dowolnie długi czas. Struny, na przykład użyć reflektor zbadać System.String.GetHashCodemetody - zauważysz mieszania ciąg zawsze zajmuje O (n) czasu, co oznacza, że może to zająć dużo czasu na bardzo długich ciągów. Na rękę, porównywania ciągów dla nierówności prawie zawsze szybciej niż mieszania, gdyż może wymagać patrząc na zaledwie kilku pierwszych znaków. Jest całkowicie możliwe do wkładki drzewo się szybciej niż słownikowych wkładkami jeśli ocena kod hash trwa zbyt długo.

    • Int32 za GetHashCodemetoda jest dosłownie return this, więc chcesz być hardpressed znaleźć przypadek, gdy hashtable z int klawiszy jest wolniejszy niż słownika drzewa.

RB Drzewa mają pewne pożądane właściwości:

  • Można znaleźć / usunąć elementy MIN i MAX w czasie O (log n), w porównaniu do O (n) czasu używania słownika.

  • Jeśli drzewo jest zaimplementowany jako połączonej listy zamiast tablicy, drzewo jest zazwyczaj bardziej wydajne niż przestrzeń słownika.

  • Podobnie, jego śmieszne łatwo pisać niezmienne wersje drzew, które wspierają insert / wyszukiwanie / usuwać w czasie O (log n). Słowniki nie dobrze dostosować się do niezmienności, ponieważ trzeba skopiować całą tablicę wewnętrznego dla każdej operacji (faktycznie, ja nie widziałem niektóre implementacje tablic opartych na niezmiennych drzew palców, rodzajem ogólnego przeznaczenia słowniku struktury danych, ale realizacja jest bardzo złożony).

  • Możesz przechodzić wszystkie elementy w drzewie posortowanych w stałej przestrzeni i O (n), natomiast trzeba by zrzucić tabeli mieszania do tablicy i sortować je, aby uzyskać ten sam efekt.

Tak więc, wybór struktury danych zależy od tego, jakie właściwości trzeba. Jeśli chcesz tylko nieuporządkowaną torbę i może zagwarantować, że funkcja hash oceniać szybko przejść z .NET Dictionary. Jeśli potrzebujesz uporządkowaną torbę lub posiada funkcję powolnego działa hash, iść z TreeDictionary.

Odpowiedział 28/01/2010 o 04:16
źródło użytkownik

głosy
0

Zrównoważona BST jest korzystne, gdy trzeba chronić swoją strukturę danych z opóźnieniami kolcami i kolizji hash ataków.

Były się dzieje, gdy zespół z rozmieszczonymi oparciem rośnie zostanie zmieniony, to ostatnie jest nieuniknione własności mieszania algorytmem rzucie z nieskończonej przestrzeni się do ograniczonej liczby całkowitej.

Innym problemem jest to, że w .NET jest LOH, a przy dostatecznie dużym słowniku napotkasz fragmentacji LOH. W tym przypadku można użyć BST, płacąc cenę większej złożoności algorytmicznej klasie.

W skrócie, z BST poparte sterty alokacji dostać najgorszym przypadku O (log (n)) razem z hashtable dostać O (N) najgorszy czas sprawy.

BST przychodzi po cenie O (log (n)) średni czas, gorszy cache miejscowości i więcej alokacji sterty, ale ma gwarancji latencji i jest zabezpieczony przed atakami i słownikowych fragmentacji pamięci.

Warto zauważyć, że BST jest również ulec fragmentacji pamięci na innych platformach, a nie za pomocą zagęszczania śmieciarza.

Co do wielkości pamięci, klasa .NET Dictionary`2 jest bardziej wydajna pamięć, ponieważ przechowuje dane jako połączonej listy off-kupa, wartość, która wyłącznie magazynuje oraz przesunięcie informacji. BST ma do przechowywania obiektów nagłówek (jak każdy węzeł jest instancją klasy na stercie), dwa wskaźniki, a niektóre dane Augmented drzewa dla zrównoważonych drzew. Na przykład drzewo czerwono-czarne musiałby wartość logiczną interpretowany jako kolor (czerwony lub czarny). Jest to co najmniej 6 maszynowe słowa, jeśli się nie mylę. Tak więc, każdy węzeł w drzewo czerwono-czarne na systemie 64-bitowym wynosi minimum:

3 słowa nagłówka = 24 Bajty 2 słowa dla wskaźników dzieci = 16 bajtów 1 słowo koloru = 8 bajtów przynajmniej jedno słowo, którego wartość bajtów = 8 + 24 + 16 + 8 + 8 = 56 bajtów (+8 bajtów jeśli drzewo używa węzeł nadrzędny wskaźnik).

Jednocześnie, minimalna wielkość wpisu słowniku byłoby zaledwie 16 bajtów.

Odpowiedział 10/12/2018 o 13:18
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more