我遇到了与subset sum problem相关的问题,我想知道差异是否更容易,即在合理的时间内可以解决。
给定值V,设定大小L和数字序列[1,N] S,S总和的L个子集的总和小于V?
这与子集和问题有三种不同:
有没有合理有效的算法来解决这个问题?
编辑: 显然,这可以使用组合生成算法在O(N选择L)中完成。我真正感兴趣的是聪明的黑客,以便大大加快它的速度。
答案 0 :(得分:19)
(决定版本)您的问题仍然是NP完整的。我们的想法是,如果我们可以解决你的问题,那么(对于每个子集大小,比方说),我们可以询问有多少集合总和小于V以及多少总和小于V-1,并且这两个数字的差异将是告诉我们是否是精确到V的子集 - 因此我们可以解决子集和问题。 [这不是一个完整的证明,因为它是Turing reduction,而不是many one reduction。]
但是,有一个简单的动态编程解决方案,它在时间O(nLV)内运行。 [这不能证明P = NP的原因是V在输入大小中可能是指数的:对于n位,您可以表示高达2 n 的值。但假设你的V不是指数,这不是问题。]
令num [v] [k] [i]表示S的前i个元素的大小k子集的数量,它们总和为v。您可以将它们计算为(对于每个i):
num[0][0][i] = 1
for v = 1 to V:
for k = 1 to L:
num[v][k][i] = num[v][k][i-1] + num[v-S[i]][k-1][i-1]
其中S [i]是序列中的第i个元素。 (任何与v相加的大小k的集合都不使用S [i],因此它在num [v] [k] [i-1]中计数,或者它使用S [i],这意味着其余的子集具有k-1个元素,仅使用序列中的第一个i-1个数,并求和为vS [i]。)最后,对于每个小于V的v计数num [v] [L] [| S |] ;这是你的答案。
此外,如果你仔细地做,你可以省略第三个下标(为每个i向下运行你的循环,等等);为了清楚起见,我只包括它。
答案 1 :(得分:2)
我不准备提供证明,但听起来它可能适合动态编程方案:将大小为2的子集列表用于大小为3的计算机子集,以便hyou只需要检查一小部分潜在客户。
答案 2 :(得分:1)
我想到的一个优化是:订购你的序列(如果它不是这样的话)。从头开始选择第一个L-1项,然后选择最后一项,即它是最大可能值(序列中的下一个最大值会给出太大的总和)。丢弃序列的其余部分,因为这些项目永远不会成为有效子集的一部分。
之后我猜它再次完全搜索。但话说可能还有其他优化措施。
答案 3 :(得分:1)
子集和问题的动态编程解决方案生成一个包含此答案的表(即V乘以N的布尔表,其中V是元素的最大数量,N是可以在集合中的最大项目数满足约束;如果< = N个元素总和为< = V),则每个布尔值为真。因此,如果N * V对您来说不是太大,则存在可接受的快速算法。子集求和解是该表中的最高集元素,其元素数为< = N / 2.
答案 4 :(得分:1)
如果只有正整数,如果需要,可以执行验证步骤;
取该集合中L-1个最小整数的总和。如果这是一个和X,那么如果问题假设有一个解决方案,则n-X必须低于最大元素。想想看,你可以用这种方式消除其他的......
答案 5 :(得分:0)
嗯,有一件事,因为你指定size = L然后即使你不能想到任何聪明的东西,只是使用蛮力,你会在最坏的情况下(N选择L)单独的总和,所以它是比n ^^ L好一点(好吧,L + 1,因为你然后对每个子集求和)。
答案 6 :(得分:0)
这听起来像是一个选择k类问题。生成k的k子集在Skiena的算法设计手册中有所涉及,本书建议按字典顺序列举相关的子集(例如,递归地)。然后对每个子集进行求和和比较。
如果你有一个排序集,你可能会从解决方案领域修剪不可能的解决方案。
答案 7 :(得分:0)
动态编程公式也许是对FPTAS的PTAS的一个补充。