从一组集合中查找集合子集的最佳方法

Best way to find a subset of a set from a group of sets

提问人:Luchian Grigore 提问时间:2/16/2012 最后编辑:Aman AggarwalLuchian Grigore 更新时间:2/17/2012 访问量:2296

问:

首先,对不起,标题模棱两可。

假设我有以下一组集合:

第 1 组

s1 = ( x1, y1 )
s2 = ( x2 )

第 2 组

m1 = ( x1, y1, y2 )
m2 = ( x1 )
m3 = ( x1 , x2 )

对于 - 调用集合中的每个集合,我需要找到 - 调用它 - 中的集合,以便它是 的子集。Group 1sGroup 2mms

所以,对于我的例子,答案是:

s1 -> m2
s2 -> nothing

目前,我将值存储在 中,但如果需要,我可以更改它。此外,集合可能会变大,因此算法需要高效。目前,我有一种蛮力方法,我并不完全满意。std:set

有什么建议吗?

C++ 算法 子集

评论

0赞 crush 2/16/2012
所以,在您的示例中,它应该只返回 m2,它是 s1 的子集?
0赞 Luchian Grigore 2/16/2012
@crush是的,我已经为我的示例发布了解决方案。
0赞 Patrick87 2/16/2012
我不确定我是否理解。你是说 m1 是 s1 的子集,还是相反?似乎 s1 是 m1 的子集。但是 s2 是 m3 的子集,所以你不会有 s2 -> m3 吗?对不起,如果我误解了这一点。
0赞 Luchian Grigore 2/16/2012
@Patrick87对不起,我的错误。它应该是 m2,而不是 m1。纠正。
0赞 crush 2/16/2012
您可以使用排序列表和二进制搜索吗?

答:

0赞 Matt T 2/16/2012 #1

你没有具体说明你的方法有多暴力。只要您在 std:: 命名空间中使用 set 查询函数,那么它们就可能尽可能高效。 例如,测试 set_intersection( s1.begin(), s2.end(), m1.begin(), m1.end() ) 是否等同于 m1。

你可以比这更有效率,因为你不想要匹配元素的副本,只是知道它们都出现了。这可以通过复制 set_intersection 的代码来完成,但将实现更改为简单地计算匹配元素的数量,而不是将它们复制出来。然后,如果计数与 m 的大小相同,则您有一个匹配项。

至于容器,对于大型收藏,我通常更喜欢排序的 deque 而不是集合。内存在堆上的分布要少得多,这有助于缓存。它还避免了底层树的开销。当容器创建一次,但被多次搜索时,这尤其有用。

1赞 mcmcc 2/16/2012 #2

第一步是根据基数(即大小)对第 1 组进行排序。然后算法是以下顺序的:

foreach std::set M in "Group 2" {
  foreach std::set S in "Group 1" and S.size()>=M.size() {  // replace with binary search
     if ( std::includes(S.begin(),S.end(),M.begin(),M.end()) )
       { /* M is a subset of S */ }
    }
  }
}

这应该具有时间复杂度~O(MSR),其中M是“组2”中集合的#,S是“组1”中集合的#,R是“组#1”中最大集合的大小。

编辑:我只是想到,使用而不是调用(按顺序迭代)可能更有效,但我认为只有当 M.size() 比 S.size() 小得多时才是正确的——O(M+S) 与 O(MlogS)。S.find()std::includes()

评论

1赞 mcmcc 2/16/2012
如果你要对我投反对票,那很好,但至少要给出一些这样做的理由。我写的错了吗?
1赞 Branko Dimitrijevic 2/16/2012
嗯。。。我们在这里受到一连串的路过投票者的折磨。有些人就是拒绝玩得好:|
0赞 Mark Wilkins 2/16/2012
@BrankoDimitrijevic:我曾经回答过一个问题。大多数答案都被否决了,没有任何解释......两次。看起来有点奇怪,但大多数人都玩得很好。这是一个很好的社区。
0赞 Branko Dimitrijevic 2/16/2012 #3

您的集合是经常修改还是只读/大部分?

  • 如果经常修改,则在修改和排序性能之间取得微妙的平衡。std::set
  • 如果是只读或只读,则可以使用排序的 .排序很昂贵,但实际上比在 中构建一整棵树要便宜,因此如果您很少这样做,性能会更好。std::vectorstd::set

制作排序容器(无论是“自动排序”还是手动排序)后,可以使用 测试子集。顺便说一句,如果你需要找到合适的子集,你可以在之后比较元素计数。std::setstd::vectorstd::includes

评论

0赞 mcmcc 2/16/2012
我什至不知道std::includes()的存在。我想我现在需要修改我的回答......
0赞 D. Cannone 2/17/2012 #4

你可以尝试这样的事情。 步骤:

  • 创建一个包含两个组中所有对象的数组
  • 转换位数组中的每一个 S 和 M,其中 array(i)=1 如果集合包含 object(i),则为 0,否则为 0
  • m(k) 是 s(j) 的子集,如果 m(k) AND s(j) = m(k)