Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate permutations of list with repeated elements

In Python, it is quite simple to produce all permutations of a list using the itertools module. I have a situation where the sequence I'm using only has two characters (i.e. '1122'). I want to generate all unique permutations.

For the string '1122', there are 6 unique permutations (1122, 1212, 1221, etc), but itertools.permutations will yield 24 items. It's simple to only record the unique permutations, but it will take much longer than necessary to collect them as all 720 items are considered.

Is there a function or module that accounts for repeated elements when generating permutations so I don't have to write my own?

like image 426
JoshD Avatar asked Nov 22 '10 20:11

JoshD


People also ask

How do you find the permutation of a list in Python?

The number of permutations on a set of n elements is given by n!. For example, there are 2! = 2*1 = 2 permutations of {1, 2}, namely {1, 2} and {2, 1}, and 3! = 3*2*1 = 6 permutations of {1, 2, 3}, namely {1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2} and {3, 2, 1}.

How do you generate permutations of an array?

You take first element of an array (k=0) and exchange it with any element (i) of the array. Then you recursively apply permutation on array starting with second element. This way you get all permutations starting with i-th element.


2 Answers

This web page looks promising.

def next_permutation(seq, pred=cmp):     """Like C++ std::next_permutation() but implemented as     generator. Yields copies of seq."""     def reverse(seq, start, end):         # seq = seq[:start] + reversed(seq[start:end]) + \         #       seq[end:]         end -= 1         if end <= start:             return         while True:             seq[start], seq[end] = seq[end], seq[start]             if start == end or start+1 == end:                 return             start += 1             end -= 1     if not seq:         raise StopIteration     try:         seq[0]     except TypeError:         raise TypeError("seq must allow random access.")     first = 0     last = len(seq)     seq = seq[:]     # Yield input sequence as the STL version is often     # used inside do {} while.     yield seq[:]     if last == 1:         raise StopIteration     while True:         next = last - 1         while True:             # Step 1.             next1 = next             next -= 1             if pred(seq[next], seq[next1]) < 0:                 # Step 2.                 mid = last - 1                 while not (pred(seq[next], seq[mid]) < 0):                     mid -= 1                 seq[next], seq[mid] = seq[mid], seq[next]                 # Step 3.                 reverse(seq, next1, last)                 # Change to yield references to get rid of                 # (at worst) |seq|! copy operations.                 yield seq[:]                 break             if next == first:                 raise StopIteration     raise StopIteration  >>> for p in next_permutation([int(c) for c in "111222"]): ...     print p ...  [1, 1, 1, 2, 2, 2] [1, 1, 2, 1, 2, 2] [1, 1, 2, 2, 1, 2] [1, 1, 2, 2, 2, 1] [1, 2, 1, 1, 2, 2] [1, 2, 1, 2, 1, 2] [1, 2, 1, 2, 2, 1] [1, 2, 2, 1, 1, 2] [1, 2, 2, 1, 2, 1] [1, 2, 2, 2, 1, 1] [2, 1, 1, 1, 2, 2] [2, 1, 1, 2, 1, 2] [2, 1, 1, 2, 2, 1] [2, 1, 2, 1, 1, 2] [2, 1, 2, 1, 2, 1] [2, 1, 2, 2, 1, 1] [2, 2, 1, 1, 1, 2] [2, 2, 1, 1, 2, 1] [2, 2, 1, 2, 1, 1] [2, 2, 2, 1, 1, 1] >>>  

2017-08-12

Seven years later, here is a better algorithm (better for clarity):

from itertools import permutations  def unique_perms(series):     return {"".join(p) for p in permutations(series)}  print(sorted(unique_perms('1122'))) 
like image 181
hughdbrown Avatar answered Sep 24 '22 02:09

hughdbrown


More Itertools has a function for this:

more-itertools.distinct_permutations(iterable)

Yields successive distinct permutations of the elements in iterable.

Equivalent to set(permutations(iterable)), except duplicates are not generated and thrown away. For larger input sequences, this is much more efficient.

from more_itertools import distinct_permutations

for p in distinct_permutations('1122'):
    print(''.join(p))

# 2211
# 2121
# 1221
# 2112
# 1212
# 1122

Installation:

pip install more-itertools
like image 41
MarredCheese Avatar answered Sep 23 '22 02:09

MarredCheese