我认为在不将整个文件加载到内存中(或进行一些昂贵的归并排序)的情况下,没有确定性的方法可以做到这一点。
但是有一些很好的概率算法。看看Count-Min Sketch。
这里有一个很棒的实现,包括其他算法,这个库。
解释一下归并排序的事情:如果您的文件已经排序,您可以使用最小堆相对容易地找到k个最频繁的词。是的,使用最小堆可以丢弃较不常见的词汇,以便在发现更具竞争力的词汇时舍弃它。这样做是因为你可以知道当前单词的频率,而无需读取整个文件。如果文件未排序,则必须保留整个列表,因为最常用的术语可能出现在文件的任何位置,并且会被过早地丢弃为“非竞争性”。
您可以使用有限的内存轻松地进行合并排序,但这是一项I/O密集型操作,可能需要一段时间。实际上,您可以使用任何一种外部排序。
根据您的评论,您需要计算频率。
您没有说明数据中有多少单词,或者什么构成一个单词。如果是英文文本,我会惊讶地看到50万个单词。5GB的文本中肯定不会有10亿个单词。但是,无论有多少单词,技术都不会真正改变。
首先要建立一个包含键值对(单词、计数)的字典或哈希表。读取每个单词时,在字典中查找它。如果存在,则增加其计数。如果不存在,则将其添加到字典中,并设置计数为1。
如果您拥有大量内存或相对较少的单词,则所有内容都可以放入内存中。如果是这样,您可以按照下面描述的方法进行堆排序。
如果您的内存填满了,则只需将键值对写入文本文件中,每行一个单词,像这样:
word1, count
word2, count
接下来,清空字典并继续添加单词或增加它们的计数。对于每个单词块,重复此操作直到输入结束。
现在你有一个包含单词/计数对的巨大文本文件。按单词对其进行排序。有许多外部排序工具可以做到这一点。两个我想到的是Windows的SORT实用程序和GNU sort。两者都可以轻松地对短行的非常大的文件进行排序。
一旦按单词排序,你将得到:
word1, count
word1, count
word2, count
word3, count
word3, count
word3, count
heap = new minheap();
for each item
// if you don't already have k items on the heap, add this one
if (heap.count < k)
heap.Add(item)
else if (item.frequency > heap.Peek().frequency)
{
// The new item's frequency is greater than the lowest frequency
// already on the heap. Remove the item from the heap
// and add the new item.
heap.RemoveRoot();
heap.Add(item);
}
在处理完所有项之后,堆将包含具有最高频率的k
项。
你可以使用选择算法(http://en.wikipedia.org/wiki/Selection_algorithm)来计算第k大的数字。然后进行线性扫描并仅选择k个大数字。
在实践中,您可能希望从估计的范围开始,其中kth min错误,并从那里继续。例如,读取前M个数字并计算估计的kth max =(k * M / N)th max在M个数字中。如果您认为数据存在偏差(即部分排序),则随机选择这些M个数字。