找到存储在文件中的数字中再次出现的数字

时间:2010-08-02 14:16:24

标签: c algorithm

说,我有10亿个数字存储在一个文件中。我怎么能找到以前曾出现的号码?

好吧,我不能只是在数组中一次填充数十亿个数字,然后保持一个简单的嵌套循环来检查数字是否先前出现过。

你会如何解决这个问题?

提前致谢:)

15 个答案:

答案 0 :(得分:7)

我曾经把这作为面试问题。

这是一个O(N)

的算法

使用哈希表。按顺序存储指向数字的指针,其中散列键是根据数值计算的。一旦发生碰撞,您就找到了副本。

作者编辑:

下面,@ Phimuemue明确指出,在保证冲突之前,4字节整数具有固定的界限;即2 ^ 32,或约。 4GB。在伴随此答案的对话中考虑时,此算法的最坏情况内存消耗将大幅降低。

此外,使用如下所述的位阵列可以将内存消耗减少到1/8,512mb。在许多机器上,现在可以在不考虑 持久散列,性能较低的排序优先策略的情况下进行此计算。

现在,较长的数字或双精度数字是比特阵列策略的低效方案。

Phimuemue编辑:

当然需要采取一些“特殊”哈希表:

获取由2 ^ 32位组成的哈希表。由于该问题询问了4字节整数,因此它们中至多有2 ^ 32个不同,即每个数字一位。 2 ^ 32位= 512mb。

所以现在只需确定hashmap中相应位的位置并进行设置即可。如果遇到已经设置的位,则序列中已经出现了数字。

答案 1 :(得分:4)

重要的问题是,您是想要解决有效这个问题,还是想要准确

如果你真的拥有100亿个数字并且只有一个单个副本,那么你就处于“大海捞针”状态。直观地说,没有非常肮脏和不稳定的解决方案,没有希望在不存储大量数字的情况下解决这个问题。

相反,转向概率解决方案,这已经在这个问题的大多数实际应用中使用过(在网络分析中,你要做的是寻找鼠标,即在大型数据集中很少出现的元素。)

可能的解决方案,可以找到确切的结果:使用足够高分辨率的Bloom filter。使用过滤器来确定是否已经看到一个元素,或者,如果你想要完美的准确性,使用(如kbrimington建议你使用标准哈希表)过滤器,呃,过滤掉你不可能拥有的元素看到,并在第二次通过,确定你实际看到两次的元素。

如果你的问题略有不同 - 例如,你知道你有至少0.001%的元素重复两次,你想知道有多少,或者你想获得这些元素的随机样本 - 然后在Flajolet & Martin,Alon等人的静脉中存在一整套概率流算法,并且非常有趣(更不用说高效)。

答案 2 :(得分:3)

读取文件一次,创建一个哈希表,存储您遇到每个项目的次数。可是等等!不使用项本身作为键,而是使用项iself的哈希值,例如最低有效数字,例如20个数字(1M项)。

第一次通过后,所有具有计数器>的项目1可能指向重复的项目,或者是误报。重新扫描文件,只考虑可能导致重复的项目(查找表1中的每个项目),使用实际值作为键构建新的哈希表并再次存储计数。

第二次传递后,计数>的项目第二个表中的1是您的副本。

这仍然是O(n),只是单次通过的两倍。

答案 3 :(得分:1)

怎么样:

  1. 使用一些算法对输入进行排序,该算法只允许部分输入在RAM中。例如there
  2. 在第一步的输出中寻找重复项 - 你一次只需要在RAM中输入2个元素的空间来检测重复。

答案 4 :(得分:1)

查找重复项

注意到它的32位整数意味着你将会有大量的重复,因为32位int只能代表43亿个不同的数字而且你有“10亿”。

如果您使用紧凑的集合,您可以表示是否所有可能性都是512 MB,这可以很容易地适应当前的RAM值。作为一个开头很容易让你认识到一个数字是否重复的事实。

重复计算

如果你需要知道一个数字被重复多少次,你就会得到一个只包含重复的hashmap(使用ram的前500MB来有效地告诉它是否应该在地图中)。在最糟糕的情况下,有一个很大的差价,你将无法适应ram。

如果数字具有均匀数量的重复数据,则另一种方法是使用紧密排列的数组,每个值为2-8位,占用大约1-4GB的RAM,允许您计算每个数字最多255次出现。< / p>

它将成为一个黑客,但它可行。

答案 5 :(得分:0)

你需要实现某种循环结构来一次读取一个数字,因为你不能一次将它们放在内存中。

