快速检查子集是否包含给定的子集列表的方法

时间:2015-09-09 08:57:20

标签: c++ algorithm stl complexity-theory

我的问题如下

我有一套K元素

此集合的每个子集由std :: bitset的实例表示(位i为真=子集中有元素i)

我有一个输入子集I和一个子集列表S1 ... Sn

我想从S1 ... Sn中返回项目,这样Si就包含在I中。(也就是说,每次Si有一个真实的时候,它在I中也必须是真的)

显然,这可以在K * n中完成,通过对每个S子集进行独立的相同检查。

但是,有没有通用的方法可以做得更好?我很确定它是可能的,因为在我的情况下,子集列表S1 ... Sn总是相同的并且可以进行预处理。 我确定可以将子集存储在特定的数据结构中(树?trie?),这样我就可以一次性丢弃大量的子目录等等。

example :
K = 5

I = [1,1,0,1,0]

S1 = [1,0,0,0,0]
S2 = [1,1,0,1,0]
S3 = [1,1,1,0,0]

the ouput should return S1,S2 (not S3!)

我有一个常量集S1,S2,...,Sn,并在同一集合上运行I的不同查询。

编辑: 我所谈论的例子: 例如,如果S1中包含S1:检查I中是否包含S1:如果不包含,则不能将S2包括在内(不需要检查) 如果S3是S1和S2的并集:如果S1和S2包含在I中,那么S3

也是如此

3 个答案:

答案 0 :(得分:1)

您可以使用inverted index方法。虽然它不能改善最坏情况的性能,但它可能会加速平均情况,特别是对于相对密集的查询向量。

对于每个j = 1,2,...,k创建一个排序列表,如果jS_i中,则每个子集都在此列表中。在预处理中仅创建一次。

在您的示例中,它将类似于:

0 -> [S1,S2,S3]
1 -> [S2,S3]
2 -> [S3]
3 -> [S2]
4 -> []

现在,给定一个查询I,找到包含" down"之一的所有集合。 I的位。这与信息检索中的OR查询相同。此查询的答案是结果中没有的子集。其余的都是。

在您的示例中,查询为2 OR 4,查询倒排索引时的结果为:S3,因此结果为S1,S2。

这基本上就是搜索引擎所做的事情,如果查询与可能性的数量相比包含的条款非常少,则效率非常高。

答案 1 :(得分:1)

以部分答案回答我的问题:

  1. 从S1 ... Sn我们构建了一个子集树,这样根节点就是空子集(bitset中都是0),并且每个子节点都包含它的父子集
  2. 对于算法,从根开始:
    • 为每个孩子:
      • 如果此节点中的子集包含在I中,请添加此子集并以此节点作为根再次调用算法
      • 否则,转到下一个孩子(从未处理过这个孩子的子树)
  3. 现在的问题是,如何从1)最佳地构建树?即具有最大深度和最小“宽度”的 例如,在我的例子中,“坏”树将是S1,S2和S3是来自根节点的子节点。 “好”树将是根节点仅具有针对子节点的S1,并且以S1为根的树具有S2且S3为子节点。 我不知道如何构建这棵树

答案 2 :(得分:1)

构建一个包含所有T的二叉树S1...Sn,其中每个级别k都有两个子节点,具体取决于S是否有01位置k。树的叶子都是你的S1...Sn

给定输入子集I,我们取Ik(位置k中的元素):如果Ik==0,则选择T的子树KIk==1 1}}对应于0.如果T您在级别K选择了O(n+k)的子树。在T上以这种方式进行,直到你到达所有的树叶。

在最糟糕的情况下,您对给定的I进行S1...Sn次操作。

由于T不会改变,因此构建树T是一次操作。

编辑:我的答案很仓促。树n有超过2^k=m个叶子,它有S1...Sn个叶子。但是我们可以删除不在O(2^k)中的叶子和死亡的子树。这将成本分析带到m,但实际上我们将拥有更少的节点。现在,分析变得更难,如果值得,取决于nS之间的比率;

我提出了一种不同的分析方法:认为在k级我们在常量时间内将所有子集k丢弃为O(n)级别的无效位,但我们必须在{{1}中这样做每个级别的子树。由于此操作重复k次,因此最高费用为O(kn),但实际上平均费用较低。