C# - Binäre Bäume-und Wörterbücher
Bin ich zu kämpfen mit dem Konzept der Verwendung von binary search trees und als Wörterbücher verwenden.
In meiner Anwendung habe ich ein kleines experiment, das den C5 Bibliothek TreeDictionary
(was ich glaube, ist ein red-black binary search tree), und der C# - Wörterbuch. Das Wörterbuch war immer schneller bei add/find-Operationen und auch immer weniger Speicherplatz. Zum Beispiel, bei 16809 <int, float>
Einträge, das Wörterbuch 342 Kb, während der Baum verwendet 723 KiB.
Dachte ich, dass BST ist eigentlich mehr Speicher effizient, aber es scheint, dass ein Knoten des Baumes erfordert mehr bytes als ein Eintrag in einem Wörterbuch. Was gibt? Gibt es einen Punkt, an dem BST-s sind besser als Wörterbücher?
Auch, als eine Seite Frage, weiß jemand, ob es ein schneller + mehr Speicher effizienten Datenstruktur für die Speicherung <int, float>
Paare für dictionary-Datentyp Zugriff als die beiden genannten Strukturen?
- Ganz ehrlich, ich würde nicht sorgen über die Speicher-Effizienz, wenn die app ist mit 723 KB. Ich würde wahrscheinlich anfangen, darüber nachzudenken, bessere Datenstrukturen wenn ich, sagen wir, 50 MB zum speichern der Sammlung.
- Das Objekt hält die Daten-Struktur haben könnte, Tausende von Instanzen, so dass dann jedes kB zählt.
- Versuchen
SortedList<T,K>
- es sollte die niedrigste Speicher-overhead der verschiedenen Optionen. Wenn es nicht zu langsam (in Ihrer Anwendung) und je KB ist wirklich egal, es scheint sicher lebensfähig. Hinzufügen/entfernen wird langsamer, aber lookup sollte ähnlich der BST.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Habe ich persönlich noch nie gehört, solch ein Prinzip. Sogar noch, es ist nur ein allgemeiner Grundsatz, nicht eine kategorische Tatsache tief in das Gewebe des Universums.
Allgemein, Wörterbücher sind wirklich nur ein schicker wrapper um ein array von verknüpften Listen. Fügen Sie in das Wörterbuch etwas wie:
So seine fast O(1) - operation. Das Wörterbuch verwendet O(internalArray.Länge + n) Speicher, wobei n die Zahl der Elemente in der Auflistung.
Im Allgemeinen BSTs umgesetzt werden können, wie:
Verschiedenheit der C5 TreeDictionary ist implementiert mit arrays, welche wahrscheinlich verantwortlich für die verschwendeten Speicherplatz.
Wörterbücher haben einige unerwünschte Eigenschaften:
Kann es nicht genug sein continugous Blöcke von Speicher zu halten, Ihr Wörterbuch, auch wenn die Anforderungen an den Arbeitsspeicher sind sehr viel weniger als die insgesamt verfügbaren RAM.
Auswertung der hash-Funktion eine beliebig lange Zeitspanne. Zeichenfolgen, zum Beispiel, verwenden den Reflektor zu prüfen, die
System.String.GetHashCode
Methode -- Sie werden bemerken, hashing ein string immer in O(n) Zeit, was bedeutet, es kann erhebliche Zeit dauern, sehr lange strings. Auf der Seite, Vergleich von strings auf Ungleichheit fast immer schneller als hashing, da kann es erfordern, dass man sich bei den ersten paar chars. Seine ganz möglich, um den Baum einfügt, um schneller als Wörterbuch einfügt, wenn die hash-code-Auswertung zu lange dauert.GetHashCode
Methode ist buchstäblich nurreturn this
, so dass Sie würde hardpressed zu finden, ein Fall, wo ein int hashtable mit keys ist langsamer als ein Baum Wörterbuch.RB Bäume haben einige wünschenswerte Eigenschaften:
Finden Sie/entfernen Sie die Min-und Max-Elementen in O(log n) Zeit, im Gegensatz zu O(n) Zeit mit einem Wörterbuch.
Wenn ein Baum wird implementiert als verkettete Liste statt einem array, der Baum ist in der Regel mehr Raum effizienter als ein Wörterbuch.
Ebenfalls, seine lächerlich einfach zu schreiben, unveränderliche Versionen von Bäumen, die Unterstützung von insert - /lookup/löschen in O(log n) Zeit. Wörterbücher nicht passen gut zu der Unveränderlichkeit, da müssen Sie kopieren die gesamte interne array-für jeden Betrieb (tatsächlich, ich haben gesehen, einige array-basierte Implementierungen von unveränderlich finger-Bäume, eine Art Allzweck-dictionary-Datenstruktur, aber die Umsetzung ist sehr Komplex).
Können Sie Durchlaufen aller Elemente eines Baumes in sortierter Reihenfolge in der ständigen Platz-und O(n) Zeit, während Sie brauchen, um zu dump eine hash-Tabelle in ein array und sortiert es den gleichen Effekt zu erhalten.
So, die Wahl der Datenstruktur hängt wirklich davon ab, welche Eigenschaften, die Sie benötigen. Wenn Sie wollen einfach nur eine ungeordnete Tasche und können garantieren, dass Ihre hash-Funktion bewerten, schnell, gehen mit ein .Net-Wörterbuch. Wenn Sie eine Tasche bestellt, oder haben eine langsam Laufenden hash-Funktion, gehen Sie mit TreeDictionary.
Macht es Sinn, dass ein Baum Knoten würden die mehr Speicherplatz benötigen, als ein Wörterbuch-Eintrag. Ein binärer Baum, Knoten braucht um den Wert zu speichern und sowohl die linken und rechten Teilbäume. Die generische
Dictionary<TKey, TValue>
ist implementiert als hash-Tabelle, die - ich nehme an - entweder verwendet eine verkettete Liste für jede Gruppe (Wert plus eins Zeiger/Referenz) oder irgendeine Art von Zuordnung (nur der Wert). Ich würde einen Blick in den Reflektor, um sicher zu sein, aber für die Zwecke dieser Frage, ich glaube es ist nicht so wichtig.Je spärlicher die hash-Tabelle, die weniger effizient in Bezug auf die Speicher/Speicher. Wenn Sie erstellen Sie eine hash-Tabelle (dictionary) und initialisieren seine Kapazität für 1 million, und nur füllen Sie es mit 10.000 Elemente, dann ich bin ziemlich sicher, dass es Essen würde up viel mehr Speicher als eine BST mit 10.000 Knoten.
Immer noch, ich würde nicht sorgen über irgendwelche von diesem, wenn die Menge der Knoten/Schlüsseln ist nur in die Tausende. Das wird gemessen in Kilobyte, die im Vergleich zu GB physischen RAM.
Wenn die Frage "warum würden Sie wollen, zu einem binären Baum anstelle einer hash-Tabelle?" Dann ist die beste Antwort, die IMO ist, dass binäre Bäume bestellt werden, in der Erwägung, dass hash-Tabellen nicht. Sie können nur suchen Sie eine hash-Tabelle für Tasten, die sind genau gleich, um etwas; mit einem Baum, Sie können suchen, für eine Reihe von Werten, nächsten Wert usw. Dies ist eine sehr wichtige Unterscheidung, wenn Sie erstellen einen index oder ähnliches.
Mir scheint, du machst eine vorzeitige Optimierung.
Ich würde Ihnen vorschlagen, ist eine Schnittstelle zu erstellen, zu isolieren, die Struktur, die Sie tatsächlich verwenden, und implementieren Sie das interface mit dem Wörterbuch (das scheint am besten zu funktionieren).
Wenn der Speicher/performance zum Thema wird (was wahrscheinlich nicht für 20k - zahlen), dann können Sie andere interface-Implementierungen, und überprüfen Sie, welche works Bestleistungen. Sie nicht brauchen, um zu ändern, fast alles in der rest des Codes (es sei denn, die Implementierung, die Sie verwenden).
Schnittstelle für einen Baum und eine Hash-Tabelle (was ich vermute, ist das, was Ihr Wörterbuch basiert) sollte sehr ähnlich sein. Immer rund um keyed-lookups.
Hatte ich immer gedacht, ein Wörterbuch war besser für Dinge, die Sie einmal erstellen und dann tun eine Menge von verweisen auf Sie. Während ein Baum war besser, wenn Sie ändern es deutlich. Allerdings weiß ich nicht, wo ich wieder, dass die Idee aus.
(Funktionale Sprachen verwenden oft Bäume als Grundlage für diese Sammlungen als Sie wieder verwenden können die meisten von dem Baum, wenn Sie machen kleine änderungen).
Sind Sie nicht zu vergleichen "äpfel mit äpfeln", eine BST-geben Sie eine bestellt Darstellung, während ein Wörterbuch erlaubt Ihnen, eine Suche auf ein Schlüssel-Wert-paar (in deinem Fall ).
Ich würde nicht erwarten, viel Größe in den Speicher-footprint zwischen den 2, aber das Wörterbuch wird Ihnen eine viel schnellere Suche. Finden Sie ein Element in einem BST du (potentiell) brauchen zum Durchlaufen der gesamten Baumstruktur. Aber dazu ein dictnary Suche Sie einfach lookup basierend auf dem Schlüssel.
Einer ausgewogenen BST ist vorzuziehen, wenn Sie brauchen, um Ihre Daten-Struktur von latency-spikes und hash-Kollisionen Angriffe.
Den ehemaligen passiert, wenn ein array-backed-Struktur wächst eine wird geändert, letzteres ist eine unvermeidliche Eigenschaft der hashing-Algorithmus, der wie eine Projektion aus dem unendlichen Raum auf eine begrenzte integer-Bereich.
Ein anderes problem .NET ist, dass es LOH, und mit einer ausreichend großen Wörterbuch führen Sie in eine LOH-Fragmentierung. In diesem Fall können Sie einen BST, zahlt Sie den Preis des größeren Algorithmische Komplexität-Klasse.
Kurz, mit einem BST-unterstützt von der heap-Größe für Sie bekommen schlimmsten Fall O(log(N)) Zeit, mit hashtable-Sie erhalten O(N) worst case Zeit.
BST kommt zu einem Preis von O(log(N)) die Durchschnittliche Zeit, schlimmer cache-Lokalität und mehrere heap-Zuweisungen, aber es hat Latenz garantiert und geschützt ist von Wörterbuch-Attacken und die Fragmentierung des Speichers.
Erwähnenswert, dass BST ist auch ein Thema, um die Fragmentierung des Speichers auf anderen Plattformen, nicht mit einem compacting garbage collector.
Als Speicher für die Größe, die .NET Dictionary`2. Klasse ist mehr Speicher effizient, denn es speichert die Daten als ein off-heap-Link-Liste, die speichert nur Wert-und offset-Informationen.
BST zum speichern von Objekt-header (da jeder Knoten ist eine Instanz der Klasse auf dem heap), zwei Zeiger, und einige augmented-Baum Daten für ausgewogene Bäume. Zum Beispiel, eine rot-schwarz-Baum müsste ein boolean interpretiert werden, wie Farbe (rot oder schwarz). Dieser ist mindestens 6 in Worten, wenn ich mich nicht Irre. Also, jeder Knoten in einem rot-schwarz Baum auf 64-bit-system mindestens:
3 Wörter für die header = 24 Byte
2 Wörter für das Kind Zeiger = 16 bytes
1 Wort für die Farbe = 8 bytes
mindestens 1 Wort für den Wert 8+ bytes
= 24+16+8+8 = 56 bytes+8 bytes, wenn der Baum nutzt einen übergeordneten Knoten Zeiger).
Zur gleichen Zeit, die minimale Größe der Wörterbuch-Eintrag wäre nur 16 bytes.