如何直接获得序列的排列元素(没有任何递归)?

假设我们有一个字符串“ABCD”,我们想要从字符串的第n个排列中检索第i个位置的字母.

在这个例子中,我知道有factorial(4)= 24个排列,并且可以使用itertools.permutations轻松检索列表,这将给出:

[‘ABCD’, ‘ABDC’, ‘ACBD’, ‘ACDB’, ‘ADBC’, ‘ADCB’, ‘BACD’, ‘BADC’, ‘BCAD’, ‘BCDA’, ‘BDAC’, ‘BDCA’, ‘CABD’, ‘CADB’, ‘CBAD’, ‘CBDA’, ‘CDAB’, ‘CDBA’, ‘DABC’, ‘DACB’, ‘DBAC’, ‘DBCA’, ‘DCAB’, ‘DCBA’]

所以我正在寻找的函数应该返回:

f(0, n) == [‘A’, ‘A’, ‘A’, ‘A’, ‘A’, ‘A’, ‘B’, ‘B’, ‘B’, ‘B’, ‘B’, ‘B’, ‘C’, ‘C’, ‘C’, ‘C’, ‘C’, ‘C’, ‘D’, ‘D’, ‘D’, ‘D’, ‘D’, ‘D’][n]

f(1, n) == [‘B’, ‘B’, ‘C’, ‘C’, ‘D’, ‘D’, ‘A’, ‘A’, ‘C’, ‘C’, ‘D’, ‘D’, ‘A’, ‘A’, ‘B’, ‘B’, ‘D’, ‘D’, ‘A’, ‘A’, ‘B’, ‘B’, ‘C’, ‘C’][n]

f(2, n) == [‘C’, ‘D’, ‘B’, ‘D’, ‘B’, ‘C’, ‘C’, ‘D’, ‘A’, ‘D’, ‘A’, ‘C’, ‘B’, ‘D’, ‘A’, ‘D’, ‘A’, ‘B’, ‘B’, ‘C’, ‘A’, ‘C’, ‘A’, ‘B’][n]

f(3, n) == [‘D’, ‘C’, ‘D’, ‘B’, ‘C’, ‘B’, ‘D’, ‘C’, ‘D’, ‘A’, ‘C’, ‘A’, ‘D’, ‘B’, ‘D’, ‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘C’, ‘A’, ‘B’, ‘A’][n]

