it-swarm.com.de

Was ist schneller, Hash-Lookup oder Binärsuche?

Wenn eine statische Menge von Objekten (statisch in dem Sinne, dass sie einmal geladen wurden, wenn sich nur selten Änderungen ergeben) gegeben wird, bei denen wiederholte gleichzeitige Suchvorgänge mit optimaler Leistung erforderlich sind, was ist besser, eine HashMap oder ein Array mit einer binären Suche unter Verwendung eines benutzerdefinierten Komparators?

Ist die Antwort eine Funktion des Objekt- oder Strukturtyps? Hash und/oder Equal Funktionsleistung? Hash-Einzigartigkeit? Listengröße Hashset Größe/Größe einstellen?

Die Größe des Sets, das ich mir anschaue, kann zwischen 500 km und 10 m liegen - falls Informationen nützlich sind.

Während ich nach einer C # -Antwort suche, denke ich, dass die wahre mathematische Antwort nicht in der Sprache liegt, also füge ich diesen Tag nicht hinzu. Wenn jedoch C # -spezifische Dinge zu beachten sind, sind diese Informationen erwünscht.

63
TheSoftwareJedi

Ok, ich werde versuchen, kurz zu sein.

C # kurze Antwort:

Testen Sie die zwei verschiedenen Ansätze.

.NET bietet Ihnen die Werkzeuge, um Ihre Vorgehensweise mit einer Codezeile zu ändern. Andernfalls verwenden Sie System.Collections.Generic.Dictionary. Stellen Sie sicher, dass Sie es mit einer großen Anzahl als Anfangskapazität initialisieren Das Einfügen von Artikeln aufgrund der Aufgabe, die GC durchführt, ist für das Sammeln alter Eimerarrays erforderlich.

Längere Antwort:

Eine Hashtabelle hat fast konstante Nachschlagzeiten, und um ein Objekt in einer Hashtabelle in der realen Welt zu erreichen, muss nicht nur ein Hash berechnet werden.

Um zu einem Objekt zu gelangen, wird Ihre Hashtabelle Folgendes tun:

  • Holen Sie sich den Hash des Schlüssels
  • Rufen Sie die Bucket-Nummer für diesen Hash ab (normalerweise sieht die Map-Funktion wie folgt aus: Bucket = Hash% BucketsCount)
  • Durchqueren Sie die Elementkette (im Grunde handelt es sich um eine Liste von Elementen, die dasselbe Bucket gemeinsam nutzen. Die meisten Hashtables verwenden diese Methode für die Behandlung von Bucket/Hash-Kollisionen.) mit demeines Elements, das Sie versuchen hinzufügen/löschen/aktualisieren/prüfen, ob enthalten ist.

Die Suchzeiten hängen davon ab, wie "gut" (wie spärlich die Ausgabe ist) und wie schnell Ihre Hash-Funktion ist, wie viele Buckets Sie verwenden und wie schnell die Vergleichstasten sind. Dies ist nicht immer die beste Lösung.

Eine bessere und tiefere Erklärung: http://en.wikipedia.org/wiki/Hash_table

20
Maghis

Bei sehr kleinen Sammlungen wird der Unterschied vernachlässigbar sein. Am unteren Ende Ihres Sortiments (500.000 Artikel) werden Sie einen großen Unterschied feststellen, wenn Sie viele Suchvorgänge durchführen. Eine binäre Suche wird O (log n) sein, während eine Hash-Suche O (1), amortized ist. Das ist nicht das Gleiche wie wirklich konstant, aber Sie müssten trotzdem eine ziemlich schreckliche Hash-Funktion haben, um eine schlechtere Leistung als eine binäre Suche zu erhalten.

(Wenn ich "schreckliches Haschisch" sage, meine ich etwas wie:

hashCode()
{
    return 0;
}

Ja, es ist blitzschnell an sich, aber Ihre Hash-Map wird zu einer verknüpften Liste.)

