Dies ist vielleicht eine der klassischen Codierungsherausforderungen, die 1986 Anklang fanden, als der Kolumnist Jon Bentley Donald Knuth aufforderte, ein Programm zu schreiben, das k häufigste Wörter in einer Datei findet. Knuth implementierte eine schnelle Lösung mit Hash-Versuchen in einem 8-seitigen Programm, um seine literarische Programmiertechnik zu veranschaulichen. Douglas McIlroy von Bell Labs kritisierte Knuths Lösung als nicht einmal in der Lage, einen vollständigen Text der Bibel zu verarbeiten, und antwortete mit einem Einzeiler, der nicht so schnell sei, aber die Aufgabe erledige:
tr -cs A-Za-z '\n' | tr A-Z a-z | sort | uniq -c | sort -rn | sed 10q
1987 wurde ein Folgeartikel mit einer weiteren Lösung veröffentlicht, diesmal von einem Princeton-Professor. Aber es konnte nicht einmal ein Ergebnis für eine einzige Bibel liefern!
Problembeschreibung
Ursprüngliche Problembeschreibung:
Bei einer gegebenen Textdatei und einer Ganzzahl k müssen Sie die k häufigsten Wörter in der Datei (und die Anzahl ihrer Vorkommen) in abnehmender Häufigkeit drucken.
Zusätzliche Problemklärungen:
- Knuth definierte ein Wort als eine Folge lateinischer Buchstaben:
[A-Za-z]+
- Alle anderen Zeichen werden ignoriert
- Groß- und Kleinbuchstaben werden als gleichwertig angesehen (
WoRd
==word
) - Keine Beschränkung der Dateigröße oder der Wortlänge
- Abstände zwischen aufeinanderfolgenden Wörtern können beliebig groß sein
- Das schnellste Programm benötigt am wenigsten CPU-Zeit (Multithreading hilft wahrscheinlich nicht)
Beispiel-Testfälle
Test 1: Ulysses von James Joyce 64-mal verkettet (96 MB-Datei).
- Laden Sie Ulysses vom Projekt Gutenberg herunter :
wget http://www.gutenberg.org/files/4300/4300-0.txt
- Verketten Sie es 64 Mal:
for i in {1..64}; do cat 4300-0.txt >> ulysses64; done
- Das häufigste Wort ist "der" mit 968832 Erscheinungen.
Test 2: Speziell generierter zufälliger Text giganovel
(ca. 1 GB).
- Python 3-Generator-Skript hier .
- Der Text enthält 148391 verschiedene Wörter, die den natürlichen Sprachen ähnlich sind.
- Häufigste Wörter: "e" (11309 Auftritte) und "ihit" (11290 Auftritte).
Allgemeinheitstest: Beliebig große Wörter mit beliebig großen Lücken.
Referenzimplementierungen
Nachdem ich Rosetta Code auf dieses Problem untersucht und festgestellt habe, dass viele Implementierungen unglaublich langsam sind (langsamer als das Shell-Skript!), Habe ich hier einige gute Implementierungen getestet . Nachfolgend finden Sie die Leistung ulysses64
zusammen mit der Zeitkomplexität:
ulysses64 Time complexity
C++ (prefix trie + heap) 4.145 O((N + k) log k)
Python (Counter) 10.547 O(N + k log Q)
AWK + sort 20.606 O(N + Q log Q)
McIlroy (tr + sort + uniq) 43.554 O(N log N)
Kannst du das schlagen?
Testen
Die Leistung wird mit dem 13 "MacBook Pro 2017 mit dem Standard-Unix- time
Befehl (" Benutzer "-Zeit) bewertet . Wenn möglich, verwenden Sie bitte moderne Compiler (z. B. verwenden Sie die neueste Haskell-Version, nicht die ältere).
Bisherige Platzierungen
Timings, einschließlich der Referenzprogramme:
k=10 k=100K
ulysses64 giganovel giganovel
C (trie + bins) by Moogie 0.704 9.568 9.459
C (trie + list) by Moogie 0.767 6.051 82.306
C (trie + sorted list) by Moogie 0.804 7.076 x
Rust (trie) by Anders Kaseorg 0.842 6.932 7.503
J by miles 1.273 22.365 22.637
C# (trie) by recursive 3.722 25.378 24.771
C++ (trie + heap) 4.145 42.631 72.138
APL (Dyalog Unicode) by Adám 7.680 x x
Python (dict) by movatica 9.387 99.118 100.859
Python (Counter) 10.547 102.822 103.930
Ruby (tally) by daniero 15.139 171.095 171.551
AWK + sort 20.606 213.366 222.782
McIlroy (tr + sort + uniq) 43.554 715.602 750.420
Kumulatives Ranking * (%, bestmögliche Punktzahl - 300):
# Program Score Generality
1 Rust (trie) by Anders Kaseorg 334 Yes
2 C (trie + bins) by Moogie 384 x
3 J by miles 852 Yes
4 C# (trie) by recursive 1278 x
5 C (trie + list) by Moogie 1306 x
6 C++ (trie + heap) 2255 x
7 Python (dict) by movatica 4316 Yes
8 Python (Counter) 4583 Yes
9 Ruby (tally) by daniero 7264 Yes
10 AWK + sort 9422 Yes
11 McIlroy (tr + sort + uniq) 28014 Yes
* Summe der Zeitleistung im Verhältnis zu den besten Programmen in jedem der drei Tests.
Bestes Programm: hier .