长话短说:1986年,一位面试官让唐纳德•克努特(donald knuth)编写一个程序,输入一段文字和一个数字n,并按频率列出n个最常用的单词。Knuth制作了一个长达10页的Pascal程序,Douglas McIlroy用下面的6行shell脚本回答了这个程序:
tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q
阅读http://www.leancrew.com/all-this/2011/12/more-shell-less-egg/上的完整故事。
当然,他们有非常不同的目标:knuth展示了他识字编程的概念,从头开始构建所有东西,而mcilroy使用一些常见的unix实用程序来实现最短的源代码。
我的问题是:那有多糟?
(纯粹从运行时速度的角度来看,因为我很确定我们都同意6行代码比10页代码更容易理解/维护,不管是否懂编程。)
我可以理解
sort -rn | sed ${1}q
可能不是提取常用单词的最有效方法,但是tr -sc A-za-z '\n' | tr A-Z a-z
有什么问题?我觉得很不错。关于
sort | uniq -c
,这是一种非常缓慢的确定频率的方法吗?一些考虑:
tr
应为线性时间(?)我不确定,但我想还没那么糟
sort
也应该是线性时间生成进程应该是线性时间(以进程数为单位)
最佳答案
Unix
脚本有几个线性操作和两个排序。它将是计算顺序O(n log(n))
。
对于仅取前n的knuth算法:http://en.wikipedia.org/wiki/Selection_algorithm
你可以在算法的时间和空间复杂度上有一些选择,但是从理论上讲,对于一些具有大量(不同)词的典型例子,它们可以更快。
所以克努特可以更快。当然,因为这本英语词典的篇幅有限。它可以在某个大的常数中变成log(n)
,尽管可能会消耗大量的内存。
但也许这个问题更适合https://cstheory.stackexchange.com/