找到总和给定总和的给定整数集的所有组合

时间:2017-01-23 12:49:39

标签: algorithm dynamic-programming subset-sum

我正在寻找以下问题的答案。

给定一组整数(无重复)和求和,找到总和为总和的集合元素的所有可能组合。解决方案顺序无关紧要(解决方案{2,2,3}和{3,2,2}相等)。

请注意,最终组合不需要是一个集合,因为它可以包含重复项。

实施例: 设置{2,3,5} 总和10

结果: {2,2,2,2,2},{2,2,3,3},{2,3,5},{5,5}

我看过Subset Sum问题以及Coin Change问​​题,但无法根据我的需要调整它们。我对动态编程并不熟悉,所以它可能是可行的,但是我无法理解它。

当我处理一组相当大的元素(大约50个)时,预先计算所有集合不是一个选项,因为它需要很长时间。从子集总和表中提取不同解决方案的方法将是可取的(如果可能的话)。

任何建议,提示或示例代码都将不胜感激!

3 个答案:

答案 0 :(得分:2)

这被称为Change-making problem,是dynamic programming中的经典示例。

一些早期的答案计算了解决方案的总计计数,而问题则要求提供可能解决方案的枚举

您还没有使用某种语言标记您的问题,因此这是Python中的一个实现。使用您的语言" bag"以适应您喜欢的任何语言。数据类型(n.b. Counter是Python" bag")。

from collections import Counter

def ways(total, coins):
    ways = [[Counter()]] + [[] for _ in range(total)]
    for coin in coins:
        for i in range(coin, total + 1):
            ways[i] += [way + Counter({coin: 1}) for way in ways[i-coin]]
    return ways[total]

输出数据类型是行李列表。用于打印它们的演示用法:

>>> from __future__ import print_function  # for Python 2 compatibility
>>> for way in ways(total=10, coins=(2,3,5)):
...     coins = (coin for coin,count in way.items() for _ in range(count))
...     print(*coins)
... 
2 2 2 2 2
2 2 3 3
2 3 5
5 5

答案 1 :(得分:0)

这是一个计算答案的Haskell函数:

partitions 0 xs = [[]]
partitions _ [] = []
partitions n (xxs@(x:xs)) | n < 0 = []
                          | otherwise = (map (x:) (partitions (n-x) xxs)) ++ partitions n xs

示例:

*Main>  partitions 1 [1]
[[1]]
*Main>  partitions 5 [1..5]
[[1,1,1,1,1],[1,1,1,2],[1,1,3],[1,2,2],[1,4],[2,3],[5]]
*Main> length $ partitions 10 [1..10]
42
*Main> length $ partitions 20 [1..20]
627
*Main> length $ partitions 40 [1..40]
37338
*Main> partitions 10 [1,2,4]
[[1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,2],[1,1,1,1,1,1,2,2],[1,1,1,1,1,1,4],[1,1,1,1,2,2,2],[1,1,1,1,2,4],[1,1,2,2,2,2],[1,1,2,2,4],[1,1,4,4],[2,2,2,2,2],[2,2,2,4],[2,4,4]]

Semi-live demo

答案 2 :(得分:-1)

解决方案的复杂性:

  • 时间:O(n * M)
  • 记忆:O(M),

其中M是sum的值,n是设置大小

int numberOfSums(Set<Integer> values, int sum) {
    // sumCount[i] -> number of ways to get sum == i 
    int sumCount[] = new int[sum+1];
    sumCount[0] = 1;
    for(int v : values) {
        for(int i=0; i<=sum-v; ++i)
            sumCount[i+v] += sumCount[i];
    }
    return sumCount[sum];
}