Präfix Suche gegen eine halbe Milliarde Strings

8

Ich habe eine Liste von 500 Mil Saiten. Die Zeichenfolgen sind alphanumerische ASCII-Zeichen unterschiedlicher Größe (normalerweise 2-30 Zeichen). Sie sind auch einzelne Wörter (oder eine Kombination von Wörtern ohne Leerzeichen wie 'helloiamastring').

Was ich brauche, ist ein schneller Weg, um gegen ein Ziel zu prüfen, sagen wir "Hallo". Das Ergebnis sollten alle Strings aus der 500mil-Liste sein, die mit 'hi' beginnen (zB 'hithere', 'hihowareyou' usw.). Dies muss schnell sein, da es jedes Mal eine neue Abfrage gibt, wenn der Benutzer etwas eingibt. Wenn er also "hi" eingibt, werden alle Strings, die mit "hi" beginnen, aus der 500-Mil-Liste angezeigt, wenn er "hey" tippt. Alle Strings, die mit "hey" beginnen, werden usw. angezeigt.

Ich habe es mit dem Tries algo versucht, aber der Speicherbedarf für die Speicherung von 300-mil-Strings ist einfach riesig. Es sollte mich 100GB + RAM dafür verlangen. Und ich bin mir ziemlich sicher, dass die Liste auf eine Milliarde anwachsen wird.

Was ist ein schneller Algorithmus für diesen Anwendungsfall?

P.S. Falls es keine schnelle Option gibt, wäre es die beste Alternative, Personen auf mindestens 4 Zeichen zu beschränken, bevor Ergebnisse angezeigt werden. Gibt es eine schnelle Möglichkeit, die Ergebnisse dann abzurufen?

    
anemaria20 15.01.2017, 17:43
quelle

6 Antworten

3

Sie möchten einen gerichteten azyklischen Wortgraphen oder DAWG. Dies verallgemeinert @ Greybeards Vorschlag, stemming zu verwenden.

Siehe zum Beispiel die Diskussion in Abschnitt 3.2 von das .

    
Jim D. 25.01.2017, 05:20
quelle
2

Wenn die Strings sortiert sind, ist eine binäre Suche sinnvoll. Als Beschleunigung könnten Sie ein Dictionary aller möglichen Bigramme ("aa", "ab" usw.) führen, wobei die entsprechenden Werte der erste und letzte Index sind, der mit diesem Bigramm beginnt (falls vorhanden) und so in O(1) Zeit Null in einer viel kleineren Unterliste, die die Zeichenfolgen enthält, die Sie suchen. Sobald Sie eine Übereinstimmung gefunden haben, führen Sie eine lineare Suche nach rechts und links durch, um alle anderen Übereinstimmungen zu erhalten.

    
John Coleman 15.01.2017 17:52
quelle
0

Wenn Sie den Benutzer zum Beispiel zwingen möchten, mindestens 4 Buchstaben zu setzen, können Sie eine Schlüssel-Wert-Karte, einen Speicher oder eine Festplatte speichern, wobei die Schlüssel alle Kombinationen aus 4 Buchstaben sind (wenn sie nicht zu groß sind) Groß- / Kleinschreibung ist nicht zu beachten, sonst können Sie auf drei beschränken), und die Werte sind eine Liste der Positionen aller Strings, die mit der Kombination beginnen.

Nachdem der Benutzer die drei (oder vier) Buchstaben eingegeben hat, haben Sie alle möglichen Zeichenfolgen gleichzeitig. Von diesem Punkt an schlingen Sie nur diese Teilmenge.

Im Durchschnitt ist diese Teilmenge klein genug, d. h. 500M geteilt durch 26 ^ 4 ... nur als Beispiel. Eigentlich größer, weil wahrscheinlich nicht alle Sätze von 4 Buchstaben als Präfix für Ihre Strings verwendet werden können.

Vergessen Sie zu sagen: Wenn Sie der großen Liste einen neuen String hinzufügen, aktualisieren Sie auch die Liste der Indizes, die dem Schlüssel in der Map entsprechen.

    
emilio 15.01.2017 21:22
quelle
0

Wenn Sie keine Datenbank verwenden möchten, sollten Sie einige datenbezogene Routinen erstellen, die in allen Datenbank-Engines bereits vorhanden sind:

  1. Versucht nicht, alle Daten im Speicher zu laden.
  2. Verwenden Sie für alle Strings eine feste Länge. Es erhöht den Speicherverbrauch, verringert aber die Suchzeit signifikant (die i-te Zeichenfolge kann an der Position L * i Bytes in der Datei gefunden werden, wobei L - feste Länge ist). Erstellen Sie einen zusätzlichen Mechanismus, um mit extrem langen Strings zu arbeiten: Speichern Sie ihn an einem anderen Ort und verwenden Sie spezielle Zeiger.
  3. Sortiere alle Strings. Sie können merge sort verwenden, um dies zu tun, ohne alle Zeichenfolgen im Speicher gleichzeitig zu laden.
  4. Erstellen Sie Indizes (die Adresse der ersten Zeile beginnt mit 'a', 'b', ...), Indizes können auch für 2-Gramm, 3-Gramm usw. erstellt werden. Indizes können gespeichert werden, um die Suchgeschwindigkeit zu erhöhen .
  5. Verwenden Sie erweiterte Strategien, um eine vollständige Indexregenerierung bei der Datenaktualisierung zu vermeiden: Zerlegen Sie Daten in eine Anzahl von Dateien nach Anfangsbuchstaben und aktualisieren Sie nur betroffene Indizes, erstellen Sie leere Leerzeichen in Daten, um die Auswirkungen von Lese-, Modifizierungs- und Schreibprozeduren zu verringern ein Cache für neue Zeilen, bevor sie zum Hauptspeicher hinzugefügt werden und in diesem Cache suchen.
  6. Verwenden Sie den Abfrage-Cache, um eine schnelle Bearbeitung von Anfragen zu ermöglichen.