对于i == 0来说很容易,我们有f(0,n)==“ABCD”[n // 6]但是当我增加时找到一个模式越来越复杂.

我根本不关心排列是如何排序的,所以可能很容易找到i的每个值的常见模式……

我打算使用一组256个元素和阶乘(256)排列,因此计算排列不是一种选择.

编辑:我已经有了这个函数,但它太慢了,我想知道是否可以使用简单的公式找到一些等效的结果,例如使用按位运算…

编辑-2:感谢@rici,这是目前最好的解决方案:

f = [factorial(i) for i in range(256)]

def _getElt(k, i):
    """
    Returns the <i>th element of the <k>th permutation of 0..255
    """
    table = list(range(256))

    for j in range(255, 254-i, -1):
        r, k = divmod(k, f[j])
        perm[j], perm[r] = perm[r], perm[j] 
    return perm[255 - i]

编辑-3:这是使用多项式近似来重建置换的另一种方法,因此问题的不同公式可以是“如何为第n个置换重建多项式的第i个系数?”.

对于N = 4,这是n,置换,多项式系数(以及最终从多项式系数的置换重建)的列表:

0 [0, 1, 2, 3] [Fraction(0, 1), Fraction(1, 1), Fraction(0, 1), Fraction(0, 1)] [0, 1, 2, 3]

1 [0, 1, 3, 2] [Fraction(0, 1), Fraction(-3, 4), Fraction(5, 2), Fraction(-2, 3)] [0, 1, 3, 2]

2 [0, 2, 1, 3] [Fraction(0, 1), Fraction(11, 2), Fraction(-9, 2), Fraction(1, 1)] [0, 2, 1, 3]

3 [0, 2, 3, 1] [Fraction(0, 1), Fraction(7, 4), Fraction(1, 2), Fraction(-1, 3)] [0, 2, 3, 1]

4 [0, 3, 1, 2] [Fraction(0, 1), Fraction(33, 4), Fraction(-13, 2), Fraction(4, 3)] [0, 3, 1, 2]

5 [0, 3, 2, 1] [Fraction(0, 1), Fraction(19, 3), Fraction(-4, 1), Fraction(2, 3)] [0, 3, 2, 1]

6 [1, 0, 2, 3] [Fraction(1, 1), Fraction(-15, 4), Fraction(7, 2), Fraction(-2, 3)] [1, 0, 2, 3]

7 [1, 0, 3, 2] [Fraction(1, 1), Fraction(-17, 3), Fraction(6, 1), Fraction(-4, 3)] [1, 0, 3, 2]

8 [1, 2, 0, 3] [Fraction(1, 1), Fraction(21, 4), Fraction(-11, 2), Fraction(4, 3)] [1, 2, 0, 3]

9 [1, 2, 3, 0] [Fraction(1, 1), Fraction(-1, 3), Fraction(2, 1), Fraction(-2, 3)] [1, 2, 3, 0]

10 [1, 3, 0, 2] [Fraction(1, 1), Fraction(31, 4), Fraction(-15, 2), Fraction(5, 3)] [1, 3, 0, 2]

11 [1, 3, 2, 0] [Fraction(1, 1), Fraction(17, 4), Fraction(-5, 2), Fraction(1, 3)] [1, 3, 2, 0]

12 [2, 0, 1, 3] [Fraction(2, 1), Fraction(-17, 4), Fraction(5, 2), Fraction(-1, 3)] [2, 0, 1, 3]

13 [2, 0, 3, 1] [Fraction(2, 1), Fraction(-31, 4), Fraction(15, 2), Fraction(-5, 3)] [2, 0, 3, 1]

14 [2, 1, 0, 3] [Fraction(2, 1), Fraction(1, 3), Fraction(-2, 1), Fraction(2, 3)] [2, 1, 0, 3]

15 [2, 1, 3, 0] [Fraction(2, 1), Fraction(-21, 4), Fraction(11, 2), Fraction(-4, 3)] [2, 1, 3, 0]

16 [2, 3, 0, 1] [Fraction(2, 1), Fraction(17, 3), Fraction(-6, 1), Fraction(4, 3)] [2, 3, 0, 1]

17 [2, 3, 1, 0] [Fraction(2, 1), Fraction(15, 4), Fraction(-7, 2), Fraction(2, 3)] [2, 3, 1, 0]

18 [3, 0, 1, 2] [Fraction(3, 1), Fraction(-19, 3), Fraction(4, 1), Fraction(-2, 3)] [3, 0, 1, 2]

19 [3, 0, 2, 1] [Fraction(3, 1), Fraction(-33, 4), Fraction(13, 2), Fraction(-4, 3)] [3, 0, 2, 1]

20 [3, 1, 0, 2] [Fraction(3, 1), Fraction(-7, 4), Fraction(-1, 2), Fraction(1, 3)] [3, 1, 0, 2]

21 [3, 1, 2, 0] [Fraction(3, 1), Fraction(-11, 2), Fraction(9, 2), Fraction(-1, 1)] [3, 1, 2, 0]

22 [3, 2, 0, 1] [Fraction(3, 1), Fraction(3, 4), Fraction(-5, 2), Fraction(2, 3)] [3, 2, 0, 1]

23 [3, 2, 1, 0] [Fraction(3, 1), Fraction(-1, 1), Fraction(0, 1), Fraction(0, 1)] [3, 2, 1, 0]

我们可以清楚地看到有一个对称性:coefs [i] = [3 – coefs [23-i] [0]] [-c对于coefs [23-i] [1:]中的c]所以它是一种方式探索,但我不知道这是可能的.

最佳答案 您可以通过重复的欧几里德除法(商和余数,也称为divmod)找到第n个排列,并跟踪您选择的字母.然后,您可以从该排列中选择第i个字母.

设S是初始字符串的副本,L是该字符串的长度,P是排列的数量(L!). T将是S的第n个排列,逐步构建.
示例:S =“ABCD”,L = 4,P = 24.我们以n = 15为例. T应该是“CBDA”的结尾.

设定P = P / L.计算divmod(n,P),令q为商(n / P),r为余数(n%P).从S中删除第q个字母并将其附加到T.设置n = r,递减L,并重复直到L = 0.
例:
1)P = 24/4 = 6,q = 15/6 = 2,r = 15%6 = 3,S =“ABD”,T =“C”,n = r = 3,L = 3.
2)P = 6/3 = 2,q = 3/2 = 1,r = 3%2 = 1,S =“AD”,T =“CB”,n = r = 1,L = 2.
3)P = 2/2 = 1,q = 1/1 = 1,r = 1%1 = 0,S =“A”,T =“CBD”,n = r = 0,L = 1.
4)P = 1/1 = 1,q = 0/1 = 0,r = 0%1 = 0,S =“”,T =“CBDA”,n = r = 0,L = 0.

由于你只需要第i个字母,所以每当T的长度等于i 1并取最后一个字母时,你就可以停止.

我不会尝试用Python编写代码,因为自从我触及Python以来已经太久了,但是here is a demo in C++.

点赞