Frage

Ich bin in der Notwendigkeit einer leistungsorientierten Hash-Funktion Implementierung in C ++ für eine Hash-Tabelle, die ich sein Codierung wird. Ich sah schon um und fand nur Fragen, was „in der Regel“ eine gute Hash-Funktion ist. Ich habe als CRC32 (aber wo gute Umsetzung zu finden?) Und ein paar Kryptographie-Algorithmen. Mein Tisch, aber hat sehr spezifische Anforderungen.

Hier ist, was die Tabelle aussehen wird:

100,000 items max
200,000 capacity (so the load is 0.5)
hashing a 6-character string which is a part of English sentence
     examples: "become"    "and he"    ", not "

Die Priorität Nummer eins meiner Hash-Tabelle ist eine schnelle Suche (Retrieval). Kurze Einführung ist nicht wichtig, aber es wird mit Schnellsuche kommen. Das Löschen ist nicht wichtig, und Re-Hashing ist nicht etwas, ich werde in suchen. So behandelt Kollisionen, werde ich sein, wahrscheinlich mit separaten Verkettungs , wie hier . Ich habe bereits an diesem Artikel sah, würde aber eine Meinung wie jene, die so behandelt haben Aufgabe vor.

War es hilfreich?

Lösung

Jetzt assumming wollen Sie einen Hash, und wollen etwas extrem schnellen , die in Ihrem Fall funktionieren würden, weil die Saiten sind nur 6 Zeichen sehnen Sie diese Magie verwenden:

size_t precision = 2; //change the precision with this
size_t hash(const char* str)
{
   return (*(size_t*)str)>> precision;
}

CRC ist für slowpokes;)

Erklärung: Dies funktioniert, indem Sie den Inhalt der Zeichenfolge Zeiger Gießen auf „aussehen wie ein“ size_t (int32 oder int64 auf die optimale Ergänzung für Ihre Hardware-basiert). So wird der Inhalt der Zeichenfolge als Ausgangsnummer interpretiert, keine Sorgen über Zeichen mehr, und Sie dann Bit-Verschiebung dies die Präzision erforderlich (Sie diese Nummer an die beste Leistung zwicken, ich habe 2 funktioniert gut gefunden für Streicher in Hashing Satz von ein paar tausend).

Auch die wirklich nette Teil ist jeder anständige Compiler auf moderner Hardware eine Zeichenfolge wie diese in 1 Montageanleitung Hash wird, schwer, das zu schlagen;)

Andere Tipps

Dieses einfache Polynom funktioniert überraschend gut. Ich habe es von Paul Larson von Microsoft Research, die eine Vielzahl von Hash-Funktionen und Hash-Multiplikatoren untersucht.

unsigned hash(const char* s, unsigned salt)
{
    unsigned h = salt;
    while (*s)
        h = h * 101 + (unsigned) *s++;
    return h;
}

salt sollte auf einige initialisiert werden zufällig gewählter Wert vor der Hash-Tabelle gegen verteidigen erstellt wird, Hash-Tabelle Angriffe . Wenn dies für Sie kein Problem, nur 0 verwenden.

Die Größe der Tabelle ist auch wichtig, um Kollisionen zu minimieren. Klingt wie das Ihre ist in Ordnung.

Boost.Functional / Hash sein könnte verwenden, um Sie. Ich habe es nicht versucht, so kann ich nicht für seine Leistung nicht garantieren.

-Boost hat auch eine CRC Bibliothek .

Ich würde schauen ein Boost.Unordered zuerst (dh boost :: unordered_map <>). Es verwendet Hash-Karten anstelle von binären Bäumen für Container.

Ich glaube, einige STL-Implementierungen haben eine hash_map <> Container im stdext Namespace.

Die Größe der Tabelle wird diktieren, welche Größe Hash Sie verwenden sollten. Sie möchten Kollisionen natürlich minimieren. Ich bin mir nicht sicher, was Sie von max Elementen und Kapazität angeben (sie scheinen wie die gleiche Sache für mich) in jedem Fall eine dieser beiden Zahlen deuten darauf hin, dass ein 32-Bit-Hash ausreichen würden. Sie könnten mit CRC16 (~ 65.000 Möglichkeiten) weg, aber Sie würden wahrscheinlich viele Kollisionen zu tun haben. Auf der anderen Seite kann eine Kollision schneller sein als mit als einem CRC32 Hash zu behandeln.

