查找列表中的最小值数以求和值

时间:2012-09-26 22:49:36

标签: algorithm knapsack-problem

所以,如果我得到一个排序列表/数组,即[1,6,8,15,40],数组的大小和请求的数字..

您如何找到该列表所需的最小值数总和为所需数量?

例如,给定数组[1,6,8,15,40],我请求数字23,它将从列表中的2个值(8和15)等于23.函数将返回2(价值数量。此外,数组中有无限数量的1(因此您的函数将始终返回一个值)

感谢任何帮助

2 个答案:

答案 0 :(得分:2)

NP-complete subset-sum problem简单地减少了你的问题:给定一组 S 的整数和一个目标值 s ,我们构造了set S'对于 x k 的值(n + 1)x k > S 并将目标设置为(n + 1)s 。如果原始集合 S 的一个子集与 s 相加,那么在新集合求和中将有一个最大 n 的子集到(n + 1)s ,这样的集合不能涉及额外的1。如果没有这样的子集,那么作为答案产生的子集必须至少包含 n + 1 元素,因为它需要足够的 1s 来获得的倍数的n + 1

因此,如果没有计算机的革命,问题就不会允许任何多项式时间解决方案。有了这个免责声明,您可以考虑一些伪多项式时间解决方案,如果集合的最大尺寸很小,这个解决方案在实践中运作良好。

这是一个Python算法,它将执行此操作:

import functools
S = [1, 6, 8, 15, 40] # must contain only positive integers
@functools.lru_cache(maxsize=None) # memoizing decorator
def min_subset(k, s):
    # returns the minimum size of a subset of S[:k] summing to s, including any extra 1s needed to get there
    best = s # use all ones
    for i, j in enumerate(S[:k]):
        if j <= s:
            sz = min_subset(i, s-j)+1
            if sz < best: best = sz
    return best

print min_subset(len(S), 23) # prints 2

即使对于相当大的列表(我测试了n = 50个元素的随机列表),这也是易处理的,提供它们的值是有界的。使用S = [random.randint(1, 500) for _ in xrange(50)]min_subset(len(S), 8489)运行时间不到10秒。

答案 1 :(得分:0)

可能有一个更简单的解决方案,但如果您的列表足够短,您可以尝试每组值,即:

  • 1 - &gt;不是23
  • 6 - &gt;不是23
  • ...
  • 1 + 6 = 7 - &gt;不是23
  • 1 + 8 = 9 - &gt;不是23
  • ...
  • 1 + 40 = 41 - &gt;不是23
  • 6 + 8 = 14 - &gt;不是23
  • ...
  • 8 + 15 = 23 - &gt;哦,看,它是23,我们添加了2个值

如果您知道您的列表已排序,则可以跳过某些测试,因为如果6 + 20&gt; 23,那么就没有必要测试6 + 40。