And I tested the various implementations along with a couple others, and ecombinations was (surprisingly) the clear winner. It makes me want to eschew Python for Lisp… :-)

]]>`reversed`

…
]]>def combinations(*list): r=[[]] for x in reversed(list): r = [[y]+i for y in x for i in r] return r

Adding `print r`

inside the loop reveals how it works:

>>> combinations([1,2],[3,4],[5,6]) and 'Done' [[5], [6]] [[3, 5], [3, 6], [4, 5], [4, 6]] [[1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]] 'Done']]>

def combinations(*list): r=[[]] for x in list: r = [i+[y] for y in x for i in r] return r print combinations([1,2,3],[4,5,6]) print print combinations([1,2],[3,4],[5,6]) print print combinations([1,2,3],[4,5],[6],[7,8,9,10])]]>

In [103]: def powerset(S): for b in range(1 << len(S)): yield set(e for (i,e) in enumerate(S) if (1<<i)&b) In [104]: myset = set(range(1,5)) In [105]: list(powerset(myset)) Out[105]: [set([]), set([1]), set([2]), set([1, 2]), set([3]), set([1, 3]), set([2, 3]), set([1, 2, 3]), set([4]), set([1, 4]), set([2, 4]), set([1, 2, 4]), set([3, 4]), set([1, 3, 4]), set([2, 3, 4]), set([1, 2, 3, 4])]

(Problem suggested by Árni Már)

]]>(range(2) for i in range(10)) (range(2) for i in range(100)) (range(2) for i in range(1000)) (range(2) for i in range(10000)) # perhaps to much? (range(10) for i in range(10)) (range(100) for i in range(10)) (range(10000) for i in range(2)) (range(i) for i in range(10)) (range(i) for i in range(100))

etc.

Arnar

]]>>>> c=1 >>> t = time(); list(gcombs(*(range(2) for i in range(18)))) and time() - t 0.87823700904846191 >>> c=1 >>> t = time(); list(gcombs(*(range(2) for i in range(18)))) and time() - t 0.67177414894104004 >>> t = time(); list(icombinations(*(range(2) for i in range(18)))) and time() - t 0.68789410591125488 >>> t = time(); list(icombinations(*(range(2) for i in range(18)))) and time() - t 0.72254085540771484 >>> t = time(); list(ecombinations(*(range(2) for i in range(18)))) and time() - t 0.58095097541809082 >>> t = time(); list(ecombinations(*(range(2) for i in range(18)))) and time() - t 0.59889411926269531

I wouldn’t take these numbers seriously though. In some of my tests, icombinations beat ecombinations.

]]>I finally ran some profiling. It seems to be about 16x slower than the recursive solution. I don’t think it’s worth updating the variable names :-).

Gary Godfrey

Austin, TX, USA

`for`

loops.
Any chance we could get a version with clearer variable names? ☺

]]>from itertools import * c = 1 def cumul(n): global c r = c c *= len(n) return r def gcombs(*s): tary = [ cycle(chain(*[ repeat(nn, cumval) for nn in news])) for (news,cumval) in [ (ns, cumul(ns)) for ns in s]] tary.append(xrange(0,c)) t = izip( *tary) for v in t: yield v[:-1] print list(gcombs([1,2],[4,5,6],[7,8,9]))]]>

def ecombinations(*args): n = len(args) defun = "def inner(args):" for_loops = "\n".join("%sfor v%s in args[%s]:" % (" "*(i+1), i, i) for i in range(n)) yield_line = "v%s, " * n % tuple(range(n)) yield_line = "%syield (%s)" % (" "*(n+1), yield_line) exec '\n'.join([defun, for_loops, yield_line]) return inner(args)]]>

Actually, the previous version goes through items in the wrong order:

`>>> print '\n'.join(repr(i) for i in icombinations("12", "ab", "yz"))`

('1', 'a', 'y')

('2', 'a', 'y')

('1', 'b', 'y')

('2', 'b', 'y')

('1', 'a', 'z')

('2', 'a', 'z')

('1', 'b', 'z')

('2', 'b', 'z')

What we actually want is this:

`def icombinations(*seqs):`

if len(seqs) == 1:

for i in seqs[0]: yield (i, )

else:

for rest in icombinations(*seqs[:-1]):

for i in seqs[-1]:

yield rest + (i, )

In order to get this output:

`>>> print '\n'.join(repr(i) for i in icombinations("12", "ab", "yz"))`

('1', 'a', 'y')

('1', 'a', 'z')

('1', 'b', 'y')

('1', 'b', 'z')

('2', 'a', 'y')

('2', 'a', 'z')

('2', 'b', 'y')

('2', 'b', 'z')

`>>> print \'\\n\'.join(repr(i) for i in icombinations(\"12\", \"ab\", \"yz\"))`

(\'1\', \'a\', \'y\')

(\'2\', \'a\', \'y\')

(\'1\', \'b\', \'y\')

(\'2\', \'b\', \'y\')

(\'1\', \'a\', \'z\')

(\'2\', \'a\', \'z\')

(\'1\', \'b\', \'z\')

(\'2\', \'b\', \'z\')

What we actually want is this:

`def icombinations(*seqs):`

if len(seqs) == 1:

for i in seqs[0]: yield (i, )

else:

for rest in icombinations(*seqs[:-1]):

for i in seqs[-1]:

yield rest + (i, )

In order to get this output:

`>>> print \'\\n\'.join(repr(i) for i in icombinations(\"12\", \"ab\", \"yz\"))`

(\'1\', \'a\', \'y\')

(\'1\', \'a\', \'z\')

(\'1\', \'b\', \'y\')

(\'1\', \'b\', \'z\')

(\'2\', \'a\', \'y\')

(\'2\', \'a\', \'z\')

(\'2\', \'b\', \'y\')

(\'2\', \'b\', \'z\')

def icombinations(*seqs): if len(seqs) == 1: for i in seqs[0]: yield (i, ) else: for rest in icombinations(*seqs[1:]): for i in seqs[0]: yield (i, ) + rest]]>