Reputation: 157
I'm trying to figure out how to create all possible n sized permutations from a list recursively.
For example the result of n=2
and list=[0,1,5]
would be:
[[0, 0], [1, 0], [5, 0], [0, 1], [1, 1], [5, 1], [0, 5], [1, 5], [5, 5]]
or n=3
and list=[2,3]
:
[[2, 2, 2], [3, 2, 2], [2, 3, 2], [3, 3, 2], [2, 2, 3], [3, 2, 3], [2, 3, 3], [3, 3, 3]]
(sort of like cartesian product).
I've managed to come up with this piece of code:
def perm(nlist, m):
if m > len(nlist):
return
if m == 0 or m == len(nlist):
return [[]]
results = []
for list_i in nlist:
temp = list(nlist)
temp.remove(list_i)
results.extend(map(lambda x: [list_i] + x, perm(temp, m-1)))
return results
but it doesn't give permutations like [0,0] [1,1] [2,2]
etc.
Does anybody have a solution for me?
How can I make this without using map()
and lambda()
?
Upvotes: 2
Views: 254
Reputation: 2567
The result you are looking for is the Cartesian product which is the set of all ordered pairs (a, b) where a ∈ A and b ∈ B. Or, all permutations of all combinations.
from itertools import combinations_with_replacement as iter_combs
from itertools import permutations
def perms_combs(l, n):
all_combs = []
for l in list(iter_combs(l, n)):
[all_combs.append(perm) for perm in list(permutations(l, n))]
return list(set(all_combs))
>> print list(product([0, 1, 5], repeat=2))
[(0, 0), (0, 0), (0, 1), (1, 0), (0, 5), (5, 0), (1, 1), (1, 1), (1, 5), (5, 1), (5, 5), (5, 5)]
>> print list(product([2, 3], repeat=3))
[(3, 3, 3), (2, 2, 2), (2, 3, 2), (3, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (2, 2, 3)]
This process, however, is already covered by an itertools functions: product
from itertools import product
>> print product([0, 1, 5], 2)
[(0, 0), (0, 0), (0, 1), (1, 0), (0, 5), (5, 0), (1, 1), (1, 1), (1, 5), (5, 1), (5, 5), (5, 5)]
>> print product([2, 3], 3)
[(3, 3, 3), (2, 2, 2), (2, 3, 2), (3, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (2, 2, 3)]
Upvotes: 2
Reputation: 13869
This isn't sort of like cartesian product; it's exactly like cartesian product.
>>> from itertools import product
>>> list(product([0,1,5], repeat=2))
[(0, 0), (0, 1), (0, 5), (1, 0), (1, 1), (1, 5), (5, 0), (5, 1), (5, 5)]
>>> list(product([2,3], repeat=3))
[(2, 2, 2), (2, 2, 3), (2, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (3, 3, 2), (3, 3, 3)]
The polyfill for itertools.product
is the following:
def product(*args, **kwds):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools = map(tuple, args) * kwds.get('repeat', 1)
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
But since you can't use itertools, you might as well take the liberty of writing a slightly more efficient solution to your problem. Since we are just computing the product of n
identical iterables, let's just call it a cartesian exponent:
def cartesian_exponent(li, e=1):
pools = [li] * e
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
return result
Or recursively using yet another incomprehensible list comprehension:
def cartesian_exponent(li, e=1):
if e == 1:
return [[x] for x in li]
else:
return [[x]+y for x in li for y in cartesian_exponent(li, e=e-1)]
Which can be compressed to one line:
def cartesian_exponent(li, e=1):
return [[x] for x in li] if e == 1 else [[x] + y for x in li for y in cartesian_exponent(li, e=e-1)]
But then you would be sacrificing readability for terseness and that's no bueno. The incomprehensible list comprehension is already opaque enough.
Some tests:
>>> cartesian_exponent([0,1,5], e=2)
[[0, 0], [0, 1], [0, 5], [1, 0], [1, 1], [1, 5], [5, 0], [5, 1], [5, 5]]
>>> cartesian_exponent([2,3], e=3)
[[2, 2, 2], [2, 2, 3], [2, 3, 2], [2, 3, 3], [3, 2, 2], [3, 2, 3], [3, 3, 2], [3, 3, 3]]
Upvotes: 2