Thought I'll throw another solution into the mix.
We can map each selection of a subset of the list to a (0-padded) binary number, where a 0 means not taking the member in the corresponsing position in the list, and 1 means taking it.
So masking [1, 2, 3, 4]
with 0101
creates the sub-list [2, 4]
.
So, by generating all 0-padded binary numbers in the range between 0 and 2^LENGTH_OF_LIST, we can iterate all selections. If we use these sub-list selections as masks and sum the selection - we can know the answer.
This is how it's done:
#!/usr/bin/env python
# use a binary number (represented as string) as a mask
def mask(lst, m):
# pad number to create a valid selection mask
# according to definition in the solution laid out
m = m.zfill(len(lst))
return map(lambda x: x[0], filter(lambda x: x[1] != '0', zip(lst, m)))
def subset_sum(lst, target):
# there are 2^n binary numbers with length of the original list
for i in xrange(2**len(lst)):
# create the pick corresponsing to current number
pick = mask(lst, bin(i)[2:])
if sum(pick) == target:
return pick
return False
print subset_sum([1,2,3,4,5], 7)
Output:
[3, 4]
To return all possibilities we can use a generator instead (the only changes are in subset_sum
, using yield
instead of return
and removing return False
guard):
#!/usr/bin/env python
# use a binary number (represented as string) as a mask
def mask(lst, m):
# pad number to create a valid selection mask
# according to definition in the solution laid out
m = m.zfill(len(lst))
return map(lambda x: x[0], filter(lambda x: x[1] != '0', zip(lst, m)))
def subset_sum(lst, target):
# there are 2^n binary numbers with length of the original list
for i in xrange(2**len(lst)):
# create the pick corresponsing to current number
pick = mask(lst, bin(i)[2:])
if sum(pick) == target:
yield pick
# use 'list' to unpack the generator
print list(subset_sum([1,2,3,4,5], 7))
Output:
[[3, 4], [2, 5], [1, 2, 4]]
Note: While not padding the mask with zeros may work as well, as it will simply select members of the original list in a reverse order - I haven't checked it and didn't use it.
I didn't use it since it's less obvious (to me) what's going on with such trenary-like mask (1, 0 or nothing) and I rather have everything well defined.