从两个文件中查找常用单词

时间:2011-03-13 08:18:49

标签: algorithm

鉴于两个文件包含单词列表(大约百万),我们需要找出共同的单词。

使用一些有效的算法,也没有足够的内存(100万,当然不是)..如果可能的话,一些基本的C编程代码会有所帮助。

文件没有排序..我们可以使用某种算法...请用基本代码支持...

对外部文件进行排序......可用的内存最少,如何用C编程实现。

任何用于外部排序文件的游戏......请分享一些代码。

5 个答案:

答案 0 :(得分:3)

又一种方法。

常规即可。首先,请注意按顺序执行O(N^2)。使用N=1,000,000,这很多。对每个列表进行排序需要O(N*log(N));然后你可以通过合并文件找到一个路口(见下文)。总数为O(2N*log(N) + 2N) = O(N*log(N))

对文件进行排序。现在让我们解决这样一个事实:使用文件比使用内存要慢得多,特别是在排序需要移动的地方。解决此问题的一种方法是 - 确定可以加载到内存中的块的大小。一次加载一个文件块,对其进行有效排序并保存到单独的临时文件中。已排序的块可以在一次通过中合并(再次,见下文)到一个已排序的文件中。

<强> Merging 即可。当你有2个排序列表(文件与否)时,你可以在一次通过中轻松地将它们合并到一个排序列表中:有2个“指针”,最初指向每个列表中的第一个条目。在每个步骤中,比较指针指向的值。将较小的值移动到合并列表(您正在构建的列表)并前进其指针。

您可以轻松修改合并算法以使其找到交叉点 - 如果指向的值相等则将其移动到结果中(考虑如何处理重复项)。

对于合并2个以上的列表(如上面的文件排序),您可以推广使用k指针的算法。

答案 1 :(得分:2)

如果你有足够的内存将第一个文件完全读入RAM,我建议将其读入字典(word - &gt;那个词的索引),循环第二个文件的单词并测试单词是否为包含在那本词典中。一百万字的记忆今天并不多。

如果你没有足够的内存,可以将第一个文件拆分成适合内存的块,并按照我上面对每个块的说法进行操作。例如,用前100.000个单词填充字典,找到每个常用单词,然后再次读取文件,提取单词100.001到200.000,找到该部分的常用单词,依此类推。

现在困难的部分:你需要一个字典结构,你说“基本C”。当您愿意使用“基本C ++”时,常见编译器供应商提供hash_map数据结构作为标准库的扩展。在基本的C中,你也应该尝试使用现成的库,阅读this SO post以找到一个似乎支持它的免费库的链接。

答案 2 :(得分:1)

你的问题是:给定两组项目,找到intersaction(两者共有的项目),同时保持RAM不足的限制(小于任何集合的大小)。

由于找到一个intersaction需要比较/搜索另一个集合中的每个项目,你必须有足够的RAM来存储至少一个集合(较小的集合)以获得有效的算法。

假设您知道一个事实,即intersaction比两个集合都小得多,并且完全适合可用内存 - 否则您将需要做进一步的工作将结果刷新到磁盘。

如果您在内存限制下工作,请将较大的组划分为适合可用内存1/3的部分。然后将较小的组分成适合第二个1/3的部分。剩余的1/3内存用于存储结果。

通过查找更大集的分区的最大值和最小值进行优化。这是您要从比较的集合。然后在加载较小集的相应分区时,跳过最小 - 最大范围之外的所有项。

首先通过双循环找到两个分区的间隔,将常用项存储到结果集中,然后将它们从原始集中删除,以便在循环中进一步保存比较。

然后用第二个分区替换较小集中的分区(跳过min-max之外的项目)。重复。请注意,较大集中的分区已减少 - 已删除了常用项。

在浏览整个较小的集后,重复较大集的下一个分区。

现在,如果您不需要保留两个原始集(例如,您可以覆盖这两个文件),那么您还可以通过从磁盘中删除常用项来进一步优化。这样,不再需要在其他分区中比较这些项目。然后,您可以跳过已删除的集合来对集合进行分区。

答案 3 :(得分:1)

我会给前缀树(又名tries)一个镜头。

我最初的方法是确定trie的最大深度,该深度非常适合我的RAM限制。选择一个任意深度(例如3,你可以稍后调整它)并为较小的文件构建一个高达该深度的trie。每个叶子都是一个“文件指针”列表,其中的单词以您到达叶子所遵循的路径编码的前缀开头。这些“文件指针”将保留文件和字长的偏移量。

然后通过读取其中的每个单词并尝试使用您构造的trie在第一个文件中找到它来处理第二个文件。这将允许您在不匹配的单词上更快地失败。你的特里越深,失败的速度就越快,但你消耗的内存就越多。

当然,就像Stephen Chung所说,如果你真的需要一个有效的算法,你还需要RAM来存储足够的信息来描述至少一个文件。如果你没有足够的内存 - 你可能没有,因为我估计我的方法需要大约相同数量的内存,你需要加载一个字长14-22个字符的文件 - 那么你有甚至按部分处理第一个文件。在这种情况下,我实际上建议将trie用于较大的文件,而不是较小的文件。只需将它分成不小于较小文件的部分(或者不超过RAM约束允许的部分),并执行我为每个部分描述的整个过程。

尽管篇幅很长,但这有点偏僻。在某些细节上我可能会非常错误,但这就是我最初解决问题的方法,然后看看它会带给我什么。

答案 4 :(得分:0)

如果你正在寻找这种东西的内存效率,你将很难获得时间效率。我的例子将用python编写,但在任何语言中都应该相对容易实现。

with open(file1) as file_1:
  current_word_1 = read_to_delim(file_1, delim)
  while current_word_1:
    with open(file2) as file_2:
      current_word_2 = read_to_delim(file_2, delim)
      while current_word_2:
        if current_word_2 == current_word_1:
          print current_word_2
        current_word_2 = read_to_delim(file_2, delim)
    current_word_1 = read_to_delim(file_1, delim)

我将read_to_delim留给你,但这是一个极端情况,它是内存最优但时间最不优的。

根据您的应用程序,您可以将两个文件加载到数据库中,执行左外连接,并丢弃两列中的一列为空的行