tags:

views:

74

answers:

3

(First, I chose to do this in Python because I never programmed in it and it would be good practice.)

Someone asked me to implement a little "combination" program that basically outputs all possible combinations of a set of group of numbers. Example, if you have:
(1,2,3) as the first set,
(4,5,6) as the second, and
(7,8,9) as the third, then one combination would be (1,4,7) and so on, with a total of 27 possible combinations. This person just wants to do a 6rows x 6cols matrix or a 5rows x 6cols matrix. However, I want to make my little program as flexible as possible.
The next requirement is to only output combinations with X even numbers. If he wants 0 even numbers, then a possible combination would be (1,5,7). You get the idea. For the permutation part, I used itertools.product(), which works perfectly. It would be easy if I just assume that the number of numbers in each set (cols) is fixed as 6. In that case, I could manually create 6 lists and append each combination to the right list. However and again, I want this to work with N number of cols.

I'm thinking of 2 ways I might be able to do this, but tried with no luck. So my question is: How can I create?

li_1 = [] 
li_2 = [] 
...
li_x = [] 

The one way I tried using "lists of lists":

for combination in itertools.product(*li):
    total_combinations = total_combinations + 1
    #Counts number of even numbers in a single combination
    for x in range(numberInRows):
        if combination[x] % 2 == 0:
            even_counter = even_counter + 1
    print "Even counter:",even_counter
    num_evens[even_counter].append(combination)
    print "Single set:",num_evens
    even_counter = 0

    print combination
print "Num_evens:",num_evens

print '\nTotal combinations:', total_combinations
+1  A: 
Ls = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
import collections
import itertools

def products_by_even_count(seq):
    ret = collections.defaultdict(set)
    for p in itertools.product(*seq):
        n_even = sum(1 for n in p if n % 2 == 0)
        ret[n_even].add(p)
    return ret

import pprint
# Calling dict() is only necessary for pretty pprint output.
pprint.pprint(dict(products_by_even_count(Ls)))

Output:

{0: set([(1, 5, 7), (1, 5, 9), (3, 5, 7), (3, 5, 9)]),
 1: set([(1, 4, 7),
         (1, 4, 9),
         (1, 5, 8),
         (1, 6, 7),
         (1, 6, 9),
         (2, 5, 7),
         (2, 5, 9),
         (3, 4, 7),
         (3, 4, 9),
         (3, 5, 8),
         (3, 6, 7),
         (3, 6, 9)]),
 2: set([(1, 4, 8),
         (1, 6, 8),
         (2, 4, 7),
         (2, 4, 9),
         (2, 5, 8),
         (2, 6, 7),
         (2, 6, 9),
         (3, 4, 8),
         (3, 6, 8)]),
 3: set([(2, 4, 8), (2, 6, 8)])}
Aaron Gallagher
+1  A: 
num_evens = {} 
for combination in itertools.product(*li):
    even_counter = len([ y for y in combination if y & 1 == 0 ])
    num_evens.setdefault(even_counter,[]).append(combination)

import pprint
pprint.pprint(num_evens)
Andre Holzner
Thanks, it worked. Could you kindly explain to me the two lines inside the for loop? Specifically the "len" part and the "setdefault(even_counter,[])".
chiurox
Andre Holzner
Building a whole list just to take its `len` isn't necessary, which is why I used `sum` and a generator expression instead.
Aaron Gallagher
The second line in the loop sets an entry in the dict 'num_evens'.This dict (in our case here) maps from a number (key) to a list of tuples (value).Now if you want to append 'combination' to the list associated to the key 'even_counter' one must make sure that there is at least an empty list (one can't append to 'None') for the given key. This is what setdefault ensures: if 'even_counter' is not yet in the dict 'num_evens', it first puts the second argument (an empty list in this example) into the dict and then returns it (or returns the value which was already in the dict before).
Andre Holzner
Ah... I get it. I just find it strange why my own code is not outputting correctly. It seems like the same basic logic.
chiurox
+1 for the good point about len/sum... (I should look into generators in more detail)
Andre Holzner
+1  A: 
from itertools import product
from collections import defaultdict
num_evens = defaultdict(list)
for comb in product(*li):
    num_evens[sum(y%2==0 for y in comb)].append(comb)

import pprint
pprint.pprint(num_evens)
gnibbler