ialiashkevich schrieb etwas C # -Code unter Verwendung eines Arrays und eines Dictionarys, um die beiden Methoden zu vergleichen, es wurden jedoch Long-Werte für Schlüssel verwendet. Ich wollte etwas testen, das tatsächlich eine Hash-Funktion während des Suchvorgangs ausführt. Daher habe ich den Code geändert. Ich habe es geändert, um String-Werte zu verwenden, und die Abschnitte "Populate" und "Lookup" in ihre eigenen Methoden umgestaltet, damit es in einem Profiler einfacher zu sehen ist. Ich verließ auch den Code, der Long-Werte verwendete, nur als Vergleichspunkt. Schließlich habe ich die benutzerdefinierte binäre Suchfunktion losgelassen und die in der Array-Klasse verwendet.

Hier ist der Code:

class Program
{
    private const long capacity = 10_000_000;

    private static void Main(string[] args)
    {
        testLongValues();
        Console.WriteLine();
        testStringValues();

        Console.ReadLine();
    }

    private static void testStringValues()
    {
        Dictionary<String, String> dict = new Dictionary<String, String>();
        String[] arr = new String[capacity];
        Stopwatch stopwatch = new Stopwatch();

        Console.WriteLine("" + capacity + " String values...");

        stopwatch.Start();

        populateStringArray(arr);

        stopwatch.Stop();
        Console.WriteLine("Populate String Array:      " + stopwatch.ElapsedMilliseconds);

        stopwatch.Reset();
        stopwatch.Start();

        populateStringDictionary(dict, arr);

        stopwatch.Stop();
        Console.WriteLine("Populate String Dictionary: " + stopwatch.ElapsedMilliseconds);

        stopwatch.Reset();
        stopwatch.Start();

        Array.Sort(arr);

        stopwatch.Stop();
        Console.WriteLine("Sort String Array:          " + stopwatch.ElapsedMilliseconds);

        stopwatch.Reset();
        stopwatch.Start();

        searchStringDictionary(dict, arr);

        stopwatch.Stop();
        Console.WriteLine("Search String Dictionary:   " + stopwatch.ElapsedMilliseconds);

        stopwatch.Reset();
        stopwatch.Start();

        searchStringArray(arr);

        stopwatch.Stop();
        Console.WriteLine("Search String Array:        " + stopwatch.ElapsedMilliseconds);

    }

    /* Populate an array with random values. */
    private static void populateStringArray(String[] arr)
    {
        for (long i = 0; i < capacity; i++)
        {
            arr[i] = generateRandomString(20) + i; // concatenate i to guarantee uniqueness
        }
    }

    /* Populate a dictionary with values from an array. */
    private static void populateStringDictionary(Dictionary<String, String> dict, String[] arr)
    {
        for (long i = 0; i < capacity; i++)
        {
            dict.Add(arr[i], arr[i]);
        }
    }

    /* Search a Dictionary for each value in an array. */
    private static void searchStringDictionary(Dictionary<String, String> dict, String[] arr)
    {
        for (long i = 0; i < capacity; i++)
        {
            String value = dict[arr[i]];
        }
    }

    /* Do a binary search for each value in an array. */
    private static void searchStringArray(String[] arr)
    {
        for (long i = 0; i < capacity; i++)
        {
            int index = Array.BinarySearch(arr, arr[i]);
        }
    }

    private static void testLongValues()
    {
        Dictionary<long, long> dict = new Dictionary<long, long>(Int16.MaxValue);
        long[] arr = new long[capacity];
        Stopwatch stopwatch = new Stopwatch();

        Console.WriteLine("" + capacity + " Long values...");

        stopwatch.Start();

        populateLongDictionary(dict);

        stopwatch.Stop();
        Console.WriteLine("Populate Long Dictionary: " + stopwatch.ElapsedMilliseconds);

        stopwatch.Reset();
        stopwatch.Start();

        populateLongArray(arr);

        stopwatch.Stop();
        Console.WriteLine("Populate Long Array:      " + stopwatch.ElapsedMilliseconds);

        stopwatch.Reset();
        stopwatch.Start();

        searchLongDictionary(dict);

        stopwatch.Stop();
        Console.WriteLine("Search Long Dictionary:   " + stopwatch.ElapsedMilliseconds);

        stopwatch.Reset();
        stopwatch.Start();

        searchLongArray(arr);

        stopwatch.Stop();
        Console.WriteLine("Search Long Array:        " + stopwatch.ElapsedMilliseconds);
    }

    /* Populate an array with long values. */
    private static void populateLongArray(long[] arr)
    {
        for (long i = 0; i < capacity; i++)
        {
            arr[i] = i;
        }
    }

