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

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

本文关键字:集合 子集 最佳 方法 一组      更新时间:2023-10-16

首先,很抱歉标题不明确。

假设我有以下一组集合:

第1组

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

第2组

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

对于Group 1中的每个集合——称之为集合s,我需要找到Group 2中的集合——称其为m——使得ms的子集。

因此,以我为例,答案是:

s1 -> m2
s2 -> nothing

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

有什么建议吗?

第一步是根据基数(即大小)对组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组"中最大集合的大小。

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

您并不清楚您的方法有多暴力。只要您在std::命名空间中使用集合查询函数,它们就可能尽可能高效。例如,测试set_interaction(s1.begin(),s2.end(),m1.begin),m1.end(())是否等价于m1。

您可以比这更高效,因为您不需要匹配元素的副本,只需要知道它们都出现了。这可以通过复制set_interaction的代码来实现,但要更改实现,只需计算匹配元素的数量,而不是将它们复制出去。那么,如果计数与m的大小相同,那么你就有了匹配项。

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

您的集合是经常修改的还是只读的?

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

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

您可以尝试这样的方法。步骤:

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