【问题标题】:Computing the nth 6 character permutation of an alphabet计算字母表的第 n 个 6 字符排列
【发布时间】:2023-03-13 08:51:01
【问题描述】:

我已经研究了好几天,试图找出解决这个问题的方法。如果需要,我很乐意花钱请人咨询时间来解决这个问题。

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

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

从文档中,这个函数产生“r-length tuples, all possible orderings, no repeat elements”。

您可以使用该库通过以下命令抓取结果排列的一部分(本示例抓取前 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 转换进行了大量研究,但是找不到任何解释如何实现接近我想要的东西的东西或我可以修改以实现此结果的算法。

任何帮助将不胜感激!

【问题讨论】:

  • @jonrsharpe OP 似乎已经知道了。
  • 这显然不是重复的。 OP 知道stackoverflow.com/questions/12007820/… 中提出的解决方案,但由于效率原因,它完全不适用于他的问题。这可能需要数年时间。

标签: python algorithm combinatorics


【解决方案1】:

您正在查找的内容在组合算法中称为unrank。考虑按固定顺序排列的集合 S 的元素列表,unrank_S(i) 返回列表的第 i 个元素而不计算列表。所以你的SPerm(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']

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

【讨论】:

    【解决方案2】:

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

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

    所以以 A 作为第一个字符,您可以有 P 排列。如果 P 小于 N,则 A 不可能排在第一位。

    现在保持 B 排在第一位,直到 B 排在第一位的排列总数为 2*P

    假设您将 Kth 字符放在首位,以便排列总数直到 Kth 字符是K*P,其中K*P小于N,并且在保持K+1th 字符 (K+1)*P 超过 N。所以你需要的字符串首先需要有 K+1th 字符。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多