Eingabe: Eine positive ganze Zahl K und ein großer Text. Der Text kann tatsächlich als Wortfolge angesehen werden. Wir müssen uns also keine Gedanken darüber machen, wie wir es in Wortfolgen aufteilen können.
Ausgabe: Die häufigsten K Wörter im Text.
Mein Denken ist so.
Verwenden Sie eine Hash-Tabelle, um die Häufigkeit aller Wörter aufzuzeichnen, während Sie die gesamte Wortfolge durchlaufen. In dieser Phase ist der Schlüssel "Wort" und der Wert ist "Wortfrequenz". Dies dauert O (n) Zeit.
sortiere das (Wort, Wort-Frequenz) Paar; und der Schlüssel ist "Worthäufigkeit". Dies dauert bei normalem Sortieralgorithmus O (n * lg (n)).
Nach dem Sortieren nehmen wir nur die ersten K Wörter. Dies dauert O (K) Zeit.
Zusammenfassend ist die Gesamtzeit O (n + n lg (n) + K) , Da K sicherlich kleiner als N ist, ist es tatsächlich O (n lg (n)).
Wir können das verbessern. Eigentlich wollen wir nur Top-K-Wörter. Mit anderen Worten, die Häufigkeit ist für uns nicht von Belang. Wir können also "partielle Heap-Sortierung" verwenden. In Schritt 2) und 3) sortieren wir nicht nur. Stattdessen ändern wir es so
2 ') einen Haufen von (Wort-, Wortfrequenz-) Paaren mit "Wortfrequenz" als Schlüssel aufbauen. Es dauert O (n) Zeit, um einen Heap zu erstellen;
3 ') extrahiere die obersten K Wörter aus dem Haufen. Jede Extraktion ist O (lg (n)). Die Gesamtzeit beträgt also O (k * lg (n)).
Zusammenfassend kostet diese Lösung Zeit O (n + k * lg (n)).
Das ist nur mein Gedanke. Ich habe keinen Weg gefunden, um Schritt 1) zu verbessern.
Ich hoffe, dass einige Experten für Information Retrieval mehr Licht in diese Frage bringen können.
quelle
Antworten:
Dies kann in O (n) Zeit erfolgen
Lösung 1:
Schritte:
Zähle die Wörter und hashe sie, was in der Struktur wie dieser endet
Durchlaufen Sie den Hash und suchen Sie das am häufigsten verwendete Wort (in diesem Fall "foo" 100). Erstellen Sie dann das Array dieser Größe
Dann können wir den Hash erneut durchlaufen und die Anzahl der vorkommenden Wörter als Array-Index verwenden. Wenn der Index nichts enthält, erstellen Sie ein Array, und fügen Sie es dem Array hinzu. Dann erhalten wir ein Array wie:
Dann durchlaufen Sie einfach das Array vom Ende und sammeln die k Wörter.
Lösung 2:
Schritte:
quelle
Sie erhalten im Allgemeinen keine bessere Laufzeit als die von Ihnen beschriebene Lösung. Sie müssen mindestens O (n) arbeiten, um alle Wörter zu bewerten, und dann O (k) zusätzliche Arbeit, um die Top-k-Begriffe zu finden.
Wenn Ihr Problem sehr groß ist, können Sie eine verteilte Lösung wie Map / Reduce verwenden. Lassen Sie n Kartenarbeiter die Häufigkeit auf jeweils 1 / n des Textes zählen und senden Sie sie für jedes Wort an einen der m Reduzierer, die anhand des Hash des Wortes berechnet wurden. Die Reduzierer summieren dann die Zählungen. Wenn Sie die Sortierung über die Ausgänge der Reduzierungen zusammenführen, erhalten Sie die beliebtesten Wörter in der Reihenfolge ihrer Beliebtheit.
quelle
Eine kleine Variation Ihrer Lösung ergibt einen O (n) -Algorithmus, wenn es uns nicht darum geht, die oberste K zu klassifizieren, und eine O (n + k * lg (k)) - Lösung, wenn wir dies tun. Ich glaube, diese beiden Grenzen sind innerhalb eines konstanten Faktors optimal.
Die Optimierung erfolgt hier erneut, nachdem wir die Liste durchlaufen und in die Hash-Tabelle eingefügt haben. Wir können den Median des Median- Algorithmus verwenden, um das k-te größte Element in der Liste auszuwählen. Dieser Algorithmus ist nachweislich O (n).
Nachdem wir das kleinste K-te Element ausgewählt haben, teilen wir die Liste genau wie bei Quicksort um dieses Element auf. Dies ist offensichtlich auch O (n). Alles auf der "linken" Seite des Pivots befindet sich in unserer Gruppe von K Elementen, also sind wir fertig (wir können einfach alles andere wegwerfen, während wir weitergehen).
Diese Strategie lautet also:
Wenn Sie die K-Elemente einordnen möchten, sortieren Sie sie einfach mit einer effizienten Vergleichssortierung in O (k * lg (k)), was eine Gesamtlaufzeit von O (n + k * lg (k)) ergibt.
Die O (n) -Zeitgrenze ist innerhalb eines konstanten Faktors optimal, da wir jedes Wort mindestens einmal untersuchen müssen.
Die zeitgebundene O (n + k * lg (k)) - Zeit ist ebenfalls optimal, da es keine vergleichsbasierte Möglichkeit gibt, k Elemente in weniger als k * lg (k) Zeit zu sortieren.
quelle
Wenn Ihre "große Wortliste" groß genug ist, können Sie einfach eine Stichprobe erstellen und Schätzungen abrufen. Ansonsten mag ich Hash-Aggregation.
Bearbeiten :
Mit Beispiel meine ich, wählen Sie eine Teilmenge von Seiten und berechnen Sie das häufigste Wort auf diesen Seiten. Vorausgesetzt, Sie wählen die Seiten in angemessener Weise aus und wählen eine statistisch signifikante Stichprobe aus, sollten Ihre Schätzungen der häufigsten Wörter angemessen sein.
Dieser Ansatz ist wirklich nur dann sinnvoll, wenn Sie so viele Daten haben, dass die Verarbeitung einfach nur albern ist. Wenn Sie nur ein paar Megabyte haben, sollten Sie in der Lage sein, die Daten zu durchbrechen und eine genaue Antwort zu berechnen, ohne ins Schwitzen zu geraten, anstatt sich die Mühe zu machen, eine Schätzung zu berechnen.
quelle
Sie können die Zeit weiter verkürzen, indem Sie mit dem ersten Buchstaben von Wörtern partitionieren und dann den größten Mehrwortsatz mit dem nächsten Zeichen partitionieren, bis Sie k Einzelwortsätze haben. Sie würden eine Art 256-Wege-Baum mit Listen von Teil- / vollständigen Wörtern an den Blättern verwenden. Sie müssen sehr vorsichtig sein, um nicht überall String-Kopien zu verursachen.
Dieser Algorithmus ist O (m), wobei m die Anzahl der Zeichen ist. Es vermeidet diese Abhängigkeit von k, was für große k sehr schön ist [da Ihre angegebene Laufzeit falsch ist, sollte es O (n * lg (k)) sein, und ich bin mir nicht sicher, was das bedeutet m].
Wenn Sie beide Algorithmen nebeneinander ausführen, erhalten Sie einen asymptotisch optimalen O-Algorithmus (min (m, n * lg (k))), der meiner Meinung nach im Durchschnitt schneller sein sollte, da er nicht involviert ist Hashing oder Sortieren.
quelle
Ihre Beschreibung enthält einen Fehler: Das Zählen dauert O (n), das Sortieren jedoch O (m * lg (m)), wobei m die Anzahl der eindeutigen Wörter ist. Dies ist normalerweise viel kleiner als die Gesamtzahl der Wörter, daher sollte wahrscheinlich nur optimiert werden, wie der Hash erstellt wird.
quelle
Ihr Problem ist dasselbe wie dieses: http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/
Verwenden Sie Trie und Min Heap, um es effizient zu lösen.
quelle
Wenn Sie nach der Liste der k häufigsten Wörter in Ihrem Text für ein praktisches k und für eine natürliche Sprache suchen, ist die Komplexität Ihres Algorithmus nicht relevant.
Nur Probe , sagen wir, ein paar Millionen Wörter aus dem Text, Verfahren , dass mit jedem Algorithmus in einer Angelegenheit von Sekunden , und die häufigsten Zählungen sehr genau sein.
Als Randnotiz ist die Komplexität des Dummy-Algorithmus (1. alle zählen 2. die Zählungen sortieren 3. die besten nehmen) O (n + m * log (m)), wobei m die Anzahl der verschiedenen Wörter in Ihrem ist Text. log (m) ist viel kleiner als (n / m), also bleibt es O (n).
Praktisch zählt der lange Schritt.
quelle
Hier ist der Code
}}
Hier sind die Unit-Tests
Weitere Einzelheiten finden Sie in diesem Testfall
quelle
Verwenden Sie eine Hash-Tabelle, um die Häufigkeit aller Wörter aufzuzeichnen, während Sie die gesamte Wortfolge durchlaufen. In dieser Phase ist der Schlüssel "Wort" und der Wert ist "Wortfrequenz". Dies dauert O (n) Zeit. Dies ist das gleiche wie bei jedem oben erläuterten
Behalten Sie beim Einfügen in die Hashmap das Treeset (spezifisch für Java, es gibt Implementierungen in jeder Sprache) der Größe 10 (k = 10) bei, um die 10 häufigsten Wörter beizubehalten. Bis die Größe weniger als 10 beträgt, fügen Sie sie hinzu. Wenn die Größe gleich 10 ist, wenn das eingefügte Element größer als das minimale Element ist, dh das erste Element. Wenn ja, entfernen Sie es und fügen Sie ein neues Element ein
Informationen zum Einschränken der Größe des Baumsatzes finden Sie unter diesem Link
quelle
Angenommen, wir haben eine Wortfolge "ad" "ad" "boy" "big" "bad" "com" "come" "cold". Und K = 2. Wie Sie erwähnt haben "Partitionierung mit dem ersten Buchstaben", haben wir dann ("Anzeige", "Anzeige") ("Junge", "groß", "schlecht") ("com" "kommen" "kalt") " Partitionieren des größten Mehrwortsatzes mit dem nächsten Zeichen, bis Sie k Einzelwortsätze haben. " es wird partitioniert ("Junge", "groß", "schlecht") ("com" "kommt" "kalt"), die erste Partition ("Anzeige", "Anzeige") wird übersehen, während "Anzeige" tatsächlich die ist häufigstes Wort.
Vielleicht verstehe ich Ihren Standpunkt falsch. Können Sie bitte Ihren Prozess bezüglich der Partition detaillieren?
quelle
Ich glaube, dieses Problem kann durch einen O (n) -Algorithmus gelöst werden. Wir könnten die Sortierung im laufenden Betrieb durchführen. Mit anderen Worten, die Sortierung ist in diesem Fall ein Unterproblem des herkömmlichen Sortierproblems, da bei jedem Zugriff auf die Hash-Tabelle nur ein Zähler um eins erhöht wird. Zu Beginn wird die Liste sortiert, da alle Zähler Null sind. Während wir die Zähler in der Hash-Tabelle weiter inkrementieren, führen wir ein weiteres Array von Hash-Werten, die nach Häufigkeit geordnet sind, wie folgt. Jedes Mal, wenn wir einen Zähler erhöhen, überprüfen wir seinen Index im Rangarray und prüfen, ob seine Anzahl den Vorgänger in der Liste überschreitet. Wenn ja, tauschen wir diese beiden Elemente aus. Als solche erhalten wir eine Lösung, die höchstens O (n) ist, wobei n die Anzahl der Wörter im Originaltext ist.
quelle
Ich hatte auch damit zu kämpfen und ließ mich von @aly inspirieren. Anstatt danach zu sortieren, können wir einfach eine vorsortierte Liste von Wörtern (
List<Set<String>>
) pflegen, und das Wort befindet sich in der Menge an Position X, wobei X die aktuelle Anzahl des Wortes ist. Im Allgemeinen funktioniert es folgendermaßen:Map<String, Integer>
.Der Nachteil dabei ist, dass die Liste möglicherweise groß ist - kann mithilfe von a optimiert werden
TreeMap<Integer, Set<String>>
-, aber dies erhöht den Overhead. Letztendlich können wir eine Mischung aus HashMap oder unserer eigenen Datenstruktur verwenden.Der Code
quelle
Ich finde gerade die andere Lösung für dieses Problem heraus. Aber ich bin nicht sicher, ob es richtig ist. Lösung:
quelle
Versuchen Sie, sich eine spezielle Datenstruktur auszudenken, um diese Art von Problemen anzugehen. In diesem Fall ist eine spezielle Baumart wie der Versuch, Zeichenfolgen auf bestimmte Weise zu speichern, sehr effizient. Oder eine zweite Möglichkeit, eine eigene Lösung wie das Zählen von Wörtern zu erstellen. Ich denke, diese TB Daten wären auf Englisch, dann haben wir im Allgemeinen ungefähr 600.000 Wörter, so dass es möglich sein wird, nur diese Wörter zu speichern und zu zählen, welche Zeichenfolgen wiederholt werden würden + diese Lösung benötigt Regex, um einige Sonderzeichen zu eliminieren. Die erste Lösung wird schneller sein, da bin ich mir ziemlich sicher.
http://en.wikipedia.org/wiki/Trie
quelle
Dies ist eine interessante Idee für die Suche und ich konnte dieses Papier im Zusammenhang mit Top-K https://icmi.cs.ucsb.edu/research/tech_reports/reports/2005-23.pd finden f finden
Auch gibt es eine Implementierung es hier .
quelle
Einfachster Code, um das Auftreten des am häufigsten verwendeten Wortes zu ermitteln.
quelle
In diesen Situationen empfehle ich die Verwendung der in Java integrierten Funktionen. Da sind sie schon gut getestet und stabil. In diesem Problem finde ich die Wiederholungen der Wörter mithilfe der HashMap-Datenstruktur. Dann schiebe ich die Ergebnisse auf ein Array von Objekten. Ich sortiere das Objekt nach Arrays.sort () und drucke die obersten k Wörter und ihre Wiederholungen.
Weitere Informationen finden Sie unter https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/TopKWordsTextFile.java . Ich hoffe, es hilft.
quelle
I recommend to use Java built-in features
Wie foreach Schleifen und Streams Verarbeitung ?)** **.
};
quelle