【问题标题】:How to code the maximum set packing algorithm?如何编写最大集打包算法?
【发布时间】:2014-03-08 17:14:32
【问题描述】:

假设我们有一个有限集 S 和一个 S 的子集列表。然后,集合打包问题询问列表中的一些 k 子集是否成对不相交。 该问题的优化版本,最大集打包,要求列表中成对不相交集的最大数量。

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

那么,让S = {1,2,3,4,5,6,7,8,9,10}

and `Sa = {1,2,3,4}`
and `Sb = {4,5,6}`
and `Sc = {5,6,7,8}`
and `Sd = {9,10}`

那么成对不相交集的最大数量为3(Sa, Sc, Sd)

我找不到任何有关所涉及算法的文章。你能解释一下吗?

我的做法:

根据大小对集合进行排序。从最小尺寸的集合开始。如果下一个集合中没有元素与当前集合相交,那么我们将集合合并并增加最大集合的计数。这听起来不错吗?有更好的想法吗?

【问题讨论】:

  • 它不会让您获得最佳效果,但可以提供一个很好的近似值
  • 你的集合都是连续的数字范围吗?
  • @ Niklas - 是的,这些数字是连续的 {1 到 30000+}
  • 对不起。我误解了你的问题。子集按升序排序,但数字可能不连续。原始集合是一个连续数字的列表
  • @user3080029:有一个简单的O(2^n * n^2) 算法。还有一个稍微复杂的O(2^n) 算法。独立集算法可以和O(1.3^n)什么的一样好。

标签: algorithm set disjoint-sets


【解决方案1】:

正如 hivert 所指出的,这个问题是 NP-hard,所以没有有效的方法来解决这个问题。但是,如果您的输入相对较小,您仍然可以将其关闭。毕竟,指数并不意味着不可能。只是随着输入大小的增长,指数问题很快变得不切实际。但是对于像 25 组这样的东西,你可以很容易地暴力破解它。

这是一种方法。假设您有 n 个子集,称为 S0S1、...等。我们可以尝试每个子集组合,然后选择一个具有最大基数的。只有 2^25 = 33554432 个选择,所以这可能足够合理。

做到这一点的一个简单方法是注意任何严格低于 2^N 的非负数都表示子集的特定选择。查看数字的二进制表示,并选择其索引对应于打开的位的集合。因此,如果数字为 11,则第 0、第 1 和第 3 位为 on,这对应于组合 [S0, S1, S3]。然后您只需验证这三个集合实际上是不相交的。

您的程序如下:

  1. 迭代 i 从 0 到 2^N - 1
  2. 对于 i 的每个值,使用打开的位来计算子集的相应组合。
  3. 如果这些子集是成对不相交的,请使用此组合更新您的最佳答案(即,如果它大于您当前的最佳答案,则使用它)。

或者,使用回溯来生成您的子集。这两种方法是等效的,模实现权衡。回溯会产生一些堆栈开销,但如果您在进行过程中检查不相交性,则可能会切断整条计算线。例如,如果 S1 和 S2 不是不相交的,那么它将永远不会打扰包含这两个的任何更大的组合,从而节省一些时间。迭代法无法通过这种方式进行自身优化,但由于位运算和紧密循环,快速高效。

这里唯一重要的是如何检查子集是否成对不相交。您也可以在这里使用各种技巧,具体取决于约束条件。

一种简单的方法是从一个空集合结构开始(从您选择的语言中选择任何您想要的),然后从每个子集中逐个添加元素。如果你碰到了一个已经在集合中的元素,那么它至少出现在两个子集中,你可以放弃这个组合。

如果原集合Sm个元素,而m比较小,可以将它们每一个映射到范围[0 , m-1] 并为每个集合使用位掩码。所以如果m ,你可以用一个Java long 来表示每个子集。打开与子集中元素对应的所有位。由于按位运算的速度,这允许极快的设置操作。按位与对应集合交集,按位或是并集。您可以通过查看交集是否为空来检查两个子集是否不相交(即,对两个位掩码进行 AND 运算会得到 0)。

如果你没有这么少的元素,你仍然可以避免多次重复设置的交集。你的集合很少,所以在开始时预先计算哪些是不相交的。您可以只存储一个布尔矩阵 D,使得 D[i][j] = true 如果 i 和 j 不相交。然后,您只需查找组合中的所有对来验证成对的不相交性,而不是进行真正的集合操作。

【讨论】:

    【解决方案2】:

    您可以通过搜索Maximum independent set 来解决集装问题。您将问题编码如下:

    • 为每个集合放置一个顶点
    • 如果两个顶点的编号相同,则在它们之间放置一条边。

    那么如果没有两个具有两个相关顶点的顶点,您就不会有一个最大的顶点集。不幸的是,这是一个 NP-Hard 问题。任何已知的算法都是指数的。

    【讨论】:

    • 我应该在每次迭代中使用蛮力方法找到最小相交集吗?我有大约 25 套
    • 25 通过蛮力可能是可行的。他们有很多包可以做到这一点(在 Sagemath 我们使用 NetworkX)。相反,我是你自己写的,我建议你找到好的数据结构来允许快速回溯。也许 Knuth Dancing Links 是一个不错的选择。
    • 广告:我刚查了一下,Sagemath 中的那个似乎很容易解决大小为 100 的随机图的问题。
    • 使用独立集问题求解。我用它解决了类似的问题。
    • @coderhacker 这错过了一些捷径的机会,请参阅cs.stackexchange.com/q/86848/83094
    猜你喜欢
    • 2012-01-25
    • 2016-10-13
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    相关资源
    最近更新 更多