如何?哦,你用的是哪种语言?

答案 6 :(得分:0)

你必须读取每个数字并将其存储到一个hashmap中,这样如果一个数字再次出现,它将自动被丢弃。

答案 7 :(得分:0)

如果文件中可能的数字范围不是太大,那么您可以使用一些位数组来指示范围内的某些数字是否出现。

答案 8 :(得分:0)

如果数字的范围足够小,您可以使用位字段存储(如果它在那里) - 通过文件中的单次扫描初始化该字段。每个可能的数字占一位。

对于大范围(如int),您需要每次都读取文件。文件布局可以允许更有效的查找(即,在排序数组的情况下进行二进制搜索)。

答案 9 :(得分:0)

如果时间不是问题且RAM是,您可以读取每个数字,然后通过读取文件将其与每个后续数字进行比较,而不将其存储在RAM中。这需要花费大量的时间,但你不会耗尽内存。

答案 10 :(得分:0)

我必须同意kbrimington和他对哈希表的看法,但首先,我想知道你正在寻找的数字的范围。基本上,如果您正在寻找32位数字,则需要一个4.294.967.296位的单个数组。首先将所有位设置为0,文件中的每个数字都将设置一个特定的位。如果该位已经设置,那么您已经找到了之前发生过的数字。你还需要知道它们发生的频率吗?
至少,它至少需要536.870.912字节。 (512 MB。)它很多,需要一些狡猾的编程技巧。根据您的编程语言和个人经验,将有数百种解决方案以这种方式解决。

答案 11 :(得分:0)

很久以前不得不这样做。 我做了什么......我尽可能多地对数字进行排序(有时间约束限制)并在排序时按照这样排列:

1到10,12,16,20到50,52会变成..

[1,10], 12, 16, [20,50], 52, ...

因为在我的情况下,我有数百个非常“接近”的数字($ a- $ b = 1),从几百万集我的内存使用率非常低

P.S。另一种存储方式

1, -9, 12, 16, 20, -30, 52,

当我没有低于零的数字时

之后我在减少的数据集上应用了各种算法(由其他海报描述)

答案 12 :(得分:0)

#include <stdio.h>
#include <stdlib.h>
/* Macro is overly general but I left it 'cos it's convenient */
#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
int main(void)
{
    unsigned x=0;
    size_t *seen = malloc(1<<8*sizeof(unsigned)-3);
    while (scanf("%u", &x)>0 && !BITOP(seen,x,&)) BITOP(seen,x,|=);
    if (BITOP(seen,x,&)) printf("duplicate is %u\n", x);
    else printf("no duplicate\n");
    return 0;
}

答案 13 :(得分:0)

这是一个简单的问题,可以很容易地解决(几行代码)和非常快(几分钟的执行)与正确的工具
我个人的做法是使用 MapReduce
MapReduce: Simplified Data Processing on Large Clusters

我很抱歉没有详细介绍,但是一旦熟悉了MapReduce的概念,就可以非常清楚如何定位解决方案了 基本上我们将实现两个简单的功能

  1. 地图(键,值)
  2. 减少(键,值[])
  3. 总而言之:

    • 打开文件并遍历数据
    • 每个数字
    • - &gt;地图(编号,line_index)
    • 在reduce中我们将数字作为关键字,总出现次数为值的数量(包括它们在文件中的位置)
    • 所以在Reduce(key,values [])中,如果值的数量> 1比重复数字
    • 打印重复项:number,line_index1,line_index2,...

      再次,这种方法可以导致非常快的执行,具体取决于MapReduce框架的设置方式,高度可扩展性和非常可靠性,MapReduce有许多不同的实现方式。
      有几家顶级公司展示了已经建立起来的云计算环境,如Google,Microsoft azure,Amazon AWS,...... 或者您可以构建自己的集群,并为任何提供虚拟计算环境的提供商设置集群,按小时支付非常低的成本

      祝好运 :)

      • 另一种更简单的方法可能是使用布隆过滤器

        AdamT

答案 14 :(得分:0)

实现BitArray,使得该数组的第i个索引对应于数字8 * i +1到8 *(i + 1)-1。即如果我们已经看到8 * i + 1,则第i个第一位是1。如果我们已经看到8 * i + 2,那么第i个第二位是1。

初始化此位数组,其大小为Integer.Max / 8,每当您看到数字k时,如果此位已经为1,则将k / 8索引的k%8位设置为1表示您已经看到此数字。< / p>