    /* Populate a dictionary with long key/value pairs. */
    private static void populateLongDictionary(Dictionary<long, long> dict)
    {
        for (long i = 0; i < capacity; i++)
        {
            dict.Add(i, i);
        }
    }

    /* Search a Dictionary for each value in a range. */
    private static void searchLongDictionary(Dictionary<long, long> dict)
    {
        for (long i = 0; i < capacity; i++)
        {
            long value = dict[i];
        }
    }

    /* Do a binary search for each value in an array. */
    private static void searchLongArray(long[] arr)
    {
        for (long i = 0; i < capacity; i++)
        {
            int index = Array.BinarySearch(arr, arr[i]);
        }
    }

    /**
     * Generate a random string of a given length.
     * Implementation from https://stackoverflow.com/a/1344258/1288
     */
    private static String generateRandomString(int length)
    {
        var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        var stringChars = new char[length];
        var random = new Random();

        for (int i = 0; i < stringChars.Length; i++)
        {
            stringChars[i] = chars[random.Next(chars.Length)];
        }

        return new String(stringChars);
    }
}

Hier sind die Ergebnisse mit verschiedenen Kollektionsgrößen. (Zeiten sind in Millisekunden.)

500000 Lange Werte ...
Bevölkerungs-langes Wörterbuch: 26
Long Array befüllen: 2
Langes Wörterbuch suchen: 9
Suche langes Array: 80 

500000 Zeichenkettenwerte ...
String-Array auffüllen: 1237
Populate String Wörterbuch: 46
Sortierfolge-Array: 1755
Suchwörterbuch: 27
Suchstring-Array: 1569 

1000000 Lange Werte ...
Bevölkerungs-langes Wörterbuch: 58
Long Array befüllen: 5
Langes Wörterbuch suchen: 23
Suche langes Array: 136 

1000000 Stringwerte ...
String Array füllen: 2070
String-Wörterbuch mit Auffüllung: 121
Sortierfolge-Array: 3579
Suche String Dictionary: 58
Suchstring-Array: 3267 

3000000 Lange Werte ...
Bevölkerungs-langes Wörterbuch: 207
Langes Array auffüllen: 14
Langes Wörterbuch suchen: 75
Long Array durchsuchen: 435 

3000000 Stringwerte ...
String Array füllen: 5553
Populate String Dictionary: 449
Sortierfolgenarray: 11695
Suche String Dictionary: 194
Suchstring-Array: 10594 

10000000 Lange Werte ...
Bevölkerungs-langes Wörterbuch: 521
Lange Reihe befüllen: 47
Langes Wörterbuch suchen: 202
Langes Array durchsuchen: 1181 

10000000 Zeichenfolgewerte ...
String-Array auffüllen: 18119
Füllen Sie das String-Wörterbuch auf: 1088
Sortierfolgenarray: 28174
Suche String Dictionary: 747
Suchstring-Array: 26503 

Zum Vergleich: Hier ist die Profiler-Ausgabe für den letzten Programmlauf (10 Millionen Datensätze und Suchvorgänge). Ich habe die relevanten Funktionen hervorgehoben. Sie stimmen ziemlich genau mit den oben genannten Stoppuhr-Zeitmesswerten überein.

 Profiler output for 10 million records and lookups

Sie können sehen, dass die Wörterbuchsuche viel schneller ist als die binäre Suche, und wie erwartet ist der Unterschied umso ausgeprägter, je größer die Sammlung ist. Wenn Sie also eine vernünftige Hash-Funktion haben (ziemlich schnell mit wenigen Kollisionen), sollte eine Hash-Suche die binäre Suche nach Sammlungen in diesem Bereich übertreffen.

48
Bill the Lizard

Die Antworten von Bobby, Bill und Corbin sind falsch. O(1) ist nicht langsamer als O (log n) für ein festes/begrenztes n:

log (n) ist konstant und hängt von der konstanten Zeit ab.

Und für eine langsame Hash-Funktion, die je von MD5 gehört wurde? 

Der standardmäßige String-Hash-Algorithmus berührt wahrscheinlich alle Zeichen und kann leicht 100-mal langsamer sein als der Durchschnittsvergleich für lange String-Schlüssel. Kenne ich schon. 

