我有一个与 子集总和问题 并且想知道差异是否使它变得更容易,即可以在合理的时间内解决。

给定一个值v,一个设定的大小为l和一个数字[1,n] s的序列,s总数的尺寸l子集至小于v?

这与三个方面的子集总和问题不同:

  1. 我关心多少个子集 较少的 比给定值,而不是多少 平等的.
  2. 子集尺寸是固定的。
  3. 我在乎 多少 设置总和小于V,而不仅仅是存在。

是否有任何合理有效的算法可以解决此问题?

编辑:显然,可以使用组合生成算法在O(n选择L)中完成。我真正感兴趣的是聪明的骇客,可以显着加快它的速度。

有帮助吗?

解决方案

(决策版本的)您的问题仍然是NP完成的。这个想法是,如果我们可以解决您的问题,那么(对于每个子集大小,说)我们可以问多少套件总和小于v,以及多少总和比V-1小于V-1,而这两个数字的差异将告诉我们是否是确切到V的子集 - 因此我们可以解决子集总和问题。 [这不是一个完整的证据,因为它是 简化图灵, ,不是 许多减少.]

但是,有一个简单的 动态编程 时间O(NLV)运行的解决方案。 [这没有证明p = np的原因是v可以在输入大小中指数:n位,您可以将值表示为2n. 。但是,假设您的V不是指数级,这不是问题。]

令num [v] [k] [i]表示s的第一个i元素的大小为k子集的数量,然后将它们计算为(对于每个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]是您序列中的ITH元素。 (任何总结到V的尺寸K都不使用s [i],因此在num [v] [k] [i-1]中计数,或者使用s [i],这意味着其余部分该子集具有K-1元素,仅使用序列中的第一个I-1号,然后总和到vs [i]。 ;那是你的答案。

另外,如果您仔细地进行操作,则可以省略第三个下标(每个I等都向下运行循环);为了清楚起见,我只包含它。

其他提示

我不准备提供证据,但这听起来可能适合动态编程方案:将大小2的子集的列表列为尺寸3的计算机子集,因此Hyou只需要检查一个少量潜在客户。

想到的一个优化是:订购序列(如果不是这样)。从其开始时选择第一个L-1项目,然后选择最后一个项目,它是最大的值(序列中的下一个最大值将使总和太大)。丢弃序列的其余部分,因为这些项目无论如何都不能成为有效子集的一部分。

之后,我想这又是完整的搜索。但是话又说回来可能还可能进行其他优化。

子集总和问题的动态编程解决方案生成了一个包含此答案的表(即v的布尔值v,其中v是最大元素的数量,n是可以在满足的集合中的最大项目数约束;如果<= n元素总和为<= v),则每个布尔值为true。因此,如果N * V对您来说不太大,则存在一种可接受的快速算法。子集总和解决方案只是该表中元素数为<= n/2的最高集元素。

如果只是正整数,您可以执行验证步骤 如果你需要;

以集合中的L-1最小整数的总和。如果是总和x,则如果问题是 应该 有解决方案。想一想,您可以以这种方式消除其他L ...

好吧,一方面,因为您要指定尺寸= l,即使您无法想到任何聪明的东西,并且只需在最坏的情况下使用(n选择L)单独的总和,所以要好一些比n ^^ l(嗯,l+1,然后将每个子集总结)。

这听起来像是n选择问题类别的问题。 SKIENA的算法设计手册涵盖了N的K-Subsets,该书建议按词典顺序列举相关子集(例如,递归地)。然后对每个子集进行总和和比较。

如果您有分类的集合,则可能会从解决方案空间中修剪不可能的解决方案。

也许动态的编程公式是在FPTA的PTA上散布的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top