具有固定子集大小的Sum子集

时间:2012-01-18 19:56:58

标签: algorithm language-agnostic np

sum-subset problem州:

  

给定一组整数,是否有一个非空子集,其总和为零?

这个问题一般是NP完全的。我很好奇这个轻微变种的复杂性是否已知:

  

给定一组整数,是否有一个大小为k的子集,其总和为零?

例如,如果k = 1,您可以进行二分查找以在O(log n)中找到答案。如果k = 2,则可以将其降至O(n log n)(例如,请参阅Find a pair of elements from an array whose sum equals a given number)。如果k = 3,则您可以执行O(n^2)(例如,请参阅Finding three elements in an array whose sum is closest to a given number)。

  

是否存在可以作为k的函数处理此问题的已知边界?

作为动机,我正在考虑这个问题How do you partition an array into 2 parts such that the two parts have equal average?,并试图确定它是否实际上是NP完全的。答案在于是否存在如上所述的公式。

除了一般解决方案之外,我对了解k=4的最佳界限非常感兴趣。

6 个答案:

答案 0 :(得分:14)

对于k = 4,空间复杂度O(n),时间复杂度O(n 2 * log(n))

对数组进行排序。从2个最小元素和2个最大元素开始,计算非递减顺序中2个元素lesser的所有(a[i] + a[j])总和以及非元素中{2}个元素greater的所有(a[k] + a[l])总和 - 增加订单。如果总和小于零,则增加lesser和,如果总和大于零,则减少greater,当总和为零(成功)或a[i] + a[j] > a[k] + a[l](失败)时停止。 / p>

诀窍是以这种方式遍历所有索引ij(a[i] + a[j])永远不会减少。对于kl(a[k] + a[l])永远不会增加。优先级队列有助于执行此操作:

  1. key=(a[i] + a[j]), value=(i = 0, j = 1)置于优先级队列。
  2. 从优先级队列中弹出(sum, i, j)
  3. 在上述算法中使用sum
  4. 只有在尚未使用这些元素的情况下,才将(a[i+1] + a[j]), i+1, j(a[i] + a[j+1]), i, j+1置于优先级队列中。要跟踪已使用的元素,请为每个'i'维护一个最大使用'j'的数组。只使用“j”的值比“i”更好。
  5. 从第2步继续。
  6. 对于k> 4

    如果空间复杂度仅限于O(n),我找不到更好的东西,比使用k-4值的强力和剩余的4值的上述算法。时间复杂度O(n (k-2) * log(n))。

    对于非常大的k integer linear programming可能会有所改善。

    <强>更新

    如果n非常大(与最大整数值的顺序相同),则可以实现O(1)优先级队列,从而将复杂性提高到O(n 2 )和O(n (k-2))。

    如果n >= k * INT_MAX,则可以使用具有O(n)空间复杂度的不同算法。预先计算k/2值的所有可能总和的位集。并使用它来检查其他k/2值的总和。时间复杂度为O(n (ceil(k / 2)))。

答案 1 :(得分:4)

在W + X + Y + Z = {w + x + y + z |中确定0是否为0的问题W中的w,X中的x,Y中的y,Z中的z}基本上是相同的,除了没有恼人的退化情况(即,问题可以用最少的资源进行互换)。

这个问题(因此k = 4的原始问题)具有O(n ^ 2 log n) - 时间,O(n) - 空间算法。 k = 2的O(n log n)时间算法(确定A + B中是否为0)按排序顺序访问A,B按反向排序顺序访问。因此,我们所需要的是A = W + X的O(n)空间迭代器,它可以对称地重复使用B = Y + Z.令W = {w1,...,wn}按排序顺序。对于X中的所有x,将键值项(w1 + x,(1,x))插入优先级队列。重复删除min元素(wi + x,(i,x))并插入(wi + 1 + x,(i + 1,x))。

答案 2 :(得分:2)

非常相似的问题:

Is this variant of the subset sum problem easier to solve?

它仍然是NP完整的。

如果不是,则子集和也将在P中,因为它可以表示为F(1) | F(2) | ... F(n),其中F是您的函数。这将是O(O(F(1)) + O(F(2)) + O(F(n)))仍然是多项式,这是不正确的,因为我们知道它是NP完全的。

请注意,如果输入上有某些边界,则可以实现多项式时间。

另请注意,可以使用二项式系数计算蛮力运行时间。

答案 3 :(得分:2)

O(n ^ 2log(n))中k = 4的解

