生成包含重复元素的列表的全排列
在Python中,使用itertools
模块可以很简单地生成一个列表的所有排列组合。我现在遇到一个情况,我用的序列只有两个字符(也就是'1122'
)。我想生成所有独特的排列。
对于字符串'1122'
,有6种独特的排列(比如1122
、1212
、1221
等等),但是itertools.permutations
会生成24个结果。虽然只记录独特的排列很简单,但这样做会花费更多的时间,因为它会考虑到所有720个排列。
有没有什么函数或模块可以在生成排列时考虑到重复的元素,这样我就不需要自己写代码了?
6 个回答
2
使用集合可以让解决方案变得更简单。输入的字符串可以是包含重复字符的,也可以是没有重复字符的。
from itertools import permutations
def perm(s):
return set(permutations(s))
l = '1122'
perm(l)
{('1', '1', '2', '2'),
('1', '2', '1', '2'),
('1', '2', '2', '1'),
('2', '1', '1', '2'),
('2', '1', '2', '1'),
('2', '2', '1', '1')}
l2 = '1234'
perm(l2)
{('1', '2', '3', '4'),
('1', '2', '4', '3'),
('1', '3', '2', '4'),
('1', '3', '4', '2'),
('1', '4', '2', '3'),
('1', '4', '3', '2'),
('2', '1', '3', '4'),
('2', '1', '4', '3'),
('2', '3', '1', '4'),
('2', '3', '4', '1'),
('2', '4', '1', '3'),
('2', '4', '3', '1'),
('3', '1', '2', '4'),
('3', '1', '4', '2'),
('3', '2', '1', '4'),
('3', '2', '4', '1'),
('3', '4', '1', '2'),
('3', '4', '2', '1'),
('4', '1', '2', '3'),
('4', '1', '3', '2'),
('4', '2', '1', '3'),
('4', '2', '3', '1'),
('4', '3', '1', '2'),
('4', '3', '2', '1')}
17
More Itertools 有一个函数可以做到这一点:
more-itertools.distinct_permutations(iterable)
这个函数会生成给定元素的不同排列组合。
它的效果和
set(permutations(iterable))
是一样的,但它不会生成重复的排列然后再把它们丢掉。对于较大的输入序列,这样做效率更高。
from more_itertools import distinct_permutations
for p in distinct_permutations('1122'):
print(''.join(p))
# 2211
# 2121
# 1221
# 2112
# 1212
# 1122
安装方法:
pip install more-itertools
21
这个网页看起来很不错。
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年8月12日
七年后,这里有一个更好的算法(更清晰易懂):
from itertools import permutations
def unique_perms(series):
return {"".join(p) for p in permutations(series)}
print(sorted(unique_perms('1122')))