itertools.permutations根据其位置而不是其值来生成其元素被视为唯一的位置。所以基本上我想避免像这样的重复:
>>> list(itertools.permutations([1, 1, 1]))
[(1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1)]
之后过滤是不可能的,因为在我的情况下排列量太大了。
有人知道合适的算法吗?
非常感谢你!
编辑:
我基本上想要的是以下内容:
x = itertools.product((0, 1, 'x'), repeat=X)
x = sorted(x, key=functools.partial(count_elements, elem='x'))
这是不可能的,因为sorted
创建了一个列表,而itertools.product的输出太大了。
对不起,我应该已经描述了实际问题。
class unique_element:
def __init__(self,value,occurrences):
self.value = value
self.occurrences = occurrences
def perm_unique(elements):
eset=set(elements)
listunique = [unique_element(i,elements.count(i)) for i in eset]
u=len(elements)
return perm_unique_helper(listunique,[0]*u,u-1)
def perm_unique_helper(listunique,result_list,d):
if d < 0:
yield tuple(result_list)
else:
for i in listunique:
if i.occurrences > 0:
result_list[d]=i.value
i.occurrences-=1
for g in perm_unique_helper(listunique,result_list,d-1):
yield g
i.occurrences+=1
a = list(perm_unique([1,1,2]))
print(a)
结果:
[(2, 1, 1), (1, 2, 1), (1, 1, 2)]
编辑(这是如何工作的):
我重写上面的程序更长但更易读。
我通常很难解释某些事情是如何起作用的,但让我试试。为了理解它是如何工作的,你必须理解一个类似但更简单的程序,它会产生重复的所有排列。
def permutations_with_replacement(elements,n):
return permutations_helper(elements,[0]*n,n-1)#this is generator
def permutations_helper(elements,result_list,d):
if d<0:
yield tuple(result_list)
else:
for i in elements:
result_list[d]=i
all_permutations = permutations_helper(elements,result_list,d-1)#this is generator
for g in all_permutations:
yield g
这个程序显然要简单得多:d代表permutations_helper中的深度,并且有两个函数。一个函数是我们的递归算法的停止条件,另一个函数是传递的结果列表。
而不是返回每个结果,我们产生它。如果没有函数/运算符yield
,我们必须在停止条件点将结果推送到某个队列。但是这样,一旦满足停止条件,结果就会通过所有堆栈传播到调用者。这就是目的
for g in perm_unique_helper(listunique,result_list,d-1): yield g
所以每个结果都传播给调用者。
回到原始程序:我们有一个独特元素列表。在我们使用每个元素之前,我们必须检查它们中有多少仍可用于推送到result_list。使用此程序与permutations_with_replacement
非常相似。不同之处在于每个元素的重复次数不能超过perm_unique_helper。
这是问题的递归解决方案。
def permutation(num_array):
res=[]
if len(num_array) <= 1:
return [num_array]
for num in set(num_array):
temp_array = num_array.copy()
temp_array.remove(num)
res += [[num] + perm for perm in permutation(temp_array)]
return res
arr=[1,2,2]
print(permutation(arr))
您可以创建一个使用collections.Counter
从给定序列中获取唯一项及其计数的函数,并使用itertools.combinations
为每个递归调用中的每个唯一项选择索引组合,并在选择所有索引时将索引映射回列表:
from collections import Counter
from itertools import combinations
def unique_permutations(seq):
def index_permutations(counts, index_pool):
if not counts:
yield {}
return
(item, count), *rest = counts.items()
rest = dict(rest)
for indices in combinations(index_pool, count):
mapping = dict.fromkeys(indices, item)
for others in index_permutations(rest, index_pool.difference(indices)):
yield {**mapping, **others}
indices = set(range(len(seq)))
for mapping in index_permutations(Counter(seq), indices):
yield [mapping[i] for i in indices]
所以[''.join(i) for i in unique_permutations('moon')]
回归:
['moon', 'mono', 'mnoo', 'omon', 'omno', 'nmoo', 'oomn', 'onmo', 'nomo', 'oonm', 'onom', 'noom']
关于什么
np.unique(itertools.permutations([1, 1, 1]))
问题是排列现在是Numpy数组的行,因此使用更多的内存,但你可以像以前一样循环它们
perms = np.unique(itertools.permutations([1, 1, 1]))
for p in perms:
print p
前几天遇到了这个问题,同时解决了我自己的问题。我喜欢Luka Rahne的方法,但我认为在集合库中使用Counter类似乎是一种适度的改进。这是我的代码:
def unique_permutations(elements):
"Returns a list of lists; each sublist is a unique permutations of elements."
ctr = collections.Counter(elements)
# Base case with one element: just return the element
if len(ctr.keys())==1 and ctr[ctr.keys()[0]] == 1:
return [[ctr.keys()[0]]]
perms = []
# For each counter key, find the unique permutations of the set with
# one member of that key removed, and append the key to the front of
# each of those permutations.
for k in ctr.keys():
ctr_k = ctr.copy()
ctr_k[k] -= 1
if ctr_k[k]==0:
ctr_k.pop(k)
perms_k = [[k] + p for p in unique_permutations(ctr_k)]
perms.extend(perms_k)
return perms
此代码将每个排列作为列表返回。如果你给它一个字符串,它会给你一个排列列表,其中每个都是一个字符列表。如果您希望输出作为字符串列表(例如,如果您是一个可怕的人并且您想滥用我的代码来帮助您在Scrabble中作弊),请执行以下操作:
[''.join(perm) for perm in unique_permutations('abunchofletters')]
在这种情况下,我想出了一个非常合适的使用itertools.product的实现(这是一个你想要所有组合的实现
unique_perm_list = [''.join(p) for p in itertools.product(['0', '1'], repeat = X) if ''.join(p).count() == somenumber]
这本质上是一个组合(n over k),其中n = X,somenumber = k itertools.product()从k = 0迭代到k = X后续的带有计数的过滤确保只输入具有正确数量的1的排列一个列表。当你计算n超过k并将其与len(unique_perm_list)进行比较时,你可以很容易地看到它有效
我见过的这个问题的最佳解决方案是使用Knuth的“算法L”(正如Gerrat先前在原帖的评论中所述): http://stackoverflow.com/questions/12836385/how-can-i-interleave-or-create-unique-permutations-of-two-stings-without-recurs/12837695
一些时间:
排序[1]*12+[0]*12
(2,704,156个独特的排列):
算法L→2.43 s
Luke Rahne的解决方案→8.56秒
scipy.multiset_permutations()
→16.8秒
因为有时新问题被标记为重复,并且他们的作者被引用到这个问题,所以提及sympy为此目的具有迭代器可能是重要的。
>>> from sympy.utilities.iterables import multiset_permutations
>>> list(multiset_permutations([1,1,1]))
[[1, 1, 1]]
>>> list(multiset_permutations([1,1,2]))
[[1, 1, 2], [1, 2, 1], [2, 1, 1]]
这依赖于实现细节,即排序的可迭代的任何排列都按排序顺序排除,除非它们是先前排列的重复。
from itertools import permutations
def unique_permutations(iterable, r=None):
previous = tuple()
for p in permutations(sorted(iterable), r):
if p > previous:
previous = p
yield p
for p in unique_permutations('cabcab', 2):
print p
给
('a', 'a')
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'b')
('b', 'c')
('c', 'a')
('c', 'b')
('c', 'c')
您可以尝试使用set:
>>> list(itertools.permutations(set([1,1,2,2])))
[(1, 2), (2, 1)]
设置的调用删除了重复项
与Luka Rahne的回答大致相同,但更短更简单,恕我直言。
def unique_permutations(elements):
if len(elements) == 1:
yield (elements[0],)
else:
unique_elements = set(elements)
for first_element in unique_elements:
remaining_elements = list(elements)
remaining_elements.remove(first_element)
for sub_permutation in unique_permutations(remaining_elements):
yield (first_element,) + sub_permutation
>>> list(unique_permutations((1,2,3,1)))
[(1, 1, 2, 3), (1, 1, 3, 2), (1, 2, 1, 3), ... , (3, 1, 2, 1), (3, 2, 1, 1)]
它通过设置第一个元素(遍历所有唯一元素)递归工作,并迭代所有剩余元素的排列。
让我们通过(1,2,3,1)的unique_permutations
来看看它是如何工作的:
unique_elements
是1,2,3first_element
以1开头。
remaining_elements
是[2,3,1](即1,2,3,1减去前1)
我们迭代(递归地)通过剩余元素的排列:(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1, 2),(3,2,1)
对于每个sub_permutation
,我们插入first_element
:(1,1,2,3),(1,1,3,2),...并得到结果。first_element
= 2,并执行与上面相同的操作。
remaining_elements
是[1,3,1](即1,2,3,1减去前2)
我们遍历其余元素的排列:(1,1,3),(1,3,1),(3,1,1)
对于每个sub_permutation
,我们插入first_element
:(2,1,1,3),(2,1,1,1),(2,3,1,1)......并得到结果。first_element
= 3做同样的事情。这是我的10行解决方案:
class Solution(object):
def permute_unique(self, nums):
perms = [[]]
for n in nums:
new_perm = []
for perm in perms:
for i in range(len(perm) + 1):
new_perm.append(perm[:i] + [n] + perm[i:])
# handle duplication
if i < len(perm) and perm[i] == n: break
perms = new_perm
return perms
if __name__ == '__main__':
s = Solution()
print s.permute_unique([1, 1, 1])
print s.permute_unique([1, 2, 1])
print s.permute_unique([1, 2, 3])
---结果----
[[1, 1, 1]]
[[1, 2, 1], [2, 1, 1], [1, 1, 2]]
[[3, 2, 1], [2, 3, 1], [2, 1, 3], [3, 1, 2], [1, 3, 2], [1, 2, 3]]
一种天真的方法可能是采取一组排列:
list(set(it.permutations([1, 1, 1])))
# [(1, 1, 1)]
但是,这种技术浪费地计算复制排列并丢弃它们。一个更有效的方法是more_itertools.distinct_permutations
,一个third-party tool。
码
import itertools as it
import more_itertools as mit
list(mit.distinct_permutations([1, 1, 1]))
# [(1, 1, 1)]
性能
使用更大的可迭代,我们将比较天真和第三方技术之间的表现。
iterable = [1, 1, 1, 1, 1, 1]
len(list(it.permutations(iterable)))
# 720
%timeit -n 10000 list(set(it.permutations(iterable)))
# 10000 loops, best of 3: 111 µs per loop
%timeit -n 10000 list(mit.distinct_permutations(iterable))
# 10000 loops, best of 3: 16.7 µs per loop
我们看到more_itertools.distinct_permutations
的速度要快一个数量级。
细节
从源头开始,递归算法(如在接受的答案中所见)用于计算不同的排列,从而避免了浪费的计算。有关详细信息,请参阅source code。
听起来你正在寻找itertools.combinations()docs.python.org
list(itertools.combinations([1, 1, 1],3))
[(1, 1, 1)]
在寻找自己的东西的时候碰到这个问题!
这是我做的:
def dont_repeat(x=[0,1,1,2]): # Pass a list
from itertools import permutations as per
uniq_set = set()
for byt_grp in per(x, 4):
if byt_grp not in uniq_set:
yield byt_grp
uniq_set.update([byt_grp])
print uniq_set
for i in dont_repeat(): print i
(0, 1, 1, 2)
(0, 1, 2, 1)
(0, 2, 1, 1)
(1, 0, 1, 2)
(1, 0, 2, 1)
(1, 1, 0, 2)
(1, 1, 2, 0)
(1, 2, 0, 1)
(1, 2, 1, 0)
(2, 0, 1, 1)
(2, 1, 0, 1)
(2, 1, 1, 0)
set([(0, 1, 1, 2), (1, 0, 1, 2), (2, 1, 0, 1), (1, 2, 0, 1), (0, 1, 2, 1), (0, 2, 1, 1), (1, 1, 2, 0), (1, 2, 1, 0), (2, 1, 1, 0), (1, 0, 2, 1), (2, 0, 1, 1), (1, 1, 0, 2)])
基本上,制作一套并继续添加它。比制作那些占用太多记忆的列表等更好。希望它有助于下一个人注意:-)注释掉函数中的“更新”设置以查看差异。