具有独特价值的排列

xyz*_*123 68 python permutation python-itertools

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)]
Run Code Online (Sandbox Code Playgroud)

之后进行过滤是不可能的,因为在我的情况下排列量太大了.

有人知道合适的算法吗?

非常感谢你!

编辑:

我基本上想要的是以下内容:

x = itertools.product((0, 1, 'x'), repeat=X)
x = sorted(x, key=functools.partial(count_elements, elem='x'))
Run Code Online (Sandbox Code Playgroud)

这是不可能的,因为sorted创建一个列表并且itertools.product的输出太大.

对不起,我应该已经描述了实际问题.

Luk*_*hne 50

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)
Run Code Online (Sandbox Code Playgroud)

结果:

[(2, 1, 1), (1, 2, 1), (1, 1, 2)]
Run Code Online (Sandbox Code Playgroud)

编辑(这是如何工作的):

我重写了上层程序更长但更易读

我通常很难解释某些事情是如何运作的,但让我试试.为了理解这是如何工作的,你必须理解类似的,但是一个更简单的程序可以产生重复的所有排列.

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
Run Code Online (Sandbox Code Playgroud)

这个程序显然要简单得多: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.

  • 我试图理解这是如何工作的,但我很难过.你能提供一些评论吗? (3认同)

Bil*_*ell 32

因为有时新问题被标记为重复,并且他们的作者被引用到这个问题,所以提及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]]
Run Code Online (Sandbox Code Playgroud)

  • 这是唯一明确标识OP正在寻找的内容的答案(即**[Multisets](https://en.wikipedia.org/wiki/Multiset)**的排列). (6认同)

Ste*_*ski 20

这依赖于实现细节,即排序的可迭代的任何排列都按排序顺序排除,除非它们是先前排列的重复.

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
Run Code Online (Sandbox Code Playgroud)

('a', 'a')
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'b')
('b', 'c')
('c', 'a')
('c', 'b')
('c', 'c')
Run Code Online (Sandbox Code Playgroud)


Min*_*ark 13

与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)]
Run Code Online (Sandbox Code Playgroud)

它通过设置第一个元素(遍历所有唯一元素)递归工作,并迭代所有剩余元素的排列.

让我们通过unique_permutations(1,2,3,1)看它是如何工作的:

  • unique_elements 是1,2,3
  • 让我们迭代它们:first_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,3,1),(2,3,1,1)...和得到的结果.
  • 最后,我们对first_element= 3 做同样的事情.


Pau*_*bel 12

您可以尝试使用set:

>>> list(itertools.permutations(set([1,1,2,2])))
[(1, 2), (2, 1)]
Run Code Online (Sandbox Code Playgroud)

设置的调用删除了重复项

  • set(permutations(somelist))!= permutations(set(somelist)) (29认同)
  • 他可能需要列表(set(itertools.permutations([1,1,2,2]))) (6认同)
  • 或者在Python 3+或Python 2.7中的`list(itertools.permutations({1,1,2,2}))`,因为存在set literals.虽然如果他不使用文字值,他仍然只是使用`set()`.而且@ralu:再看看这个问题,之后过滤会很费劲. (2认同)
  • @JAB:嗯,这需要很长时间才能超过12个值......我真正想要的是``itertools.product((0,1,'x'),repeat = X)``但是我需要先处理几个'x'的值(排序不合适,因为它生成一个列表并使用太多内存). (2认同)

Lit*_*oys 9

这是我的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])
Run Code Online (Sandbox Code Playgroud)

---结果----

[[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]]
Run Code Online (Sandbox Code Playgroud)

  • 我很好奇,这门课有什么价值吗?为什么这不只是一个函数? (2认同)

pyl*_*ang 5

天真的方法可能是采用一组排列:

list(set(it.permutations([1, 1, 1])))
# [(1, 1, 1)]
Run Code Online (Sandbox Code Playgroud)

但是,该技术浪费了计算重复排列并丢弃它们的过程。一种更有效的方法是more_itertools.distinct_permutations使用第三方工具

import itertools as it

import more_itertools as mit


list(mit.distinct_permutations([1, 1, 1]))
# [(1, 1, 1)]
Run Code Online (Sandbox Code Playgroud)

性能

使用更大的迭代器,我们将比较幼稚和第三方技术的性能。

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
Run Code Online (Sandbox Code Playgroud)

我们看到more_itertools.distinct_permutations速度快了一个数量级。


细节

从源头来看,递归算法(如接受的答案所示)用于计算不同的排列,从而避免了浪费的计算。请参阅源代码以获取更多详细信息。