【问题标题】:Get permutation count获取排列计数
【发布时间】:2020-02-10 17:04:46
【问题描述】:

我正在寻找一种算法,它可以为我提供元素1....n 的排列计数。如果我定义循环长度。

例如n := 4

<Set of cycle lengths> -> permutation count

1,1,1,1 -> 1 读取 4 个长度为 1 的循环导致 1 个排列:1,2,3,4

1,1,2 -> 5 读取长度为 1 的 2 个循环和长度为 2 的 1 个循环导致 5 个排列:1,2,4,31,4,3,21,3,2,42,1,3,43,2,1,4

2,2 -> 3 读取 2 个长度为 2 的循环导致 3 个排列:2,1,4,33,4,1,24,3,2,1

1,3 -> 9 读取长度为 1 的 1 个循环和长度为 3 的 1 个循环导致 9 个排列 1,3,2,41,3,4,21,4,2,32,3,1,42,4,3,13,1,2,4 ,3,2,4,1,4,1,3,2,4,2,1,3,

4 -> 6 读取 1 个长度为 4 的循环导致 6 个排列: 2,3,4,12,4,1,33,1,4,23,4,2,14,1,2,34,3,1,2

如何计算包含循环长度的给定集合的排列计数?遍历所有排列不是一种选择。

【问题讨论】:

  • 请澄清您的问题。我不清楚你的真正意思是什么。 1,1,1,11,1,2 的值是有意义的,但我不知道之后会发生什么。
  • 是的,2,2 怎么等于 6?我完全迷路了。
  • 不知道“周期长度”是什么意思。
  • 2,2 的答案应该是 3。可能的循环集是 12:3413:2414:23
  • 是的,我用解释纠正了它

标签: algorithm math permutation combinatorics circular-permutations


【解决方案1】:

对于给定的循环类型,我们可以通过写下列表1, ..., n 的排列,然后根据循环类型中的长度将其适当地括起来,以生成具有该循环类型的排列,以获得写入的排列cycle notation.

例如,如果我们想要循环类型(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) 的情况下:我们除以3 × 2 × 2(a)和2!(b),因为有两个长度为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!。循环类型是partitionsn。我们可以通过递归算法相当简单地计算这些。这里有一些代码可以做到这一点。 partitions 是我们想要的函数; 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

【讨论】:

    【解决方案2】:

    这可以分为:

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

    1:对于存储桶大小 s1...sk,计算结果为 n!/(s1!* 。 .. * sk!)

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

    3:对于包含m个元素的循环,有(m-1)个!如果 m > 1,则不同的循环排序,否则只有 1 个排序

    【讨论】:

    • 在您的情况下,桶是什么?
    • 例如,如果循环大小是 2,2,2,3,那么有一个大小为 2 的桶必须包含 6 个元素,平均分为 3 个循环。还有一个大小为 3 的桶,它只包含一个循环的 3 个元素
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-11
    • 1970-01-01
    • 2012-01-15
    相关资源
    最近更新 更多