提问者:小点点

获取排列计数


我在寻找一种算法,它给我元素的排列计数1…… n。如果我定义循环长度。

例如n:=4

1,1,1,1 -

1,1,2 -

2,2 -

1,3 -

4 -

如何计算由循环长度组成的给定集合的排列计数?迭代所有排列不是一个选项。


共2个答案

匿名用户

对于给定的循环类型,我们可以通过写下列表1,…, n的排列来产生具有该循环类型的排列,然后根据循环类型的长度将其适当地括起来,以获得以循环表示法编写的排列。

例如,如果我们想要循环类型(3,2,2),那么排列1,2,3,4,5,6,7被括起来为(1 2 3)(4 5)(6 7),而5,1,6,2,4,3,7给出(5 1 6)(2 4)(3 7)

很明显,我们可以通过这种方式获得循环类型(3,2,2)的所有排列,但也很明显,我们可以通过多种不同的方式获得每个排列。多计有两个原因:首先,我们可以对任何循环进行循环移位:(5 1 6)(2 4)(3 7)(1 6 5)(2 4)(3 7)(6 5 1)(2 4)(3 7)是相同的排列。其次,相同长度的循环可以任意排列:(5 1 6)(2 4)(3 7)(5 1 6)(3 7)(2 4)是相同的排列。稍微思考一下应该会让你相信这些是多计的唯一可能原因。

为了解释这两个多计的原因,我们将排列总数除以(a)循环长度的乘积,以及(b)任何给定循环长度的循环数的阶乘。在(3,2,2)的情况下:我们将(a)除以3×2×2,将(b)除以2!,因为有两个长度为2的循环。

由于这是Stack Overflow,这里有一些Python代码:

from collections import Counter
from math import factorial

def count_cycle_type(p):
    """Number of permutations with a given cycle type."""

    count = factorial(sum(p))
    for cycle_length, ncycles in Counter(p).items():
        count //= cycle_length ** ncycles * factorial(ncycles)
    return count

示例:

>>> count_cycle_type((2, 2))
3
>>> count_cycle_type((3, 2, 2))
210

为了仔细检查正确性,我们可以添加给定长度n的所有循环类型的计数,并检查我们是否得到n!。循环类型是n的分区。我们可以通过递归算法相当简单地计算它们。这里有一些代码可以做到这一点。分区是我们想要的函数;bounded_partitions是一个助手。

def bounded_partitions(n, k):
    """Generate partitions of n with largest element <= k."""
    if k == 0:
        if n == 0:
            yield ()
    else:
        if n >= k:
            for c in bounded_partitions(n - k, k):
                yield (k,) + c
        yield from bounded_partitions(n, k - 1)


def partitions(n):
    """Generate partitions of n."""
    return bounded_partitions(n, n)

示例:

>>> for partition in partitions(5): print(partition)
... 
(5,)
(4, 1)
(3, 2)
(3, 1, 1)
(2, 2, 1)
(2, 1, 1, 1)
(1, 1, 1, 1, 1)

这是仔细检查:所有循环类型的总和,对于总长度56720。我们得到5!6!7!20!的预期结果。

>>> sum(count_cycle_type(p) for p in partitions(5))
120
>>> sum(count_cycle_type(p) for p in partitions(6))
720
>>> sum(count_cycle_type(p) for p in partitions(7))
5040
>>> sum(count_cycle_type(p) for p in partitions(20))
2432902008176640000
>>> factorial(20)
2432902008176640000

匿名用户

这可以分解为:

  1. 将元素划分到与每个不同循环大小所需的元素计数相匹配的桶中的方法的数量;
  2. 乘以对于每个不同的循环大小,将元素均匀划分为所需循环数的唯一方法的数量;
  3. 乘以每个循环的不同循环顺序的数量

1:对于桶大小s1… sk,结果为n!/(s1!*…*sk!)

2:对于包含m个必须划分为c个循环的元素的桶,有m!/((m/c)!c*c!)方式

3:对于包含m个元素的循环,有(m-1)!如果m