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
from math import log
# Build a cost dictionary, assuming Zipf's law and cost = -math.log(probability).
words = open("words-by-frequency.txt").read().split()
wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words))
maxword = max(len(x) for x in words)
def infer_spaces(s):
"""Uses dynamic programming to infer the location of spaces in a string
without spaces."""
# Find the best match for the i first characters, assuming cost has
# been built for the i-1 first characters.
# Returns a pair (match_cost, match_length).
def best_match(i):
candidates = enumerate(reversed(cost[max(0, i-maxword):i]))
return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates)
# Build the cost array.
cost = [0]
for i in range(1,len(s)+1):
c,k = best_match(i)
cost.append(c)
# Backtrack to recover the minimal-cost string.
out = []
i = len(s)
while i>0:
c,k = best_match(i)
assert c == cost[i]
out.append(s[i-k:i])
i -= k
return " ".join(reversed(out))
mit denen Sie verwenden können
s = 'thumbgreenappleactiveassignmentweeklymetaphor'
print(infer_spaces(s))
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.
Vorher: thumbgreenappleactiveassignmentweeklymetaphor.
Nachher: Daumengrün Apfel aktive Zuordnung wöchentliche Metapher.
Vorher: Es gibt eine Reihe von Textinformationen von Personenkommentaren, die aus dem ml herausgesucht wurden, aber keine begrenzten Zeichen in der vorherigen Beispiel-Grün-App-Aktivierungszuweisung enthalten sind.
Nachher: Es gibt eine Menge Textinformationen von Kommentaren von Leuten, die aus HTML analysiert werden, aber es gibt keine abgegrenzten Zeichen in ihnen, zum Beispiel Daumengrünapfel, aktive Zuordnung, wöchentliche Metapher, anscheinend gibt es Daumengrünapfel usw. in der Zeichenfolge, zu der ich auch ein großes Wörterbuch habe Fragen Sie, ob das Wort vernünftig ist. Was ist der schnellste Weg, um viel zu extrahieren?
Vorher: Es war dunkel und stürmisch, und es kam zu heftigen Abständen, außer bei gelegentlichen Intervallen, bei denen die Straßen wegen einer Entzündung in der Mitte der Straßen, die zwischen den Hausoberseiten klapperten, heftig klapperten und die Flamme der Lampe, die stark bekämpft war, heftig klapperten.
Nachher: Es war eine dunkle und stürmische Nacht, in der der Regen in Strömen fiel, außer in gelegentlichen Abständen, als er von einem heftigen Windstoß kontrolliert wurde, der die Straßen fegte, denn in London liegt unsere Szene rasselnd auf den Hausdächern und bewegt die spärliche Flamme der Lampen, die gegen die Dunkelheit kämpften.
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.