Eingabe: "tableapplechairtablecupboard..."
viele Wörter
Was wäre ein effizienter Algorithmus, um solchen Text in die Liste der Wörter aufzuteilen und zu erhalten:
Ausgabe: ["table", "apple", "chair", "table", ["cupboard", ["cup", "board"]], ...]
Das erste, was mir in den Sinn kommt, ist, alle möglichen Wörter (beginnend mit dem ersten Buchstaben) durchzugehen und das längste mögliche Wort zu finden position=word_position+len(word)
PS
Wir haben eine Liste aller möglichen Wörter.
Das Wort "Schrank" kann "Tasse" und "Brett" sein, wählen Sie am längsten.
Sprache: Python, aber Hauptsache ist der Algorithmus selbst.
['able', 'air', 'apple', 'boa', 'boar', 'board', 'chair', 'cup', 'cupboard', 'ha', 'hair', 'lea', 'leap', 'oar', 'tab', 'table', 'up']
Antworten:
Ein naiver Algorithmus liefert keine guten Ergebnisse, wenn er auf reale Daten angewendet wird. Hier ist ein 20-Zeilen-Algorithmus, der die relative Worthäufigkeit ausnutzt, um genaue Ergebnisse für Echtworttext zu erhalten.
(Wenn Sie eine Antwort auf Ihre ursprüngliche Frage wünschen, bei der die Worthäufigkeit nicht verwendet wird, müssen Sie verfeinern, was genau unter "längstes Wort" zu verstehen ist: Ist es besser, ein Wort mit 20 Buchstaben und zehn Wörter mit drei Buchstaben zu haben, oder ist dies der Fall? Es ist besser, fünf Wörter mit 10 Buchstaben zu haben. Sobald Sie sich für eine genaue Definition entschieden haben, müssen Sie nur noch die Liniendefinition ändern
wordcost
, um die beabsichtigte Bedeutung wiederzugeben.)Die Idee
Der beste Weg, um fortzufahren, besteht darin , die Verteilung der Ausgabe zu modellieren . Eine gute erste Annäherung ist die Annahme, dass alle Wörter unabhängig voneinander verteilt sind. Dann müssen Sie nur noch die relative Häufigkeit aller Wörter kennen. Es ist anzunehmen, dass sie dem Zipf-Gesetz folgen, dh das Wort mit Rang n in der Liste der Wörter hat eine Wahrscheinlichkeit von ungefähr 1 / ( n log N ), wobei N die Anzahl der Wörter im Wörterbuch ist.
Sobald Sie das Modell repariert haben, können Sie mithilfe der dynamischen Programmierung auf die Position der Räume schließen. Der wahrscheinlichste Satz ist derjenige, der das Produkt der Wahrscheinlichkeit jedes einzelnen Wortes maximiert, und es ist einfach, ihn mit dynamischer Programmierung zu berechnen. Anstatt die Wahrscheinlichkeit direkt zu verwenden, verwenden wir Kosten, die als Logarithmus der Umkehrung der Wahrscheinlichkeit definiert sind, um Überläufe zu vermeiden.
Der Code
mit denen Sie verwenden können
Die Ergebnisse
Ich verwende dieses schnelle und schmutzige Wörterbuch mit 125.000 Wörtern, das ich aus einer kleinen Teilmenge von Wikipedia zusammengestellt habe.
Wie Sie sehen können, ist es im Wesentlichen einwandfrei. Der wichtigste Teil ist, sicherzustellen, dass Ihre Wortliste auf einen Korpus trainiert wurde, der dem ähnelt, auf den Sie tatsächlich stoßen, da sonst die Ergebnisse sehr schlecht sind.
Optimierung
Die Implementierung verbraucht linear viel Zeit und Speicher, ist also einigermaßen effizient. Wenn Sie weitere Beschleunigungen benötigen, können Sie aus der Wortliste einen Suffixbaum erstellen, um die Größe der Kandidatenmenge zu verringern.
Wenn Sie eine sehr große aufeinanderfolgende Zeichenfolge verarbeiten müssen, ist es sinnvoll, die Zeichenfolge zu teilen, um eine übermäßige Speichernutzung zu vermeiden. Sie können den Text beispielsweise in Blöcken mit 10000 Zeichen plus einem Rand von 1000 Zeichen auf beiden Seiten verarbeiten, um Randeffekte zu vermeiden. Dies reduziert die Speichernutzung auf ein Minimum und hat mit ziemlicher Sicherheit keinen Einfluss auf die Qualität.
quelle
pip install wordninja
words.txt
enthält "comp": `` `$ grep" ^ comp $ "words.txt comp` `` und es ist alphabetisch sortiert. Dieser Code geht davon aus, dass er in abnehmender Häufigkeit des Auftretens sortiert ist (was bei solchen n-Gramm-Listen üblich ist). Wenn Sie eine ordnungsgemäß sortierte Liste verwenden, wird Ihre Zeichenfolge gut angezeigt: `` >>> wordninja.split ('namethecompanywherebonniewasemployedwhenwestarteddating') ['name', 'the', 'company', 'where', 'bonnie', ' war ',' angestellt ',' wann ',' wir ',' angefangen ',' datiert '] `` `Basierend auf der hervorragenden Arbeit in der Top-Antwort habe ich ein
pip
Paket für die einfache Verwendung erstellt.Führen Sie zum Installieren aus
pip install wordninja
.Die einzigen Unterschiede sind gering. Dies gibt
list
eher ein als ein zurückstr
, es funktioniert inpython3
, es enthält die Wortliste und wird ordnungsgemäß aufgeteilt, selbst wenn Nicht-Alpha-Zeichen (wie Unterstriche, Bindestriche usw.) vorhanden sind.Nochmals vielen Dank an Generic Human!
https://github.com/keredson/wordninja
quelle
Hier ist eine Lösung mit rekursiver Suche:
ergibt
quelle
Bei Verwendung einer Trie- Datenstruktur , die die Liste möglicher Wörter enthält, wäre es nicht zu kompliziert, Folgendes zu tun:
quelle
"tableprechaun"
danach aufgeteilt werden"tab"
."tableprechaun"
die längste Übereinstimmung von Anfang an ist"table"
, verlassen"prechaun"
, die nicht in Wörterbuchwörter aufgeteilt werden kann. Sie müssen also das kürzere Match nehmen"tab"
und haben ein"leprechaun"
.Die Lösung von Unutbu war ziemlich nah, aber ich finde den Code schwer zu lesen und er lieferte nicht das erwartete Ergebnis. Die Lösung von Generic Human hat den Nachteil, dass Wortfrequenzen benötigt werden. Nicht für alle Anwendungsfälle geeignet.
Hier ist eine einfache Lösung mit einem Divide and Conquer-Algorithmus .
find_words('cupboard')
zurückkehren wird ,['cupboard']
anstatt['cup', 'board']
(dh unter der Annahmecupboard
,cup
undboard
sind in der Dictionnary)find_words('charactersin')
könnte zurückkehren['characters', 'in']
oder vielleicht wird es zurückkehren['character', 'sin']
(wie unten gezeigt). Sie können den Algorithmus ganz einfach ändern, um alle optimalen Lösungen zurückzugeben.Der Code:
Dies dauert auf meinem 3-GHz-Computer ungefähr 5 Sekunden:
quelle
Die Antwort von https://stackoverflow.com/users/1515832/generic-human ist großartig. Aber die beste Umsetzung, die ich je gesehen habe, war Peter Norvig selbst in seinem Buch 'Beautiful Data'.
Bevor ich seinen Code einfüge, möchte ich erläutern, warum Norvigs Methode genauer ist (obwohl sie in Bezug auf den Code etwas langsamer und länger ist).
1) Die Daten sind etwas besser - sowohl in Bezug auf die Größe als auch in Bezug auf die Genauigkeit (er verwendet eine Wortzahl anstelle einer einfachen Rangfolge). 2) Noch wichtiger ist, dass die Logik hinter n-Gramm den Ansatz wirklich so genau macht .
Das Beispiel, das er in seinem Buch liefert, ist das Problem des Aufteilens einer Zeichenfolge "Sitdown". Jetzt würde eine Nicht-Bigram-Methode der Zeichenfolgenaufteilung p ('sit') * p ('down') berücksichtigen, und wenn dies weniger als p ('sitdown') ist - was ziemlich oft der Fall sein wird - wird es NICHT aufgeteilt es, aber wir würden es wollen (meistens).
Wenn Sie jedoch das Bigram-Modell haben, können Sie p ("hinsetzen") als Bigram gegen p ("sitzen") bewerten, und das erstere gewinnt. Wenn Sie keine Bigramme verwenden, wird die Wahrscheinlichkeit, dass die Wörter, die Sie teilen, als unabhängig behandelt, grundsätzlich behandelt. Dies ist jedoch nicht der Fall. Einige Wörter werden eher nacheinander angezeigt. Leider sind dies auch die Wörter, die in vielen Fällen oft zusammenkleben und den Splitter verwirren.
Hier ist der Link zu den Daten (es sind Daten für 3 verschiedene Probleme und die Segmentierung ist nur eine. Bitte lesen Sie das Kapitel für Details): http://norvig.com/ngrams/
und hier ist der Link zum Code: http://norvig.com/ngrams/ngrams.py
Diese Links sind schon eine Weile aktiv, aber ich werde den Segmentierungsteil des Codes hier trotzdem kopieren und einfügen
quelle
RuntimeError: maximum recursion depth exceeded in cmp
Hier ist die akzeptierte Antwort, die in JavaScript übersetzt wurde (erfordert node.js und die Datei "wordninja_words.txt" von https://github.com/keredson/wordninja ):
quelle
Wenn Sie die Wortliste in einen DFA vorkompilieren (was sehr langsam sein wird), ist die Zeit, die zum Abgleichen einer Eingabe benötigt wird, proportional zur Länge der Zeichenfolge (tatsächlich nur ein wenig langsamer als nur das Durchlaufen der Zeichenfolge).
Dies ist effektiv eine allgemeinere Version des zuvor erwähnten Trie-Algorithmus. Ich erwähne es nur für vollständig - bis jetzt gibt es keine DFA-Implementierung, die Sie nur verwenden können. RE2 würde funktionieren, aber ich weiß nicht, ob Sie mit den Python-Bindungen einstellen können, wie groß ein DFA sein darf, bevor er nur die kompilierten DFA-Daten wegwirft und die NFA-Suche durchführt.
quelle
Es scheint, als würde ein ziemlich banales Backtracking ausreichen. Beginnen Sie am Anfang der Saite. Scannen Sie nach rechts, bis Sie ein Wort haben. Rufen Sie dann die Funktion für den Rest der Zeichenfolge auf. Die Funktion gibt "false" zurück, wenn sie ganz nach rechts scannt, ohne ein Wort zu erkennen. Andernfalls wird das gefundene Wort und die Liste der vom rekursiven Aufruf zurückgegebenen Wörter zurückgegeben.
Beispiel: "Tableapple". Findet "tab", dann "leap", aber kein Wort in "ple". Kein anderes Wort in "leapple". Findet "Tabelle" und dann "App". "le" kein Wort, also versucht Apfel, erkennt, kehrt zurück.
Um so lange wie möglich zu werden, machen Sie weiter und geben Sie nur korrekte Lösungen heraus (anstatt sie zurückzugeben). Wählen Sie dann das optimale anhand eines von Ihnen gewählten Kriteriums (Maxmax, Minmax, Durchschnitt usw.).
quelle
Basierend auf der Lösung von unutbu habe ich eine Java-Version implementiert:
Eingang:
"tableapplechairtablecupboard"
Ausgabe:
[table, apple, chair, table, cupboard]
Eingang:
"tableprechaun"
Ausgabe:
[tab, leprechaun]
quelle
Für die deutsche Sprache gibt es CharSplit, das maschinelles Lernen verwendet und für Zeichenfolgen von wenigen Wörtern ziemlich gut funktioniert.
https://github.com/dtuggener/CharSplit
quelle
Wenn Sie den Vorschlag von @ miku zur Verwendung von a erweitern
Trie
,Trie
ist die Implementierung eines Nur-Anhängens relativ einfach inpython
:Wir können dann ein
Trie
Wörterbuch auf Basis einer Reihe von Wörtern erstellen :Was einen Baum erzeugt, der so aussieht (
*
zeigt den Anfang oder das Ende eines Wortes an):Wir können dies in eine Lösung integrieren, indem wir es mit einer Heuristik über die Auswahl von Wörtern kombinieren. Zum Beispiel können wir längere Wörter kürzeren Wörtern vorziehen:
Wir können diese Funktion folgendermaßen verwenden:
Weil wir unsere Position in der halten
Trie
wir für mehr und längere Wörter suchen, durchqueren wir dietrie
höchstens einmal pro mögliche Lösung ( und nicht2
mal fürpeanut
:pea
,peanut
). Der letzte Kurzschluss erspart uns im schlimmsten Fall, char-weise durch die Saite zu gehen.Das Endergebnis ist nur eine Handvoll Inspektionen:
Ein Vorteil dieser Lösung besteht darin, dass Sie sehr schnell wissen, ob längere Wörter mit einem bestimmten Präfix vorhanden sind, sodass Sie die Sequenzkombinationen nicht ausführlich anhand eines Wörterbuchs testen müssen. Es macht es auch
unsolvable
vergleichsweise billig, eine Antwort zu einer anderen Implementierung zu finden.Die Nachteile dieser Lösung sind ein großer Speicherbedarf für die
trie
und die Kosten für dentrie
Aufbau des Vorlaufs.quelle
Wenn Sie eine vollständige Liste der in der Zeichenfolge enthaltenen Wörter haben:
word_list = ["table", "apple", "chair", "cupboard"]
Verwenden des Listenverständnisses zum Durchlaufen der Liste, um das Wort zu lokalisieren und wie oft es erscheint.
Die Funktion gibt eine
string
Ausgabe von Wörtern in der Reihenfolge der Liste zurücktable table apple chair cupboard
quelle
Vielen Dank für die Hilfe in https://github.com/keredson/wordninja/
Ein kleiner Beitrag davon in Java von meiner Seite.
Die öffentliche Methode
splitContiguousWords
kann in die anderen beiden Methoden der Klasse eingebettet werden, die ninja_words.txt im selben Verzeichnis haben (oder gemäß der Wahl des Codierers geändert werden). Und die MethodesplitContiguousWords
könnte für diesen Zweck verwendet werden.quelle
public
akzeptiert die Methode im obigen Ansatz einen Satz vom Typ,String
der basierend auf einer ersten Ebene mit Regex aufgeteilt wird. Und für die Listeninja_words
steht es zum Download im Git Repo zur Verfügung.Das wird helfen
quelle
Sie müssen Ihren Wortschatz identifizieren - vielleicht reicht jede freie Wortliste.
Verwenden Sie anschließend dieses Vokabular, um einen Suffixbaum zu erstellen, und vergleichen Sie Ihren Eingabestream mit dem folgenden: http://en.wikipedia.org/wiki/Suffix_tree
quelle