Stanislav Ivanov 22.01.2017 12:34
quelle
0

In diesem hypothetischen Fall, in dem die indizierten Strings nicht mit anderen Informationen verknüpft sind (z. B. andere Spalten in derselben Zeile), gibt es relativ wenig Unterschied zwischen einem vollständigen Index und der Sortierung der Strings (wie in ein kleiner Unterschied, aber nicht so viel, wie du dir erhoffst. Angesichts des wachsenden Charakters der Liste und der Kosten für deren Aktualisierung könnte der gegenteilige Ansatz die von Ihnen gewünschten Performance-Kompromisse besser erfüllen.

Für ein beliebiges Zeichen an einer beliebigen Stelle in der Zeichenfolge ist Ihr Basisfall, dass keine Zeichenfolge existiert, die diesen Buchstaben enthält. Zum Beispiel, sobald 'Hallo' eingegeben wurde, wenn der nächste Buchstabe 't' ist, dann ist Ihr Grundfall, dass es keinen String gibt, der 'hellot' beginnt. Es gibt eine endliche Anzahl von Zeichen, die "Hallo" an Position 5 (sagen wir 26) folgen könnten. Sie benötigen 26 Leerzeichen mit fester Länge, in denen Informationen über Zeichen gespeichert werden, die an Position 5 "Hallo" folgen. Jedes Leerzeichen sagt Null, wenn keine Zeichenfolge vorhanden ist, z. B. "t" folgt "Hallo" oder enthält eine Zahl von Datenspeicheradressen, durch die zu suchen ist, um die Liste von Zeichen zu finden, für die eine oder mehrere Zeichenfolgen das Zeichen nach "hellot" an Stelle 6 verwenden (oder absolute Datenspeicheradressen verwenden, obwohl nur relative Adressen den vorgeschlagenen Algorithmus erlauben) unterstützt eine unendliche Anzahl von Strings unendlicher Länge ohne irgendeine Modifikation, um größere Zeiger zu ermöglichen, wenn die Liste wächst.)

Der Algorithmus kann sich dann durch diese auf der Festplatte gespeicherten Daten vorwärts bewegen, indem er einen Baum von Stringanfängen im Speicher erstellt und Verzögerungen vermeidet, die durch Lesezugriffe mit wahlfreiem Zugriff verursacht werden. Für einen speicherinternen Index speichern Sie einfach den Teil des Baums, der dem Stamm am nächsten ist, im Speicher. Nachdem der Benutzer "Hallo" getippt hat und der Algorithmus die Informationen über eine oder mehrere Zeichenfolgen verfolgt hat, die an der Datenspeicheradresse X beginnen, findet der Algorithmus eine von zwei Arten von Listen an einer Position. Entweder ist es eine andere Sequenz B. 26 Räume mit fester Länge mit Informationen über Charaktere, die nach "Hellot" an Position 6 folgen, oder es ist ein vorab zugeteilter Raumblock, der alle Post-Fixes auflistet, die "hellot" folgen, abhängig davon, wie viele solcher Post-Fixes existieren. Sobald es genügend Post-Fixes gibt, dass die Verwendung eines herkömmlichen Such- und / oder Sortieralgorithmus zum Aktualisieren und Durchsuchen der Post-Fix-Liste nicht die gewünschten Leistungsvorteile bietet, wird es aufgeteilt und durch eine Sequenz ersetzt, z. 26 Räume fester Länge.

Dies beinhaltet die Vorabzuweisung einer relativ großen Menge an Festplattenspeicher im Voraus, mit der Abwägung, dass Ihr Baum in sortierter Form verwaltet werden kann, ohne dass für die meisten Updates etwas herumgereicht werden muss, und Ihre Suchen können durchgeführt werden Voll in einem single sequential read . Es bietet außerdem mehr Flexibilität und benötigt wahrscheinlich weniger Speicherplatz als eine Lösung, die darauf basiert, die Zeichenfolgen selbst als Zeichenfolgen fester Länge zu speichern.

    
RichardB 23.01.2017 06:11
quelle
0

Zunächst sollte ich sagen, dass das Tag, das Sie für Ihre Frage hinzufügen sollten, "Information Retrieval" ist.

Ich denke mit Apache Lucene ist PrefixQuery ist der beste Weg, um Platzhalteranfragen zu bearbeiten. Apache hat eine Python-Version , wenn Sie mit Python vertraut sind. Aber um Apache lucent zu benutzen, um Ihr Problem zu lösen, sollten Sie zuerst über Indizierung Ihrer Daten Bescheid wissen (das ist der Teil, den Sie verwenden) Daten werden komprimiert und auf effizientere Weise gespeichert).

Wenn Sie auch im Abschnitt IR-Buch auf den Index- und Wildcard-Abfrageabschnitt schauen, erhalten Sie eine bessere Sicht.

    
Alikbar 25.01.2017 05:34
quelle