Permutations are about taking an ordered set of things and moving these things around (i.e. changing order). Your question is about combinations of things from your list.
Now, an easy way of enumerating combinations is by mapping entries from your list to bits in a number. For example, lets assume that if bit #0 is set (i.e. 1), then number lst[0]
participates in the combination, if bit #1 is set, then lst[1]
participates in the combination, etc. This way, numbers in range 0 <= n < 2**(len(lst))
identify all possible combinations of lst
members, including an empty one (n = 0
) and the whole lst
(n = 2**(len(lst)) - 1
).
You need only combinations of 2 items or more, i.e. only those combination IDs that have at least two nonzero bits in their binary representation. Here is how to identify these:
def HasAtLeastTwoBitsSet(x) :
return (x & (x-1)) != 0
# Testing:
>>> [x for x in range(33) if HasAtLeastTwoBitsSet(x)]
[3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
Next step is to extract a combination of list members identified by a combination id. This is easy, thanks to the power of list comprehensions:
def GetSublistByCombination(lst, combination_id) :
res = [x for (i,x) in enumerate(lst) if combination_id & (1 << i)]
return res
# Testing:
>>> GetSublistByCombination([0,1,2,3], 1)
[0]
>>> GetSublistByCombination([0,1,2,3], 3)
[0, 1]
>>> GetSublistByCombination([0,1,2,3], 12)
[2, 3]
>>> GetSublistByCombination([0,1,2,3], 15)
[0, 1, 2, 3]
Now let's make a generator that produces all sums, together with their string representations:
def IterAllSums(lst) :
combinations = [i for i in range(1 << len(lst)) if HasAtLeastTwoBitsSet(i)]
for comb in combinations :
sublist = GetSublistByCombination(lst, comb)
sum_str = '+'.join(map(str, sublist))
sum_val = sum(sublist)
yield (sum_str, sum_val)
And, finally, let's use it:
>>> for sum_str, sum_val in IterAllSums([1,2,3,4]) : print sum_str, sum_val
1+2 3
1+3 4
2+3 5
1+2+3 6
1+4 5
2+4 6
1+2+4 7
3+4 7
1+3+4 8
2+3+4 9
1+2+3+4 10