Ich würde sagen, gehen mit CRC32. Sie werden keinen Mangel an Dokumentation und Beispielcode. Da Sie Ihre Maxima haben herausgefunden und die Geschwindigkeit ist eine Priorität, geht mit einer Reihe von Zeigern. Verwenden des Hash einen Index zu erzeugen. Zusammenstoß, bis Sie Schritt Index schlug einen leeren Eimer .. schnell und einfach.

Da Sie Englisch Wörter speichern, werden die meisten Ihrer Zeichen Buchstaben sein und es wird nicht viel Variation in den zwei höchstwertigen Bits der Daten sein. Abgesehen davon aus würde ich es hält sehr einfach, nur XOR. Schließlich sind Sie auf der Suche nicht für kryptographische Stärke, sondern nur für eine einigermaßen gleichmäßige Verteilung. Etwas in diese Richtung:

size_t hash(const std::string &data) {
  size_t h(0);
  for (int i=0; i<data.length(); i++)
    h = (h << 6) ^ (h >> 26) ^ data[i];
  }
  return h;
}

Neben dem, dass, haben Sie bei std :: tr1 :: angesehen Hash als Hash-Funktion und / oder std :: tr1 :: unordered_map als Implementierung einer Hash-Tabelle? werden diese unter Verwendung wäre wahrscheinlich viel Arbeit sparen im Gegensatz zu Ihrer eigenen Klassen implementieren.

  

Die oberste Priorität meiner Hash-Tabelle ist eine schnelle Suche (Retrieval).

Na, dann verwenden Sie die richtige Datenstruktur, wie sie in einer Hash-Tabelle der Suche ist O (1)! :)

Die CRC32 sollte gut tun. Die Implementierung ist nicht so komplex, es ist vor allem auf XORs. So stellen Sie sicher es ein gutes Polynom verwendet wird.

Wie wäre es etwas einfacher:

// Initialize hash lookup so that it maps the characters
// in your string to integers between 0 and 31
int hashLookup[256];

// Hash function for six character strings.
int hash(const char *str)
{
    int ret = 0, mult = 1;
    for (const char *p = str; *p; *p++, mult *= 32) {
        assert(*p >= 0 && *p < 256);
        ret += mult * hashLookup[*p];
    }

    return ret;
}

Dies setzt voraus, 32 Bit ints. Es verwendet 5 Bits pro Zeichen, so dass der Hash-Wert hat nur 30 Bits in ihm. Man könnte dies beheben, vielleicht durch sechs Bits für die ersten ein oder zwei Zeichen zu erzeugen. Wenn Sie Zeichensatz klein genug ist, können Sie nicht mehr als 30 Bits benötigen.

Wenn Sie kurze Strings und Einfügen suchen müssen, ist kein Problem, vielleicht könnten Sie einen B-Baum verwenden oder ein 2-3-Baum, Sie nicht viel gewinnen, indem sie in Ihrem Fall Hashing.

Die Art und Weise Sie dies tun würde, ist durch einen Brief in jedem Knoten platzieren, so dass Sie erste Prüfung für den Knoten „a“, dann überprüfen Sie „a“ 's Kinder für ‚p‘, und es ist Kinder für ‚p‘, und dann „l“ und dann „e“. In Situationen, in denen Sie „Apfel“ und „Übernehmen“ Sie müssen versuchen, die letzten Knoten haben (da der einzige Unterschied in der letzten „e“ und „y“)

Aber, aber in den meisten Fällen werden Sie in der Lage sein, das Wort nach einer nur wenige Schritte zu bekommen ( „Xylophon“ => „x“ -> „ylophone“), so können Sie wie folgt zu optimieren. Dies kann schneller sein als Hashing

Da C ++ 11, C ++ ist vorgesehen ein std::hash< string >( string ) . Das ist wahrscheinlich eine effiziente Hashing-Funktion sein, dass ein gute Verteilung des Hash-Codes für die meisten Saiten.

Wenn Sie darüber hinaus eine Hash-Tabelle der Umsetzung denken, sollten Sie jetzt in Erwägung ziehen mit einem C ++ std::unordered_map statt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top