Given a bag of elements, find all the subsets of the elements that sum to a given target.
For example:
>>> subset_sum(bag=[3, 2, 2, 1, 1, 1, 1], target=7)
[
(3, 2, 2),
(3, 2, 1, 1),
(2, 2, 1, 1, 1),
(3, 1, 1, 1, 1),
]Assumptions
- The integers are strictly non-negative
- We do not want duplicate subsets
- The input lists are sorted
Algorithm
The following algorithm using dynamic programming (courtesy of GitHub Copilot):
def subset_sum(numbers, target):
bins = [set() for _ in range(target + 1)]
bins[0].add(())
for num in numbers:
for t in range(target, num - 1, -1):
for prev in bins[t - num]:
bins[t].add(prev + (num,))
return bins[target]Complexity
Runtime:
Space:
Intuition
- We create “bins” for
(0..target), where each bin contains the combination of elements that sum to that given bin’s number - For a given element, we want to memoize the result by adding that element to the subset of elements
elementsteps away. Below is an example for an element 2, acting on bin 6:

- The beauty is that if there are duplicates, the operation above doesn’t change anything, since
bins[i]: set[tuple[int]].- Because
tuple[int]is used, it means that the input list has to be sorted. Otherwise, you’ll gettuple[int]s that are merely permutations of the same subset of elements (e.g.(2, 1, 3)and(3, 1, 2)). There are two ways to account for it:- Sort the input (simplest!)
- Use a Counter data structure
- Yet another beauty is that the solution still works even if you swap the ordering of the bag of elements
- Because
- When performing the operation above, it has to be done backwards, as shown by the
range(target, num - 1, -1). This is because we are using the result ofbins[t - num]forbins[t]. Had we operated non-reversed, we would have double/triple/multiple-counted a given element (num)‘s contribution to a set.