什么是在两组之间找到重复的有效方法?

时间:2013-04-14 19:09:46

标签: algorithm set

我设置了A和B

设置A有1亿个数字(每个数字是64位)

设置B有1亿个数字(每个数字是64位)

两套相同的尺寸。

数据全部是随机的,没有排序。

您建议使用哪种算法来查找两组之间的重复数字?

(我可以利用大约4G内存和100~200 Gb的磁盘空间)

提前谢谢。

4 个答案:

答案 0 :(得分:2)

假设首先arr是arr1,第二个是arr2;

sort arr1//max O(n*log_n)
for(int i = 0; i < arr2.length; i++){ //n
    binarySearch(arr1, arr2[i]) //log_n
}

总计O(n logn)

答案 1 :(得分:2)

执行时间方面可能最便宜(但不幸的是,编程时间)是将A中的元素放入open-addressed hash table,然后在哈希表中查看B的每个元素。如果你能提出一个合理的散列函数(下面更多),那么你可以使用简单的线性散列,加载因子约为60%,这意味着你的表将占用10 8 *(1 /。 6)* 8字节,或约1.3 GB。 (我不知道在标准库中提供开放地址哈希表的语言; C ++的unordered_sets是用存储桶链实现的,如果单个元素不是单独的存储分配,那只会略微增加开销。一个好的分配器可能会使这个可行。)

幸运的是,开放式地址线性探测哈希表非常容易编写,特别是如果您不需要处理删除元素。您只有两个问题:

  1. 您需要保留一些意味着“无人居住”的值。

  2. 您需要一个好的哈希函数。或者至少是合理的。

  3. 如果您的数据真正随机分布在64位空间中,则散列很简单;您只需将数据减少到所需的大小。一个简单的方法是使用模运算符,即使数据不是完全随机分布也应该很好,只要你安排使表大小成为一个素数(166666783大约适合60%负载大小)有1亿元素的因素)。

    找到一个意味着“无人居住”的值可能会更棘手。您可能已经知道一个值是不可能的(可能是值0)。如果没有,你可以选择一个随机的64位数字;在你的数据集中没有它的可能性非常好,但是如果你有一个简单的备份:不要把它放到哈希表中,并检查B的每一个值。

    Pseudo-C ++ - 代码,基于上面的描述,包括提到的“无价值”黑客:

    class HundredMillionSet {
      std::vector<uint64_t> h_;
      const size_t size_
      const uint64_t no_value_;
      bool has_no_value_;
      HundredMillionSet(size_t size, uint64_t no_value)
          : h_(size, no_value), size_(size), no_value_(no_value), has_no_value_(false) {}
    
      void insert(uint64_t v) {
        if (v == no_value_) { has_no_value_ = true; }
        else {
          size_t i = v % size_;
          while (h_[i] != no_value_) {
            if (++i == size_) i = 0;
          }
          h_[i] = v;
        }
      }
    
      bool check(uint64_t v) {
        if (v == no_value_) return has_no_value_;
        size_t i = v % size_;
        while (h_[i] != v && h_[i] != no_value_) {
          if (++i == size_) i = 0;
        }
        return h_[i] == v;
      }
    };
    

答案 2 :(得分:0)

64bit = 8byte。 2 * 8 * 100,000,000byte = 1.6GB =&gt;你可以在RAM中保存你的数字(你可能需要两倍的节点结构...)。我会选择平衡二叉树(只搜索AVL,AB ...树木的维基)。只需将一组中的数字添加到一棵树,从另一组添加到另一棵树,然后进行交叉。

您可能只想对两个数组进行排序并将它们相交。这应该是最简单的解决方案。

如果您无法处理内存中的所有数字,请使用数据库(MySQL,PostgreSQL ...)。两个排序的表和交叉点。它应该非常快,最重要的是易于实现。

答案 3 :(得分:0)

由于您的整个数据集(很容易)适合您的RAM,因此您无需在订购时做任何聪明的事情,也不需要使用磁盘空间(除了首先加载数据)。

我假设每个元素在每个列表中最多只能存在一次。

哑(蛮力)方法,O(n ^ 2):

foreach a in A (this could be as you read it from disk)
    foreach b in B
        if a is b
            increase count
            break out of inner loop

预先排序的方法:(2 * n * log(n)+ n),所以O(n * log(n))

sort A
sort B
B_ind = 0
foreach a in A
    foreach b in B from B_ind
        if a is b
            increase count
            B_ind = current b index + 1
            break out of inner loop
        else if b > a
            B_ind = current b index
            break out of inner loop

我推荐前者,但需要并行处理。很容易将外部循环拆分成块以在处理器/工作站之间进行分割。后者也可以在某种程度上并行化(同时进行两种排序)但远不及那么多。

同样在前者的情况下,通过将b循环拆分为缓存大小的块,可以获得一些性能提升。即检查A [0]对B [0 ... 1023](如果你的缓存可容纳1024项),然后检查A [1],... A [final],然后检查A [0]对B [1024] ...... [2047]等等。