Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何计算我可以用多少种不同的方式在python中对列表排序_Python_Probability - Fatal编程技术网

如何计算我可以用多少种不同的方式在python中对列表排序

如何计算我可以用多少种不同的方式在python中对列表排序,python,probability,Python,Probability,我对如何做到这一点有点困惑,我知道这可能也需要一些概率知识(我缺乏) 我如何计算有多少种方式,并获得所有的可能性,有多少种方式我可以订购一个列表 例如,如果我有lst=[“a”、“a”、“a”、“b”、“b”、“b”],我可以用多少种方式来订购此产品/如何获得所有可能的组合?我一直在查看itertools,但没有找到相关内容。您可以使用permutations()获取所有排列,并使用set()删除重复项: >>> from itertools import permutatio

我对如何做到这一点有点困惑,我知道这可能也需要一些概率知识(我缺乏)

我如何计算有多少种方式,并获得所有的可能性,有多少种方式我可以订购一个列表


例如,如果我有
lst=[“a”、“a”、“a”、“b”、“b”、“b”]
,我可以用多少种方式来订购此产品/如何获得所有可能的组合?我一直在查看
itertools
,但没有找到相关内容。

您可以使用
permutations()
获取所有排列,并使用
set()
删除重复项:

>>> from itertools import permutations
>>> set(permutations(lst))
{('b', 'a', 'b', 'a', 'a', 'a', 'b'), ('b', 'a', 'a', 'b', 'a', 'a', 'b'), ('b', 'a', 'a', 'b', 'b', 'a', 'a'), ('a', 'a', 'b', 'b', 'a', 'a', 'b'), ('a', 'a', 'b', 'a', 'b', 'b', 'a'), ('b', 'b', 'a', 'b', 'a', 'a', 'a'), ('b', 'a', 'a', 'a', 'b', 'a', 'b'), ('b', 'a', 'b', 'a', 'b', 'a', 'a'), ('b', 'b', 'a', 'a', 'b', 'a', 'a'), ('b', 'b', 'b', 'a', 'a', 'a', 'a'), ('a', 'a', 'a', 'b', 'a', 'b', 'b'), ('a', 'a', 'b', 'b', 'b', 'a', 'a'), ('a', 'a', 'a', 'b', 'b', 'b', 'a'), ('a', 'b', 'b', 'a', 'a', 'b', 'a'), ('b', 'a', 'b', 'b', 'a', 'a', 'a'), ('a', 'b', 'b', 'b', 'a', 'a', 'a'), ('a', 'b', 'a', 'a', 'a', 'b', 'b'), ('a', 'b', 'a', 'b', 'a', 'b', 'a'), ('a', 'b', 'b', 'a', 'a', 'a', 'b'), ('a', 'b', 'b', 'a', 'b', 'a', 'a'), ('a', 'a', 'b', 'a', 'b', 'a', 'b'), ('a', 'b', 'a', 'b', 'b', 'a', 'a'), ('b', 'b', 'a', 'a', 'a', 'b', 'a'), ('a', 'a', 'b', 'a', 'a', 'b', 'b'), ('a', 'a', 'a', 'a', 'b', 'b', 'b'), ('b', 'a', 'b', 'a', 'a', 'b', 'a'), ('b', 'b', 'a', 'a', 'a', 'a', 'b'), ('a', 'b', 'a', 'a', 'b', 'b', 'a'), ('b', 'a', 'a', 'b', 'a', 'b', 'a'), ('a', 'a', 'a', 'b', 'b', 'a', 'b'), ('a', 'b', 'a', 'a', 'b', 'a', 'b'), ('a', 'a', 'b', 'b', 'a', 'b', 'a'), ('a', 'b', 'a', 'b', 'a', 'a', 'b'), ('b', 'a', 'a', 'a', 'a', 'b', 'b'), ('b', 'a', 'a', 'a', 'b', 'b', 'a')}
>>> 
请注意,他的方法不是一种优化方法,因为它首先计算所有置换,尽管它返回一个迭代器,并且不会将所有置换都存储在内存中,但这仍然不是最好的方法,如果您处理的是非大型数据集,这就很好了


如果您想使用优化的方法,您可以自定义
置换
的等效函数。

正如Kasramvd提到的,使用
itertools。置换
不是生成包含重复元素的列表置换的有效方法。示例数据有7个元素,因此
itertools.permutations
生成7!=5040个排列,但只有35个=唯一排列

