2009-05-05 9 views
4

Die Leute sagen, es dauert amortisiert O (1) in eine Hash-Tabelle zu setzen. Daher müssen n Elemente O (n) sein. Das stimmt jedoch nicht für große n, da ein Beantworter sagte: "Alles, was Sie benötigen, um das erwartete amortisierte O (1) zu erfüllen, ist, die Tabelle zu erweitern und alles mit einer neuen zufälligen Hash-Funktion zu wiederholen, wenn eine Kollision auftritt."Laufzeit zum Einfügen von n Elementen in eine leere Hash-Tabelle

Also: Was ist die durchschnittliche Laufzeit von Einfügen von n Elementen in eine Hash-Tabelle? Mir ist klar, dass dies wahrscheinlich von der Implementierung abhängig ist. Erwähnen Sie daher, von welcher Art von Implementierung Sie sprechen.

Zum Beispiel, wenn es (log n) mit gleichem Abstand Kollisionen und jede Kollision nimmt O (k) zu lösen, wobei k die aktuelle Größe der Hash-Tabelle ist, dann würden Sie diese Rekursion haben:

(das heißt, Sie nehmen sich die Zeit, n/2 Elemente einzufügen, dann haben Sie eine Kollision, n/2 zu lösen, dann machen Sie die restlichen n/2 Einsätze ohne eine Kollision). Dies endet immer noch in O (n), also yay. Aber ist das vernünftig?

Antwort

5

Es hängt vollständig davon ab, wie ineffizient Ihre Umwertung ist. Insbesondere wenn Sie die erwartete Größe Ihrer Hashtable das zweite Mal richtig schätzen können, nähert sich Ihre Laufzeit weiterhin O (n) an. Effektiv müssen Sie angeben, wie ineffizient die Berechnung der Wiederverwendungsgröße ist, bevor Sie die erwartete Reihenfolge bestimmen können.

+0

Beachten Sie, dass Sie in vielen Implementierungen die erwartete Größe der vollständigen Hashmap angeben können. Wenn n bekannt ist, bevor Sie mit dem Füllen der Map beginnen, ist die erwartete Laufzeit immer noch O (1). – gnud

+0

@gnud, das war mein genauer Punkt; Das erneute Laden ist nur notwendig, wenn Sie die ursprüngliche Größe falsch erhalten (oder die nachfolgende Größe falsch erhalten und erneut aufbereitet werden müssen usw.). –

+0

Ja, ich weiß - du hast über das Schätzen der Größe zum zweiten Mal geschrieben. Ich dachte, ich sollte erwähnen, dass es oft möglich ist, die Größe beim ersten Mal anzugeben =) – gnud

0

Warum nicht einfach ein paar Tests auf Ihrem System durchführen? Vielleicht, wenn Sie die Quelle veröffentlichen, können wir zurückgehen und sie auf unseren Systemen testen und wir könnten das wirklich zu einer sehr nützlichen Diskussion formen.

Es ist einfach nicht die Implementierung, sondern die Umgebung entscheidet darüber, wie viel Zeit der Algorithmus tatsächlich benötigt. Sie können jedoch prüfen, ob Benchmarking-Stichproben verfügbar sind oder nicht. Das Problem, dass ich meine Ergebnisse gepostet habe, wird nutzlos sein, da die Leute keine Ahnung haben, was sonst auf meinem System läuft, wie viel RAM im Moment frei ist und so weiter. Du kannst immer nur eine Idee haben. Und das ist ungefähr so ​​gut wie das, was dir das große O gibt.

5

Die Leute sagen, es dauert amortisiert O (1) in eine Hash-Tabelle zu setzen.

Aus theoretischer Sicht ist es erwartet abgeschrieben O (1).

Hashtabellen sind grundsätzlich eine randomisierte Datenstruktur, in dem Sinne, dass Quicksort ein randomisierter Algorithmus ist. Sie müssen Ihre Hash-Funktionen mit einer gewissen Zufälligkeit generieren, sonst existieren pathologische Eingaben, die nicht O (1) sind.

Sie erwarten erreichen können abgeschrieben O (1) mit dynamic perfect hashing:

Die naiven Idee, die ich ursprünglich geschrieben auf jeder Kollision mit einer neuen zufälligen Hash-Funktion wieder aufwärmen. (Siehe auch perfect hash functions) Das Problem dabei ist, dass dies O (n^2) Raum vom Geburtstagsparadox erfordert.

Die Lösung ist zwei Hash-Tabellen zu haben, mit der zweiten Tabelle für Kollisionen; Lösen Sie Kollisionen an dieser zweiten Tabelle auf, indem Sie sie neu erstellen. Diese Tabelle wird O (\ sqrt {n}) -Elemente haben und somit auf O (n) wachsen. In der Praxis verwenden Sie oft nur eine feste Hash-Funktion, weil Sie davon ausgehen können, dass Ihre Eingabe pathologisch ist (ähnlich wie Sie oft Quicksort, ohne die Eingabe vorzuspannen).

+0

Also hier ist meine Frage genau. Sie sagen: "Alles, was Sie benötigen, um das erwartete amortisierte O (1) zu erfüllen, ist, die Tabelle zu erweitern und alles mit einer neuen zufälligen Hash-Funktion zu wiederholen, wenn eine Kollision auftritt." Lass uns sagen, dass du das tust. Wenn Sie keine Kollision mit n Einfügungen haben, dann haben Sie definitiv O (n). Aber wie hoch ist die erwartete Anzahl an Kollisionen pro n Elemente und wie lange dauert es, bis diese gelöst sind? Dann können wir eine genauere Anzahl für n Einfügungen in eine Hash-Tabelle erhalten. Etwas wie O (n + #col * coltime) - vielleicht O (n + (log n)^2)? – Claudiu

+0

Korrigiert. Ich hatte vergessen, dass der Trick war, einen zweiten Tisch zu haben; Einfaches Wiederholen bei jeder Kollision würde O (n^2) Raum wegen des Geburtstagsparadoxons erfordern. –

1

Alle O (1) sagt, dass die Operation in konstanter Zeit ausgeführt wird, und es ist nicht abhängig von der Anzahl der Elemente in Ihrer Datenstruktur.

In einfachen Worten bedeutet dies, dass Sie die gleichen Kosten bezahlen müssen, egal wie groß Ihre Datenstruktur ist.

In der Praxis bedeutet dies, dass einfache Datenstrukturen wie Bäume im Allgemeinen effektiver sind, wenn Sie nicht viele Daten speichern müssen. Nach meiner Erfahrung finde ich Bäume schneller bis zu ~ 1k Elementen (32bit Ganzzahlen), dann übernehmen Hashtabellen. Aber wie immer YMMW.