Möglicherweise können Sie eine Basisstation (teilweise) verwenden. Wenn Sie sich in 256 Blöcke von ungefähr gleicher Größe aufteilen können, betrachten Sie die binäre Suche von 2k bis 40k. Das wird wahrscheinlich eine viel bessere Leistung bringen.

[Bearbeiten] Zu viele Leute stimmen ab, was sie nicht verstehen.

Stringvergleiche für binäre Suche sortierte Sets haben eine sehr interessante Eigenschaft: Sie werden langsamer, je näher sie dem Ziel kommen. Zuerst brechen sie beim ersten Zeichen, am Ende nur beim letzten Zeichen. Eine konstante Zeit für sie anzunehmen, ist falsch. 

35

Die einzig vernünftige Antwort auf diese Frage ist: Es kommt darauf an. Dies hängt von der Größe Ihrer Daten, der Form Ihrer Daten, Ihrer Hash-Implementierung, Ihrer binären Suchimplementierung und dem Ort Ihrer Daten ab (auch wenn dies in der Frage nicht erwähnt wird). Ein paar andere Antworten sagen so viel aus, dass ich das einfach löschen könnte. Es könnte jedoch nett sein, das, was ich aus dem Feedback gelernt habe, meiner ursprünglichen Antwort mitzuteilen.

  1. Ich schrieb: "Hash-Algorithmen sind O(1), während die binäre Suche O (log n) ist." - Wie in den Kommentaren erwähnt, schätzt die Big O-Notation die Komplexität und nicht die Geschwindigkeit. Das ist absolut wahr. Es ist erwähnenswert, dass wir normalerweise Komplexität verwenden, um ein Gefühl für den Zeit- und Platzbedarf eines Algorithmus zu erhalten. Während es dumm ist anzunehmen, dass Komplexität genau das Gleiche ist wie Geschwindigkeit, ist es ungewöhnlich, die Komplexität ohne Zeit oder Raum im Hinterkopf zu schätzen. Meine Empfehlung: Vermeiden Sie die Big O-Notation.
  2. Ich schrieb: "So wie n sich unendlich nähert" ... "- Hier geht es um das Dümmste, was ich in eine Antwort hätte aufnehmen können. Infinity hat nichts mit Ihrem Problem zu tun. Sie erwähnen eine Obergrenze von 10 Millionen. Ignoriere die Unendlichkeit. Wie die Kommentatoren darauf hinweisen, werden sehr große Zahlen alle möglichen Probleme mit einem Hash erzeugen. (Sehr große Zahlen machen die binäre Suche auch nicht zu einem Spaziergang im Park.) Meine Empfehlung: Erwähnen Sie Unendlichkeit nicht, es sei denn, Sie meinen Unendlichkeit.
  3. Auch aus den Kommentaren: Achtung, Standard-String-Hashes (Haben Sie Zeichenketten? Sie erwähnen das nicht.), Datenbankindizes sind oft B-Trees (Denkanstöße). Meine Empfehlung: Bedenken Sie alle Möglichkeiten. Betrachten Sie andere Datenstrukturen und Ansätze ... wie ein altmodischer trie (zum Speichern und Abrufen von Strings) oder einen R-Baum (für räumliche Daten) oder einen MA-FSA Minimaler azyklischer Finite-State-Automat (geringer Speicherbedarf).

Bei den Kommentaren können Sie davon ausgehen, dass Personen, die Hash-Tabellen verwenden, gestört sind. Sind Hashtische rücksichtslos und gefährlich? Sind diese Leute verrückt?

Es stellt sich heraus, dass sie nicht sind. So wie binäre Bäume bei bestimmten Dingen gut sind (Datenreihenfolge in der Reihenfolge, Speichereffizienz), haben auch Hashtabellen den richtigen Moment. Sie können insbesondere die Anzahl der Lesevorgänge reduzieren, die zum Abrufen Ihrer Daten erforderlich sind. Ein Hash-Algorithmus kann einen Ort generieren und direkt in den Speicher oder auf die Festplatte springen, während die binäre Suche bei jedem Vergleich Daten liest, um zu entscheiden, was als Nächstes gelesen werden soll. Bei jedem Lesevorgang besteht die Möglichkeit eines Cache-Fehlschlags, der eine Größenordnung (oder mehr) langsamer ist als eine CPU-Anweisung.

