子集和变化:获取尽可能多的子集总和

时间:2017-01-01 14:35:20

标签: algorithm optimization sum subset greedy

对于碎片整理算法,我需要解决以下问题: 给定正整数的集合,提取尽可能多的子集,总和为给定值。集合中的每个项目只能出现在一个子集中。

贪婪算法(迭代正常子集和)不起作用,反例:

collection: 3 5 8 4 5 1 1 1 1 1, targeted sum: 10
1. subset: 5 1 1 1 1 1
there is no other subset

but:
1. subset: 8 1 1
2. subset: 5 4 1
3. subset: 3 5 1 1

正如您所看到的,如果我选择以前的子集很差,那么解决方案并不是最佳的。 我该如何解决这个问题?我已经实现了“正常”子集和。

编辑:解决方案可能是先使用小子集吗?此外,这个问题与如何找到可能的子集数量的问题没有重复。我希望找到尽可能多的不相交的子集。

谢谢,菲尔

4 个答案:

答案 0 :(得分:2)

如果您允许回溯(即,能够返回并撤消之前的决定),那么您可以搜索整个可能性空间并确保找到您的解决方案。不是最有效的,但它有效。

或者你可以建立在@Druid的评论上:找到所有可能的子集,并搜索这些子集以找到你的分区,允许重复子集。您可以添加想要在较大子集之前尝试较小子集的启发式(为将来的选择留出更多灵活性)。

答案 1 :(得分:2)

这个问题可能强NP-hard *,所以让我勾画一个O(2 ^ n * n)时间算法。

让输入为多集U并让目标和为s。想象一下在与U的子集相对应的2 ^ n个顶点上的图形。当且仅当(1)Y = X - {x}对于X中的某些x时,存在从子集X到子集Y的弧,以及(2) (sum(U - X)mod s)+x≤s,即我们不会溢出当前分区。从U开始遍历此图并报告具有最小总和的子集的路径。

*我的减少显示硬度来自一个名为3-dimensional matching的问题。假设匹配实例具有n个顶点,则找到n个数,使得所有三元组具有不同的和。应用概率方法,如果我们从1..n ^ 6随机均匀地选择所有n,则成功的概率大于11/12(一减(n选3)选择2三倍时< 1 / n ^ 6失败每个的概率),验证需要时间O(n ^ 3 log n)。从技术上讲,我们必须去随机化;见my question on math.SE

要准备此问题的输入,对于与数字x关联的每个顶点,输出n ^ 12 + x。对于可以匹配的每个三重x,y,z,输出n ^ 9 - x - y - z作为数字。目标是s = 3 n ^ 12 + n ^ 9。一些繁琐的数学应该表明,目标总和的唯一子集对应于三维匹配的三元组。

答案 2 :(得分:0)

对于问题的复杂性,它是强烈意义上的NPC。这是3-Partition问题的后续跟进:

  

给定n = 3 m正整数的多集S,可以对S进行分区   进入m三元组S 1 ,S 2 ,...,S m ,使得每个数字的总和   子集是否相等?子集S 1 ,S 2 ,...,S m 必须形成S的分区   在某种意义上说,他们是不相交的,他们涵盖了S.

我们需要来自三个分区问题的一个额外信息(这是其硬度证明的直接结果):

  

设B表示每个子集S i 的(期望的)和,或等效地,让S中的数字的总和为m B.当3时,3分区问题仍然是NP完全的S中的每个整数都严格地在B / 4和B / 2之间。

对于您的问题,我们可以假设输入由 3m 元素组成,给定值为 B ,并且对于每个元素a∈S< / em>我们有 B / 4&lt; a&lt; B / 2 。然后我们可以找到 m 集合,使得每个集合都有总和 B 当且仅当我们能够解决这三个分区时。 (请注意,范围约束会自动强制在每个集合中使用 3 元素)。

答案 3 :(得分:0)

我创建了一个递归过程,该过程查找所有子集和。它主动确定集合中的数字太大或太小而无法成为父节点。我用Nodejs编写了一个运行良好的程序。只需重新访问它即可进行优化和重构代码。

对于一个全为正数的集合,可以对我的代码进行许多启发式和优化。

您可以在此处阅读有关我手工进行的数学运算的信息: https://medium.com/@clint.mulligan/innovative-solution-to-the-subset-sum-problem-10a19f87056b

以及此处的代码链接: https://github.com/ClintMulligan/subset-sum-finder

实质上,该集合已排序。第一个数字被视为包含在常规树中作为父节点。相关数据复制该数字所属的整数集,即正数或负数。

太大而不能成为父节点| n | > |目标总和-(相对组)| 并且太小是(同一组)<目标总和

如果其中任何一个为真,则考虑下一个数字。如果它们都为假,则将Number作为节点移动,并创建一个新的目标总和减去Sub Number的子问题,并创建其余的集合。直到每个子问题都以解决方案或死胡同结束为止。

我希望通过一些优化和进一步的研究来消除最死角。哪个还不错?