步骤1:计算成对总和并对列表进行排序。有n(n-1)/ 2个和。因此复杂度为O(n ^ 2log(n))。保留个人的身份。

步骤2:对于上面列表中的每个元素,搜索补全并确保它们不共享“个体”。有n ^ 2个搜索,每个搜索都有复杂度O(log(n))

编辑:原始算法的空间复杂度为O(n ^ 2)。通过模拟虚拟2D矩阵(O(n),如果考虑空间来存储数组的排序版本),可以将空间复杂度降低到O(1)。

首先关于2D矩阵:对数字进行排序并使用成对总和创建矩阵X.现在,矩阵的方式是对所有行和列进行排序。要在此矩阵中搜索值,请搜索对角线上的数字。如果数字介于X [i,i]和X [i + 1,i + 1]之间,则基本上可以将搜索空间减半到矩阵X [i:N,0:i]和X [0:i ,我:N]。由此产生的搜索算法是O(log ^ 2n)(我不是很确定。可以检查一下吗?)。

现在,不使用真实矩阵,而是使用虚拟矩阵,根据需要计算X [i,j],而不是预先计算它们。

产生的时间复杂度:O((nlogn)^ 2)。

PS:在以下链接中,它表示2D排序矩阵搜索的复杂性是O(n)复杂度。如果确实如此(即O(log ^ 2n)不正确),则最终复杂度为O(n ^ 3)。

答案 4 :(得分:1)

时间复杂度非常简单O(n^k)(来自k元素的n个大小的子集数。)

由于k是一个给定的常量,一个(可能是相当高阶的)多项式上限将复杂性限制为n的函数。

答案 5 :(得分:1)

以awesomo的答案为基础...如果我们可以假设数字被排序,我们可以比给定k的O(n ^ k)做得更好;简单地取所有大小为(k-1)的O(n ^(k-1)个子集,然后在剩余的数字中进行二元搜索,当加到第一个(k-1)时,给出目标。这是O(n ^(k-1)log n)。这意味着复杂性肯定不到那个。

事实上,如果我们知道k = 3的复杂度为O(n ^ 2),我们可以做得更好k> 3:选择所有(k-3)子集,其中有O(n ^(k-3)),然后在剩余元素上解决O(n ^ 2)中的问题。对于k> = 3,这是O(n ^(k-1))。

然而,也许你可以做得更好?我会想到这个。

编辑:我最初会添加很多建议对这个问题采取不同的看法,但我决定发布一个删节版本。我鼓励其他海报看看他们是否认为这个想法有任何优点。分析很艰难,但它可能只是疯狂到可以工作。

我们可以使用这样一个事实,即我们有一个固定的k,并且奇数和偶数的和在某些方面表现,以定义一个递归算法来解决这个问题。

首先,修改问题,使你在列表中同时包含偶数和奇数(如果全部是偶数,则可以通过除以2来实现,或者通过从数字中减去1来实现,如果所有都是奇数则从目标和减去k ,并在必要时重复。)

接下来,使用甚至仅通过使用偶数个奇数来达到目标​​和的事实,并且可以仅使用奇数个奇数来达到奇数目标和。生成奇数的适当子集,并使用偶数递归调用算法,总和减去要检查的奇数子集的总和,并且k减去奇数子集的大小。当k = 1时,进行二分搜索。如果k> n(不确定会发生这种情况),返回false。

如果您的奇数很少,这可以让您快速获取必须属于获胜子集的条款,或者丢弃不能获得的条款。通过使用减法技巧,您可以将具有大量偶数的问题转换为具有大量奇数的等效问题。因此,最坏的情况必须是偶数和奇数的数量非常相似......而这就是我现在所处的位置。一个无用松散的上限是比蛮力更糟糕的许多数量级,但我觉得这可能至少和蛮力一样好。欢迎思考!

EDIT2:以上的一个例子,为了说明。

{1, 2, 2, 6, 7, 7, 20}, k = 3, sum = 20.
Subset {}:
 {2, 2, 6, 20}, k = 3, sum = 20
 = {1, 1, 3, 10}, k = 3, sum = 10
 Subset {}:
  {10}, k = 3, sum = 10
  Failure
 Subset {1, 1}:
  {10}, k = 1, sum = 8
  Failure
 Subset {1, 3}:
  {10}, k = 1, sum = 6
  Failure
Subset {1, 7}:
 {2, 2, 6, 20}, k = 1, sum = 12
 Failure
Subset {7, 7}:
 {2, 2, 6, 20}, k = 1, sum = 6
 Success