计算字母表的第n个6字符排列

时间:2014-02-22 16:10:36

标签: python algorithm combinatorics

我一直在研究试图找出解决这个问题的方法。如果需要的话,我很乐意为某人咨询时间来解决这个问题。

我目前正在使用Python itertools生成32个字符的6个字符排列。通过以下命令:

gen = itertools.permutations('ABCDEFGHJKLMNPQRSTUVWXYZ23456789',6) 

从文档中,这个函数产生“r-length元组,所有可能的排序,没有重复的元素”。

您可以使用该库通过以下命令获取结果排列的一部分(此示例获取前10个排列,0-10:

gen2 = itertools.islice(gen,0,10)

当迭代结果gen2时,我得到了我想要的东西:

('A', 'B', 'C', 'D', 'E', 'F')
('A', 'B', 'C', 'D', 'E', 'G')
('A', 'B', 'C', 'D', 'E', 'H')
('A', 'B', 'C', 'D', 'E', 'J')
('A', 'B', 'C', 'D', 'E', 'K')
('A', 'B', 'C', 'D', 'E', 'L')
('A', 'B', 'C', 'D', 'E', 'M')
('A', 'B', 'C', 'D', 'E', 'N')
('A', 'B', 'C', 'D', 'E', 'P')
('A', 'B', 'C', 'D', 'E', 'Q')

这很好,但我真正的愿望是能够选择任意排列并从排列列表中获取它(无需存储所有可能的排列值)。如果我的计算是正确的,当生成上面列出的6个字母序列时,有652,458,240种可能的组合。所以我希望能够做一些像抓住10,353,345排列的东西。问题是如果你使用上面的islice函数来获取这个排列,它必须迭代整个排列集,最多10,353,345个元素,然后再返回给你。你可以想象,这是非常低效的,需要很长时间才能返回。

我的问题是,实现所需计算的算法是什么?我已经对阶乘分解和基数n转换做了大量研究,但是无法找到解释如何实现接近我想要的东西的任何东西,或者我可以修改的算法来实现这个结果。

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:2)

您正在寻找的内容在组合算法中称为unrank。以固定顺序考虑集合S的元素列表,unrank_S(i)返回列表的i - 元素而不计算列表。因此,S此处为Perm(n, k):所有k的列表 - 一组大小n的排列。如您所知,此集的大小为n!/k!。一种方法是使用Factoradic numbers

这是python中的一个unrank算法:

def factorial(n):
    if n == 0: return 1
    return n*factorial(n-1)

def unrank(S, k, i):
    S = list(S)   # make a copy to avoid destroying the list
    n = len(S)
    nb = factorial(n) // factorial(n-k)
    if i >= nb:
        raise IndexError
    res = []
    while k > 0:
        nb = nb // n
        pos = i // nb   # the factoradic digits
        i = i % nb      # the remaining digits
        res.append(S[pos])
        del S[pos]
        k = k-1
        n = n-1
    return res

然后

[unrank(range(5), 2, i) for i in range(20)]
 [[0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 2], [1, 3], [1, 4], [2, 0], [2, 1], [2, 3], [2, 4], [3, 0], [3, 1], [3, 2], [3, 4], [4, 0], [4, 1], [4, 2], [4, 3]]

unrank(list('ABCDEFGHJKLMNPQRSTUVWXYZ23456789'),6, 128347238)\
['G', 'L', 'E', 'H', 'T', 'R']

当然,您可能希望使用更好的方法计算阶乘,甚至将其缓存在预先计算的数组中,以避免重新计算它。

答案 1 :(得分:0)

我没有太多时间给你完整的解决方案但是跟随想法可以提供一些思路。

您需要一次性 6 字符找到 N th 排列。
让我们修复第一个角色。然后还有25个其他角色 剩余字符的排列总数为 P = 25 C 5 * 5!

因此,当 A 作为第一个字符时,您可以使用 P 排列。如果 P 小于 N ,则 A 不能位于第一位。

现在将 B 保留在第一位,并且在第一位置直到 B 的排列总数 2 * P

假设您在第一个位置保留 K th 字符,以便直到 K th 字符的排列总数是 K * P K * P 小于 N ,并保持 K + 1 th 字符,(K + 1)* P ,超过 N 。所以你需要的字符串首先需要 K + 1 th 字符。

所以你必须找到 N-K * P 剩余的排列。剩下25个字符和5个地方。 因此,同样的问题减少到1个字符,减少1个位置,并且找到更多的排列 因此,所有地方都以类似的方式解决。