部分堆排序在5GB文件中查找k个最常用的单词

时间:2013-02-26 20:54:17

标签: database algorithm data-structures hash large-files

我知道我想使用哪种算法,但想知道由于文件太大而需要更改的内容。

我想使用哈希来存储单词的频率,并使用最小堆来存储最常用的单词,并在循环单词时相应地调整最小堆。我认为这应该采取O(nlogk)。如果我有太多的数据存储在内存中,我的算法将如何更改?这是一个我一般难以理解的问题,不仅仅是针对这个具体问题,而是我只是给出了背景,以便它可以帮助解释。

3 个答案:

答案 0 :(得分:4)

我认为如果不将整个文件放在内存中(或进行一些昂贵的合并排序),就没有确定的方法可以做到这一点。

但是有一些很好的概率算法。看看Count-Min Sketch

this library中,有很好的实现此算法和其他算法。

解释合并排序的事情:如果你的文件已经排序,你可以很容易地用最小堆找到k。是的,当你发现一个更具竞争力的时候,能够丢弃较不频繁的术语的最小堆。您可以这样做,因为您可以知道当前单词的频率而无需读取整个文件。如果您的文件未分类,则必须保留整个列表,因为最常用的术语可能出现在文件的任何位置,并且过早地被丢弃为“非竞争性”。

您可以很容易地对内存有限进行合并排序,但这是I / O密集型操作,可能需要一段时间。实际上你可以使用任何类型的External Sort

答案 1 :(得分:4)

在评论后添加您需要计算频率。

您没有说出您期望在数据中有多少单词,或者单词构成了什么。如果是英文文本,我会惊讶地发现有50万字。在5千兆字节的文本中肯定不会有十亿字。但是这种技术并没有真正改变,无论有多少单词。

首先构建包含键值对的字典或哈希映射:word,count。在阅读每个单词时,请在字典中查找。如果它在那里,增加其数量。如果不存在,请将其添加为1。

如果你有很多记忆或相对较少的单词,它们都会适合记忆。如果是这样,你可以做我在下面描述的堆。

如果您的内存已满,则只需将键值对写入文本文件,每行一个单词,如下所示:

word1, count
word2, count

然后清除字典并继续前进,添加单词或增加计数。根据需要对每个单词块重复,直到到达输入结束。

现在你有一个包含字/计数对的巨大文本文件。按字词排序。有许多外部排序工具可以做到这一点。想到的两个是Windows SORT实用程序和GNU排序。两者都可以轻松地对非常大的短行文件进行排序。

文件按字词排序后,您将拥有:

word1, count
word1, count
word2, count
word3, count
word3, count
word3, count

现在,按顺序浏览文件,累积单词计数是一件简单的事情。在每个单词中断处,检查其对堆的计数,如下所述。

整个过程需要一些时间,但效果很好。您可以通过对单词块进行排序并将其写入单个文件来加快速度。然后,当您到达输入结束时,在几个块上进行N路合并。这更快,但强制你编写合并程序,除非你能找到一个。如果我这样做一次,我会选择简单的解决方案。如果我经常这样做,我会花时间编写自定义合并程序。

计算出频率后......

假设你的文件包含单词及其频率,你想要做的就是获得频率最高的k个单词,然后是O(n log k),你不需要将所有项目存储在内存中。你的堆只需要k个项目。

这个想法:

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个项目。

答案 2 :(得分:0)

您可以使用选择算法(http://en.wikipedia.org/wiki/Selection_algorithm)来计算第k个最大数字。然后进行线性扫描并仅选择k个大数。

在实践中,您可能希望从估计范围开始,其中kth min false进入并从那里继续。例如。读取前M个数并计算M个数中的估计kth max =(k * M / N)th max。如果您认为数据有偏差(即部分排序),则随机选择那些M数。

相关问题