Ini adalah masalah klasik yang mendapat resonansi pada tahun 1986, ketika Donald Knuth menerapkan solusi cepat dengan hash mencoba dalam program sepanjang 8 halaman untuk menggambarkan teknik pemrograman melek hurufnya, sementara Doug McIlroy, ayah baptis pipa Unix, merespons dengan satu-liner, itu tidak secepat, tetapi menyelesaikan pekerjaan:
tr -cs A-Za-z '\n' | tr A-Z a-z | sort | uniq -c | sort -rn | sed 10q
Tentu saja, solusi McIlroy memiliki kompleksitas waktu O (N log N), di mana N adalah jumlah total kata. Ada banyak solusi yang lebih cepat. Sebagai contoh:
Berikut ini adalah implementasi C ++ dengan kompleksitas waktu batas atas O ((N + k) log k), biasanya - hampir linier.
Di bawah ini adalah implementasi Python cepat menggunakan kamus hash dan tumpukan dengan kompleksitas waktu O (N + k log Q), di mana Q adalah sejumlah kata unik:
import collections, re, sys
filename = sys.argv[1]
k = int(sys.argv[2]) if len(sys.argv)>2 else 10
text = open(filename).read()
counts = collections.Counter(re.findall('[a-z]+', text.lower()))
for i, w in counts.most_common(k):
print(i, w)
Perbandingan waktu CPU (dalam detik):
bible32 bible256
C++ (prefix tree + heap) 5.659 44.730
Python (Counter) 10.314 100.487
Sheharyar (AWK + sort) 30.864 251.301
McIlroy (tr + sort + uniq) 60.531 690.906
Catatan:
- bible32 adalah Alkitab yang digabungkan dengan dirinya sendiri 32 kali (135 MB), bible256 - 256 kali masing-masing (1,1 GB).
- Perlambatan non-linier skrip Python disebabkan murni oleh fakta bahwa ia memproses file sepenuhnya dalam memori, sehingga overhead semakin besar untuk file besar.
- Jika ada alat Unix yang dapat membangun heap dan memilih n elemen dari atas heap, solusi AWK dapat mencapai kompleksitas waktu dekat-linear, sedangkan saat ini adalah O (N + Q log Q).