幸运的是,14世纪的印度数学家纳拉亚娜·潘迪塔(Narayana Pandita)提出了一种古老的排列算法,该算法以字典顺序生成排列,能够优雅地处理重复的元素。下面是一个描述(来源于文章),展示了该算法如何从当前置换生成下一个置换

  • 求最大索引j,使a[j]
  • 求出大于j的最大索引k,使a[j]
  • 将a[j]的值与a[k]的值交换
  • 从a[j+1]到并包括最终元素a[n]的顺序颠倒
  • 这是一个实现该算法的生成器函数。为了得到所有的排列,输入列表必须按字典顺序按升序排序

    def lexico_permute(a):
        a = list(a)
        yield a
        n = len(a) - 1
        while True:
            for j in range(n-1, -1, -1):
                if a[j] < a[j + 1]:
                    break
            else:
                return
    
            v = a[j]
            for k in range(n, j, -1):
                if v < a[k]:
                    break
    
            a[j], a[k] = a[k], a[j]
            a[j+1:] = a[j+1:][::-1]
            yield a
    
    # Test
    lst = ["a", "a", "a", "a", "b", "b", "b"]
    
    for i, u in enumerate(lexico_permute(lst), 1):
        print(i, u)
    
    FWIW,对于问题中给出的列表,该代码比使用
    set(排列(lst))
    快8倍左右;对于较大的输入列表,可以节省更多的时间


    lexico\u permute
    最初从输入序列(也可以是元组、字符串等)生成一个新列表。然后,它生成新列表,将其推进到下一个置换,然后再次生成相同的列表。因此,如果您只是将其输出附加到一个空列表,那么您将得到一个列表列表,该列表仅包含对同一列表的多个引用。这通常不是很有用。:)

    解决这个问题的简单方法是附加一个由
    lexico\u permute
    生成的列表副本,例如

    all_perms = []
    for u in lexico_permute(lst):
        all_perms.append(u[:])
    
    或作为列表:

    all_perms = [u[:] for u in lexico_permute(lst)]
    
    或者,将
    lexico\u permute
    中的两个yield语句更改为

    yield a[:]
    
    然后你就可以做了

    all_perms = list(lexico_permute(lst))
    

    听起来你只是在计算可区分排列的数量,而不是生成它们

    如果有一种类型的n1个不可区分元素,另一种类型的n2个不可区分元素,直到最后一种类型的nk个元素,那么集合的不可区分排列数的公式为:

    要在Python中计算此值,我们可以执行以下操作:

    from collections import Counter
    from math import factorial
    from functools import reduce
    import operator    
    
    
    def unique_permutations(lst):
        c = Counter(lst)
        return factorial(len(lst)) // reduce(operator.mul, map(factorial, c.values()))
    
    以下是输出:

    >>> unique_permutations(["a", "a", "a", "a", "b", "b", "b"])
    35
    

    排序数:
    数学阶乘(len(lst))
    (函数)。可能的排列顺序:
    itertools.permutations(lst)
    @sascha:该公式不考虑重复元素。@PM2Ring他对该要求做了任何声明吗?@sascha没有明确说明,但从上下文来看,OP不希望重复排列。否则,他将只使用
    itertools.permutations
    。值得注意的是,随着列表变长,这将需要大量内存来同时存储所有可能的组合。@Josh不,permutations返回一个迭代器对象,但这仍然不是一种优化方法,至少在运行时方面是如此。但这是一个快速的方法<代码>置换确实返回迭代器对象,但将其转换为集合会将迭代器返回的每个元素复制到内存中。@Josh迭代器是一次性的可重用对象,因此当您将迭代器转换为类似集合或列表的可重用对象时,python会将项目逐个传递到目标对象。它实际上相当于
    set(迭代器中的i代表i)
    ,因此我们不会同时在内存中存储所有这些重复项,因为
    set()
    不存储它们。@Josh:也许你误解了Kasra。当然,集合存储了所有的唯一项,但在任何时候都不会存储在内存中的所有排列。因此,使用OP的
    lst
    置换
    生成器生成5040个元组,这些元组在生成时一次添加一个到集合中。任何重复的都会被拒绝,所以集合中包含的元组不会超过35个。感谢您提供的伟大的备选答案。我有一个简短的(可能是愚蠢的)问题。在最后一行中,我没有打印(i,u),而是将它们附加到一个空列表中,这样我就可以得到一个列表列表。然而,一旦我这样做了,我所做的就是得到原始“lst”变量的重复。在您的示例中,您知道有什么东西可以阻止追加正常发生吗?@tzhu10对此表示抱歉。我很快会在回答中添加更多信息。
    >>> unique_permutations(["a", "a", "a", "a", "b", "b", "b"])
    35