Das bedeutet nicht, dass Hashtabellen besser sind als die binäre Suche. Sie sind nicht. Es soll auch nicht darauf hingewiesen werden, dass alle Hash- und binären Suchimplementierungen gleich sind. Sie sind nicht. Wenn ich einen Punkt habe, dann ist es folgender: Beide Ansätze existieren aus einem bestimmten Grund. Es liegt an Ihnen zu entscheiden, welches für Ihre Bedürfnisse am besten ist.

Ursprüngliche Antwort:


Hash-Algorithmen sind O(1), während die binäre Suche O (log n) ist. So wie n nähert sich unendlich, verbessert sich die Hash-Leistung relativ zu binär Suche. Ihre Laufleistung variiert abhängig von n, Ihrem Hash Implementierung und Implementierung Ihrer binären Suche.

Interessante Diskussion zu O(1) . Paraphrased:

O (1) bedeutet nicht sofort. Es bedeutet, dass die Leistung nicht ändern Sie sich, wenn die Größe von n wächst. Sie können einen Hash-Algorithmus entwerfen das ist so langsam, dass niemand es jemals benutzen würde und es wäre immer noch O (1) . Ich bin ziemlich sicher, dass .NET/C # nicht unter kostenintensivem Hashing leidet, jedoch ;)

17
Corbin March

Wenn Ihr Satz von Objekten wirklich statisch und unveränderlich ist, können Sie einen perfect hash verwenden, um die Leistung von O(1) zu gewährleisten. Ich habe gperf schon ein paar Mal gesehen, obwohl ich nie Gelegenheit hatte, es selbst zu benutzen.

7
Mark Ransom

Überrascht hat niemand das Kuckuck-Hashing erwähnt, das garantiertes O(1) bietet und im Gegensatz zu perfektem Hashing den gesamten Speicher nutzen kann, den es zuweist, wobei perfektes Hashing mit garantiertem O(1) aber den größten Teil seiner Zuweisung verschwenden. Der Vorbehalt? Die Einfügungszeit kann sehr langsam sein, insbesondere wenn die Anzahl der Elemente zunimmt, da die gesamte Optimierung während der Einfügungsphase durchgeführt wird.

Ich glaube, eine Version davon wird in Router-Hardware für IP-Lookups verwendet.

Siehe Linktext

6
ApplePieIsGood

Hashes sind normalerweise schneller, obwohl binäre Suchen bessere Worst-Case-Eigenschaften aufweisen. Ein Hash-Zugriff ist normalerweise eine Berechnung, um einen Hash-Wert zu erhalten, um zu bestimmen, in welchem ​​"Bucket" sich ein Datensatz befindet. Daher hängt die Leistung im Allgemeinen davon ab, wie gleichmäßig die Datensätze verteilt werden, und von der Methode, mit der der Bucket durchsucht wird. Eine schlechte Hash-Funktion (die ein paar Buckets mit vielen Datensätzen belässt) mit einer linearen Suche durch die Buckets führt zu einer langsamen Suche. (Auf der dritten Seite, wenn Sie eine Platte und nicht Speicher lesen, sind die Hash-Buckets wahrscheinlich zusammenhängend, während der binäre Baum nicht-lokalen Zugriff garantiert.)

Wenn Sie generell schnell sein wollen, verwenden Sie den Hash. Wenn Sie wirklich eine eingeschränkte Leistung wünschen, können Sie den binären Baum verwenden.

6
David Thornley

Dictionary/Hashtable verbraucht mehr Speicher und benötigt mehr Zeit zum Auffüllen im Vergleich zu array .. Die Suche wird jedoch schneller durch das Dictionary als durch die binäre Suche innerhalb des Arrays durchgeführt.

Hier sind die Zahlen für 10 Million von Int64 Elementen, die gesucht und ausgefüllt werden sollen . Außerdem einen Beispielcode, den Sie selbst ausführen können.

Wörterbuchspeicher: 462,836

Array-Speicher: 88.376

Bevölkerungswörterbuch: 402

Array füllen: 23

Suchwörterbuch: 176

