I am looking for an answer to the following problem.
Given a set of integers (no duplicates) and a sum, find all possible combinations of the set's elements summing up to the sum. Solutions order does not matter (solutions {2, 2, 3} and {3, 2 ,2} are equal).
Please note that the final combination does not need to be a set, as it can contain duplicates.
Example: Set {2,3,5} Sum 10
Result: {2, 2, 2, 2, 2}, {2, 2, 3, 3}, {2, 3, 5}, {5, 5}
I've looked at Subset Sum problem as well as Coin Change problem, but couldn't adapt them to suit my needs. I am not really familiar with dynamic programming, so it's probably doable, however I couldn't figure it out.
As I am dealing with a fairly large set of elements (around 50), precomputing all the sets is not an option as it would take a very long time. A way to pull out different solutions from a Subset Sum table would be preferable (if possible).
Any advice, tips, or sample code would be appreciated!
This is known as the Change-making problem and is a classic example in dynamic programming.
Some earlier answers have calculated the total count of solutions, whilst the question has asked for an enumeration of the possible solutions.
You haven't tagged your question with a language, so here's an implementation in Python. Adapt it to whatever language you please by using your language's "bag" datatype (n.b. the Counter
is Python's "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]
The output datatype is a list of bags. Demo usage for printing them:
>>> 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
Here is a Haskell function that calculates the answer:
partitions 0 xs = [[]]
partitions _ [] = []
partitions n (xxs@(x:xs)) | n < 0 = []
| otherwise = (map (x:) (partitions (n-x) xxs)) ++ partitions n xs
Examples:
*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]]
Solution complexity:
- time: O(n*M)
- memory: O(M),
where M is value of sum, n is set size
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];
}
来源:https://stackoverflow.com/questions/41806819/find-all-combinations-of-a-given-set-of-integers-summing-up-to-a-given-sum