如何生成排列而不生成重复结果但具有固定数量的字符Python [重复]

Posted

技术标签:

【中文标题】如何生成排列而不生成重复结果但具有固定数量的字符Python [重复]【英文标题】:How to generate permutations without generating repeating results but with a fixed amount of characters Python [duplicate] 【发布时间】:2016-07-23 17:13:14 【问题描述】:

我正在尝试找出一种方法来生成具有几个重复字符但不生成重复元组的字符串的所有可能排列。

现在我正在使用itertools.permutations()。它有效,但我需要删除重复,我不能使用set() 删除重复。

我期待什么样的结果?好吧,例如,我想得到DDRR 的所有组合,itertools.permutations() 的事情是我会得到大约四次DDRR,因为itertools 看到Ds 就好像它们是不同,与Rs 相同。

list(itertools.permutations('DDRR')) 我得到:

[('D', 'D', 'R', 'R'), ('D', 'D', 'R', 'R'), ('D', 'R', 'D', 'R'), ('D', 'R', 'R', 'D'), ('D', 'R', 'D', 'R'), ('D', 'R', 'R', 'D'), ('D', 'D', 'R', 'R'), ('D', 'D', 'R', 'R'), ('D', 'R', 'D', 'R'), ('D', 'R', 'R', 'D'), ('D', 'R', 'D', 'R'), ('D', 'R', 'R', 'D'), ('R', 'D', 'D', 'R'), ('R', 'D', 'R', 'D'), ('R', 'D', 'D', 'R'), ('R', 'D', 'R', 'D'), ('R', 'R', 'D', 'D'), ('R', 'R', 'D', 'D'), ('R', 'D', 'D', 'R'), ('R', 'D', 'R', 'D'), ('R', 'D', 'D', 'R'), ('R', 'D', 'R', 'D'), ('R', 'R', 'D', 'D'), ('R', 'R', 'D', 'D')]

我想要的理想结果是:

[('D', 'R', 'R', 'D'), ('R', 'D', 'R', 'D'), ('R', 'R', 'D', 'D'), ('D', 'R', 'D', 'R'), ('D', 'D', 'R', 'R'), ('R', 'D', 'D', 'R')]

【问题讨论】:

为什么无法使用set?有什么不好的? 因为我遇到了内存错误。我正在使用非常长的字符串。 这是一种设计选择,有一些解决方法,请参阅:***.com/questions/6534430/… “非常长的字符串”不是总是会导致不切实际的大输出吗?包含两次字母的 52 个字符的字符串具有超过 10^60 种排列。 我已经在那个问题中回答了关于正确性的问题(由弗洛伊德提出)。该算法确实是正确的,但需要对初始输入进行排序。 【参考方案1】:

如果您的字符串包含大量重复字符,那么您可以使用基于组合的算法来生成排列。

基本上,这是通过选择一个字母并找到该字母的副本可以去的所有位置来实现的。通过这些可能性中的每一种,您可以找到下一个字母所在的所有位置,依此类推。

代码:

from collections import Counter
from itertools import combinations

def perms_without_reps(s):
    partitions = list(Counter(s).items())
    k = len(partitions)
    def _helper(idxset, i):
        if len(idxset) == 0:
            yield ()
            return
        for pos in combinations(idxset, partitions[i][1]):
            for res in _helper(idxset - set(pos), i+1):
                yield (pos,) + res

    n = len(s)
    for poses in _helper(set(range(n)), 0):
        out = [None] * n
        for i, pos in enumerate(poses):
            for idx in pos:
                out[idx] = partitions[i][0]
        yield out

像这样运行它:

for p in perms_without_reps('DDRR'):
    print p

两个重要说明:

这不会生成以任何特定方式排序的输出。如果要排序输出,请在k = 之前添加permutations.sort(),将_helper(idxset - set(pos), i+1) 替换为_helper(sorted(set(idxset) - set(pos)), i+1),并将_helper(set(range(n)), 0) 替换为_helper(list(range(n)), 0)。这会使函数变慢。 如果您有大量不平衡的重复次数,此功能非常有效。例如,任何基于排列的方法都将永远使用输入 'A'*100 + 'B'*2 (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB),而此方法几乎会立即完成 5151 个唯一排列。

【讨论】:

【参考方案2】:

通常每个排列算法都应该生成n!/(n-r)! 结果,但您可以决定实现一个检查重复的算法,这应该很有趣。

让我们看看这是否有帮助。

def __permutate(self, objectToPermute):
    in_list = []

    for i in range(self.__cur, len(objectToPermute)):
        in_list.append(self.__swap(self.__cur, i, objectToPermute))

    ''' At initial call, self.__cur = 0
       and self.__permSize would be the r in the permutation formula '''
    if self.__cur < self.__permSize - 1:
        self.__cur += 1

        for obj in in_list:
            self.__permutate(obj)

        self.__cur -= 1


    if self.__cur == self.__permSize -1:
        for obj in in_list:
            #this does the job
            if self.norepeat and obj in self.__perm_obj:
                continue
            self.__perm_obj.append(obj[:self.__permSize])

你一定注意到了,这是我从很久以前写的一个类中提取出来的,它是一种排列算法,我喜欢称之为 Melon 算法(别介意)。

这部分代码所做的只是递归地置换一个对象,使用了交换函数,因为它存在于类中,但您可以轻松地将其编码出来......现在主要要旨,为了避免重复所有你必须做的是确保属性self.norepeat = True 和每个重复的对象都会被跳过。如果您需要完整的课程,我很乐意分享

我需要一个反馈,以便知道你是否明白我的意思

【讨论】:

【参考方案3】:

这将是创建具有重复元素的集合的排列的标准方法:计算每个元素的出现次数(使用例如关联数组或字典),遍历字典中的元素并每次附加元素到排列,并与字典的其余部分递归。但是,对于很长的输入数组,它永远不会很快;但随后可能什么都不会。

javascript 中的代码示例;我不会说 Python;翻译应该很容易。)

function permute(set) 
    var alphabet = ;
    for (var s in set) 
        if (!alphabet[set[s]]) alphabet[set[s]] = 1;
        else ++alphabet[set[s]];
                                   // alphabet = 'a':5, 'b':2, 'r':2, 'c':1, 'd':1
    perm("", set.length);

    function perm(part, level) 
        for (var a in alphabet)    // every letter in alphabet
            if (alphabet[a])       // one or more of this letter available
                --alphabet[a];      // decrement letter count
                if (level > 1) perm(part + a, level - 1);  // recurse with rest
                else document.write(part + a + "<br>");    // deepest recursion level
                ++alphabet[a];      // restore letter count
            
        
    

permute(['a','b','r','a','c','a','d','a','b','r','a']); // 83,160 unique permutations
                                                        // instead of 39,916,800 non-
                                                        // unique ones plus filtering

【讨论】:

以上是关于如何生成排列而不生成重复结果但具有固定数量的字符Python [重复]的主要内容,如果未能解决你的问题,请参考以下文章

从 n 个元素生成长度为 r 的组合而不重复或排列的函数的时间复杂度是多少?

当数组的数量和每个数组的长度未知时生成字符组合的所有排列

一些固定数字的排列

使用 LINQ 生成排列

使用 LINQ 生成排列

生成邀请码