Such-Array: 680

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace BinaryVsDictionary
{
    internal class Program
    {
        private const long Capacity = 10000000;

        private static readonly Dictionary<long, long> Dict = new Dictionary<long, long>(Int16.MaxValue);
        private static readonly long[] Arr = new long[Capacity];

        private static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            for (long i = 0; i < Capacity; i++)
            {
                Dict.Add(i, i);
            }

            stopwatch.Stop();

            Console.WriteLine("Populate Dictionary: " + stopwatch.ElapsedMilliseconds);

            stopwatch.Reset();

            stopwatch.Start();

            for (long i = 0; i < Capacity; i++)
            {
                Arr[i] = i;
            }

            stopwatch.Stop();

            Console.WriteLine("Populate Array:      " + stopwatch.ElapsedMilliseconds);

            stopwatch.Reset();

            stopwatch.Start();

            for (long i = 0; i < Capacity; i++)
            {
                long value = Dict[i];
//                Console.WriteLine(value + " : " + RandomNumbers[i]);
            }

            stopwatch.Stop();

            Console.WriteLine("Search Dictionary:   " + stopwatch.ElapsedMilliseconds);

            stopwatch.Reset();

            stopwatch.Start();

            for (long i = 0; i < Capacity; i++)
            {
                long value = BinarySearch(Arr, 0, Capacity, i);
//                Console.WriteLine(value + " : " + RandomNumbers[i]);
            }

            stopwatch.Stop();

            Console.WriteLine("Search Array:        " + stopwatch.ElapsedMilliseconds);

            Console.ReadLine();
        }

        private static long BinarySearch(long[] arr, long low, long hi, long value)
        {
            while (low <= hi)
            {
                long median = low + ((hi - low) >> 1);

                if (arr[median] == value)
                {
                    return median;
                }

                if (arr[median] < value)
                {
                    low = median + 1;
                }
                else
                {
                    hi = median - 1;
                }
            }

            return ~low;
        }
    }
}
4
ialiashkevich

Ich vermute stark, dass das Hashing bei einem Problem der Größe ~ 1M schneller ist.

Nur für die Zahlen:

eine binäre Suche würde ~ 20 Vergleiche erfordern (2 ^ 20 == 1M)

ein Hash-Lookup erfordert 1 Hash-Berechnung für den Suchschlüssel und möglicherweise eine Handvoll Vergleiche, um mögliche Kollisionen aufzulösen

Bearbeiten Sie die Zahlen:

    for (int i = 0; i < 1000 * 1000; i++) {
        c.GetHashCode();
    }
    for (int i = 0; i < 1000 * 1000; i++) {
        for (int j = 0; j < 20; j++)
            c.CompareTo(d);
    }

times: c = "abcde", d = "rwerij" Hashcode: 0,0012 Sekunden. Vergleiche: 2,4 Sekunden. 

haftungsausschluss: Ein Benchmarking einer Hash-Suche im Vergleich zu einer Binär-Suche ist möglicherweise besser als dieser nicht ganz relevante Test. Ich bin mir nicht mal sicher, ob GetHashCode unter der Haube gespeichert wird

3
Jimmy

Ich würde sagen, es hängt hauptsächlich von der Leistung der Hash- und Vergleichsmethoden ab. Wenn Sie beispielsweise sehr lange, aber zufällige Zeichenfolgenschlüssel verwenden, führt ein Vergleich immer zu einem sehr schnellen Ergebnis. Eine standardmäßige Hash-Funktion verarbeitet jedoch die gesamte Zeichenfolge.

In den meisten Fällen sollte die Hash-Map jedoch schneller sein.

2

Ich frage mich, warum niemand perfektes Hashing erwähnt hat.

Dies ist nur relevant, wenn Ihr Dataset lange Zeit fixiert ist, aber es analysiert die Daten und erstellt eine perfekte Hash-Funktion, die keine Kollisionen gewährleistet.

Ziemlich ordentlich, wenn Ihre Daten konstant sind und die Berechnungszeit für die Funktion im Vergleich zur Anwendungslaufzeit gering ist.

2
orip

Hier wird beschrieben, wie Hashes erstellt werden und weil das Schlüsseluniversum relativ groß ist und Hashfunktionen so aufgebaut sind, dass sie "sehr injektiv" sind, so dass selten Kollisionen auftreten. Die Zugriffszeit für eine Hashtabelle ist nicht O(1) eigentlich ... es ist etwas, das auf einigen Wahrscheinlichkeiten basiert ..__, aber es ist vernünftig zu sagen, dass die Zugriffszeit eines Hash fast immer kleiner ist als die Zeit O (log_2 (n)) 

