【问题标题】:Maintaining a set of smallest subsets维护一组最小子集
【发布时间】:2023-03-13 23:10:01
【问题描述】:

以下是我想对一个假设的集合数据结构执行的操作,该结构将集合作为其元素:

  1. 在数据结构中插入一个集合,但是:(1) 如果新集合是任何现有集合的超集,则不要添加它 (2) 如果新集合是任何现有集合的子集,删除它们。
  2. 枚举当前集合中的所有集合

所有有问题的集合都是已知有限集合的子集,例如 {0..10^4}。

有没有办法有效地做到这一点?

【问题讨论】:

  • 什么是“高效”?你有什么具体的限制吗?
  • 如果结构中有一大堆集合,然后将所有这些集合的超集添加到结构中,会发生什么情况?它会删除旧的吗?
  • 好吧,我正在探索一种可能的约束求解技术。大致的想法是约束求解器在搜索过程中尝试学习新的约束。这样做的问题是约束数据库变得非常大而且速度很慢,因为添加了很多约束。许多约束是多余的。如果一个约束是另一个约束的超集(粗略地说),它就是多余的。我想要一个只维护最小子集的数据结构。没有固定的限制;只是越快越好,因为约束求解器将能够处理更大的问题。
  • @templatetypedef:是的,完全正确:) 我忘了在问题中提到这一点;我现在添加它。

标签: algorithm data-structures set subset


【解决方案1】:

这是关于这个问题的最新论文:http://research.google.com/pubs/pub36974.html

简而言之,在最坏的情况下,你不能比二次时间做得更好;但有一些技巧可以在实践中加快速度。

【讨论】:

  • 谢谢!这就是我一直在寻找的。​​span>
【解决方案2】:

所有有问题的集合都是已知有限集合的子集,例如 {0..10^4}。

我们称其为 N = 10^4。这是相当小的,这将证明是有用的。假设你有 S 个集合。

“逻辑上”这意味着您有一个 N*S 矩阵。

您已经拥有了一组集合。在这个一级结构中有 S 个集合。

10^4 足够小,您可以维护一个 secondary 数据结构,该数据结构为每个 N 值存储它所在的 sets 列表。这个结构有点像一级结构的转置。这可能是一个长度为 N 的向量,允许通过恒定时间查找来查找特定值所在的集合列表。

现在,当您添加一个新集合时,可以使用此二级结构来查找其每个值所在的其他集合。例如,我们添加一个值为 2,5,10 的新集合

new_set = {2,5,10}

二级结构告诉我们它们在哪些集合中:

 2 : {A,B,D}
 5 : {B,D}
10 : {B}

我们可以合并和排序这三个列表以获得ABBBDD,它不仅告诉我们它与哪些集合重叠,而且告诉我们重叠的大小。三个节点与 B 共享,这意味着我们的新集合是 B 的子集或等于 B。我们与 A 共享 1 个节点,与 D 共享两个节点。如果结果 A 的总大小为 1,那么我们现在知道 A 是我们新集合的一个子集。

【讨论】:

    【解决方案3】:

    枚举集合中的集合很容易,O(n)。然而,检查一个新的候选者是否是所有现有集合的一个子集将会有点昂贵。有众所周知的算法来测试一个集合是否是另一个集合的子集,很简单

    for each subset s in S
        for each candidate set C
            test of C is a subset of s
            if it is, break
    if never found, add C to S.
    

    这将类似于 O(n^2 lg n)。这算“高效”吗?

    【讨论】:

    • 这就是蛮力方法。我希望有更高效的东西;)
    • 我现在没有时间或空间来尝试编写证明,但我怀疑 O(n^2 lg n) 是最佳的。我不认为你可以使外部循环比 O(|S|*C 候选数) 更好,而且我认为你不能在小于 O(lg n) 的时间内测试子集。我认为一个证明随之而来的是矛盾。
    • 该论点只是表明没有明显的方法可以改进您的算法。为什么不能有完全不同的算法?
    • 似乎有更好的算法,事实上,次二次时间(参见 Falk Hüffner 的回答或搜索“An Old Sub-Quadratic Algorithm for Finding Extremal Sets”)。
    • Jules,当有人说“我怀疑但我现在不能写证明”时,意思是“但我可能错了。”
    【解决方案4】:

    为所有存储的集合维护一个布隆过滤器。为要插入的集合生成一个布隆过滤器。如果您将要插入的集合的过滤器(称为 X)与另一个集合的布隆过滤器按位与,并取回值 X,那么您要插入的集合可能是一个子集(可能是误报,您需要检查此时的缓慢方式)。否则肯定不行,你可以换一个试试。

    在构建布隆过滤器时有许多可调整的参数,可让您在空间效率和误报概率之间进行权衡。

    http://en.wikipedia.org/wiki/Bloom_filter

    【讨论】:

      【解决方案5】:

      为了节省空间,您可以使用位集来表示已知有限集的每个子集。还有一些表示稀疏位集的方法(例如,参见this Java sample),以进一步节省空间。

      整体结构可以是一组位集。在 Java 中,BitSet 没有子集测试方法,但我认为扩展 BitSet 以包含有效的子集测试方法不会太难。 (这将避免测试要添加的候选是否等于其与任何现有子集的交集这一令人讨厌的任务。)

      【讨论】:

        【解决方案6】:

        使用某种树结构。

        例如。将已排序的现有集合存储在 Trie 中。如果通向该节点的路径是现有集合,则在每个节点维护一个标志

        1 检查给定集合是否是现有集合的超集:

        def issuperset(node, set[N], setc, N):
            if node.is_set:
                return True
            for j = setc:N
                if set[j] is a child of node:
                    if issuperset(node.child[set[j]], set, j+1, N):
                        return True
            return False
        

        2 删除给定集合的所有超集

        def remsuperset(node, set[N], setc, N):
            if setc == N+1:
                remove_all_sets_on_or_below_this_node(node)
                return
            for ch in node.child:
                if ch< set[setc]:
                    remsuperset(node.child[ch], set, setc, N)
                elif ch == set[setc]:
                    remsuperset(node.child[ch], set, setc+1, N)
        

        3 枚举集合只需遍历树,打印路径为 is_set 标志为真

        【讨论】:

          猜你喜欢
          • 2020-10-09
          • 1970-01-01
          • 2017-08-24
          • 2013-06-04
          • 2022-11-13
          • 1970-01-01
          • 1970-01-01
          • 2012-08-04
          • 2011-11-13
          相关资源
          最近更新 更多