我遇到了以下问题 - 我收到一个文本文件,其中包含大量允许重复的单词。我需要编写一个算法,输出那些出现频率最高的1000个单词。这是一个例子
**input.txt**
aa aa bb cc bb bb bb dd dd
**output.txt** (note - frequencies can be repeated)
bb 4
aa 2
dd 2
cc 1
答案 0 :(得分:4)
有没有更好的方法来解决这个问题?
有两种方法可以看待这个......取决于你对“更好”的意思。
如果“更好”意味着“使用更少的时间和/或内存”,那么有可能解决此问题,具体取决于问题的大小和您可以使用的资源。例如:
如果文本文件(或多个文件)足够大以证明这一点,您可以考虑将问题分区为在多处理器上运行。但这不是直截了当的......如果问题太小,分区的开销实际上会使解决方案变慢!
链接的Q& A描述了一种可能加快您的步骤2和3的方法。问题是,如果您对代码进行基准测试,您可能会发现第1步是花费大部分CPU时间的地方。 (输入文件越大,效果就越明显......)
如果“更好”意味着“更快地解决问题”,那么答案就是“可能没有”。假设您已经实现了所描述的算法的良好实现版本,那么采用更加复杂的算法(可能)更快的努力可能并不值得。
计算机时间比软件开发人员时间便宜!
我的建议是做以下事情:
对现有计划进行基准测试。在真实的输入文件上运行得足够快吗?如果“是”,请调用程序完成。
配置在现实输入文件上运行的现有程序。分析是否显示任何明显的性能热点?有代码调整的机会吗?如果“是”尝试,请重复基准测试步骤,看看他们是否确实有所帮助。
查看GC日志。是否有迹象表明您的代码有过多的GC开销?如果“是”,请查看是否有一些简单的GC调整(如增加堆大小)会产生影响。
如果在上述所有情况之后仍然没有达到可接受的性能,那么现在是时候开始寻找替代算法了。
答案 1 :(得分:2)
让我们关注第2步和第3步。假设有N个单词,每个单词都有一个给定的频率,你必须找到前K(在你的情况下为1000)。
您的方法在O(N log N)中运行。斯蒂芬提到,这已经足够了:)无论如何,这里有一些可能的改进。
第一种方法:正如链接中所建议的那样,您可以使用最小堆(您可以使用优先级队列)来维护前1000名。实现起来并不难。获得具有频率的地图后,开始迭代它。如果最小堆的大小小于K,则继续推动该对(字,频率)。如果堆的大小为K,则将当前条目的频率与映射到堆的根(堆中的最低频率)进行比较。如果新频率较低,请忽略它。如果它更高,则从堆中弹出最低值并推送当前值。这种方法将在O(N log K)中运行。
第二种方法: selection algorithm在O(N)中找到列表(未排序)中的第K个最大(或最小)数字。您可以找到最大的K_th,然后迭代地图以获得小于或等于K的所有内容。如果大于K,则必须是第K_th个最大数字重复多次的情况。如果你跟踪它,你可以删除这个数字所需的数量来获得前K.这将需要O(N)。
第三种方法:你可以做一个随机快速排序的变种来找到前K.我认为,预期的运行时间是O(N)。