1
xxxxxxx

Es hängt davon ab, wie Sie mit Duplikaten für Hashtabellen umgehen (wenn überhaupt). Wenn Sie Hashschlüssel-Duplikate zulassen möchten (keine Hash-Funktion ist perfekt), bleibt O(1) für die Suche nach Primärschlüsseln, aber die Suche nach dem Wert "right" kann teuer sein. Antwort ist dann, theoretisch meistens, Hashes sind schneller. YMMV abhängig davon, welche Daten Sie dort ablegen ...

1
Keltia

Diese Frage ist komplizierter als der Umfang der reinen Algorithmusleistung. Wenn wir die Faktoren entfernen, die den binären Suchalgorithmus für den Cache freundlicher machen, ist die Hash-Suche im Allgemeinen schneller. Der beste Weg, dies herauszufinden, besteht darin, ein Programm zu erstellen und die Optionen zur Compileroptimierung zu deaktivieren. Wir konnten feststellen, dass die Hash-Suche schneller ist, da der Algorithmus im Allgemeinen eine Zeiteffizienz von O(1) aufweist.

Wenn Sie jedoch die Compileroptimierung aktivieren und denselben Test mit weniger als 10.000 Stichproben durchführen, hat die Binärsuche die Hash-Suche übertroffen, indem sie die Vorteile ihrer cachefreundlichen Datenstruktur ausgenutzt hat.

0
Richard Lin

Natürlich ist Hash für so einen großen Datensatz am schnellsten.

Eine Möglichkeit, die Geschwindigkeit noch weiter zu beschleunigen, ist, da sich die Daten selten ändern, programmgesteuert Ad-hoc-Code für die erste Suchebene als riesige switch-Anweisung (wenn Ihr Compiler dies verarbeiten kann) und anschließend zur Suche verzweigt der resultierende Eimer.

0
Mike Dunlavey

Die Antwort hängt davon ab. Nehmen wir an, dass die Anzahl der Elemente 'n' sehr groß ist. Wenn Sie eine bessere Hash-Funktion schreiben können, bei der weniger Kollisionen auftreten, ist Hash das Beste Beachten Sie, dass Die Hash-Funktion wird nur einmal bei der Suche ausgeführt und leitet zum entsprechenden Bucket. Es ist also kein großer Aufwand, wenn n hoch ist.
Problem in Hashtable: Aber das Problem in Hash-Tabellen ist, wenn die Hash-Funktion nicht gut ist (mehr Kollisionen passieren), dann ist die Suche nicht O (1). Es neigt zu O(n), da die Suche in einem Bucket eine lineare Suche ist. Kann am schlimmsten sein als ein binärer Baum . Problem im Binärbaum: Wenn der Baum im Binärbaum nicht ausgeglichen ist, neigt er auch zu O (n). Wenn Sie zum Beispiel 1,2,3,4,5 in einen Binärbaum eingefügt haben, wäre dies wahrscheinlich eine Liste . Also, Wenn Sie eine gute Hashmethode sehen können, verwenden Sie eine Hashtabelle Wenn nicht, verwenden Sie besser einen binären Baum.

0
Lahiru

Dies ist mehr ein Kommentar zu Bills Antwort, weil seine Antwort so viele Upvotes hat, obwohl es falsch ist. Also musste ich das posten. 

Ich sehe eine Menge Diskussionen darüber, was im schlimmsten Fall die Komplexität einer Suche in der Hashtabelle ist und was als amortisierte Analyse angesehen wird/was nicht .. Bitte überprüfen Sie den Link unten

Hash-Komplexität der Hashtabelle (Einfügen, Suchen und Löschen)

im schlimmsten Fall ist die Komplexität O(n) und nicht O(1) im Gegensatz zu dem, was Bill sagt. Und so wird seine Komplexität O(1) nicht abgeschrieben, da diese Analyse nur für den schlimmsten Fall verwendet werden kann (auch sein eigener Wikipedia-Link sagt das).

https://en.wikipedia.org/wiki/Hash_table

https://en.wikipedia.org/wiki/Amortized_analysis

0
homeOfTheWizard