两个向量的集合交集的有效或快速大小

时间:2014-06-21 01:52:49

标签: c++ performance vector stl intersection

我发现自己需要返回两个向量交集的大小:

std::vector<int> A_, B_

我不需要相交的值,只需要集合的大小。这个功能需要被调用很多次。这是对(数学)图形/网络进行更大模拟的一部分。

我的工作条件是:

  • 容器是载体。改变它们是纯粹的痛苦,但如果获得保证,它肯定会这样做。
  • A_和B_的大小的上限为~100。但往往很多 小。
  • A_和B_的元素代表取自{1,2,...,M}的样本,其中M> 10,000。
  • 一般来说,A_和B_有相似但不相等的尺寸。
  • 两个向量都是无序的。
  • 作为“更大模拟”的一部分,A_和B_的内容会发生变化。
  • 每个向量仅包含唯一元素,即不重复。

我的第一次尝试,使用一个天真的循环,如下。但我认为这可能还不够。我假设......由于重复的排序和分配,std :: set_intersection会过于繁重。

   int vec_intersect(const std::vector<int>& A_, const std::vector<int>& B_) {

      int c_count=0;

  for(std::vector<int>::const_iterator it = A_.begin(); it != A_.end(); ++it){
     for(std::vector<int>::const_iterator itb = B_.begin(); itb != B_.end(); ++itb){

      if(*it==*itb) ++c_count;
     }
  }

  return c_count;
}

鉴于我的上述条件,我怎样才能实现这一目标以获得速度,相对容易?我应该考虑哈希表还是使用排序和STL,或不同的容器?

2 个答案:

答案 0 :(得分:14)

你的算法在元素数量上是O(n 2 )(假设两个向量的大小大约等于n)。这是一个O(n)算法:

  • 创建std::unordered_set<int>
  • 将向量A的所有项目放入集合
  • 浏览向量B的所有项目,检查它们是否存在于unordered_set中,并递增每个项目的计数。
  • 返回最终计数。

这是C ++ 11中的一个实现,为简洁起见使用lambda:

vector<int> a {2, 3, 5, 7, 11, 13};
vector<int> b {1, 3, 5, 7, 9, 11};
unordered_set<int> s(a.begin(), a.end());
int res = count_if(b.begin(), b.end(), [&](int k) {return s.find(k) != s.end();});
// Lambda above captures the set by reference. count_if passes each element of b
// to the lambda. The lambda returns true if there is a match, and false otherwise.

(这会打印4; demo

答案 1 :(得分:3)

你的算法是O(n * m),其中n和m是向量中元素的数量。

如果您没有输入数据不受信任的问题,您可能会获得最佳效果:

  • A的所有元素放入unordered_set
  • 对于B中的每个元素,如果它在集合中,则递增计数器。

例如:

int vec_intersect(const std::vector<int>& A_, const std::vector<int>& B_)
{
    std::unordered_set<int> aSet(A_.cbegin(), A_.cend());
    return std::count_if(B_.cbegin(), B_.cend(), [&](int element) {
        return aSet.find(element) != aSet.end();
        });
}

这将概率地给出O(m + n)个结果。 (哈希表几乎总是O(1),但如果攻击者可以强制表中的许多冲突,他们可能会强制O(n)行为,导致拒绝服务)

如果您需要确定性结果,并且向量的顺序无关紧要,则排序一个向量将起作用,其仅为O(m lg m + m + n)。那就是:

  • 排序第一个向量
  • 对于第二个向量中的每个元素,使用二进制搜索来确定元素是否在第一个向量中,如果是,则递增计数器。

例如:

int vec_intersect(std::vector<int>& A_, const std::vector<int>& B_)
{
    std::sort(A_.begin(), A_.end());
    return std::count_if(B_.cbegin(), B_.cend(), [&](int element) {
        return std::binary_search(A_.cbegin(), A_.cend(), element);
        });
}

只是为了咯咯笑,这里有一个<algorithm>版本的算法:

int vec_intersect(const std::vector<int>& A_, const std::vector<int>& B_)
{
    return std::count_if(B_.cbegin(), B_.cend(), [&](int element) {
        return std::find(A_.cbegin(), A_.cend(), element) != A_.cend();
        });
}