第k个排列的第i个元素

时间:2015-02-15 07:01:03

标签: python algorithm complexity-theory permutation combinatorics

是否有快速算法来计算序列0..n-1的第k个排列(0 <= i < n)的第i个元素(0 <= k < n!)

可以选择排列的任何顺序,它不必是词典。有些算法在k中构建O(n) - 排列(见下文)。但是这里不需要完整的排列,只需要它的第i个元素。是否有比O(n)更好的算法?

是否存在空间复杂度小于O(n)的算法?

有些算法通过处理大小为k的数组(见下文)来构造n - 排列,但对于大n,空间要求可能不合需要。是否存在需要较少空间的算法,尤其是当仅需要i个元素时?


使用时间空间复杂度k来构造序列0..n-1的{​​{1}} - 排列的算法:

O(n)

来源:http://webhome.cs.uvic.ca/~ruskey/Publications/RankPerm/MyrvoldRuskey.pdf

2 个答案:

答案 0 :(得分:1)

你可能无法得到O(n)时间或空间中n个元素的第k个排列的第i个数字,因为表示数字k本身需要O(log(n!))= O(n log) n)位,对它的任何操作都有相应的时间复杂度。

答案 1 :(得分:1)

jkff说的是什么。你可以像你发布的那样修改一个算法,只返回第k个排列的第i个元素,但你不会节省很多时间(或空间),你肯定不会减少Big-O基本算法的复杂性。

您发布的无序排列代码实际上并不适合修改,因为它必须遍历执行其交换的所有元素,并且确定是否可能破坏它是很痛苦的早早离开了。

但是,有一个类似的算法可以产生有序排列,并且可以提前突破那个,但你仍然需要执行i内部循环来获得第i个元素。第k个排列。

我已将此算法实现为一个类,只是为了保持它使用的各种常量整洁。下面的代码产生完整的排列,但是应该很容易修改以返回第i个元素。

#!/usr/bin/env python

''' Ordered permutations using factorial base counting 

    Written by PM 2Ring 2015.02.15
    Derived from C code written 2003.02.13
'''

from math import factorial

class Permuter(object):
    ''' A class for making ordered permutations, one by one '''
    def __init__(self, seq):
        self.seq = list(seq)
        self.size = len(seq)
        self.base = factorial(self.size - 1)
        self.fac = self.size * self.base

    def perm(self, k):
        ''' Build kth ordered permutation of seq '''
        seq = self.seq[:]
        p = []
        base = self.base
        for j in xrange(self.size - 1, 0, -1):
            q, k = divmod(k, base)
            p.append(seq.pop(q))
            base //= j
        p.append(seq[0])
        return p


def test(seq):
    permuter = Permuter(seq)
    for k in xrange(permuter.fac):
        print '%2d: %s' % (k, ''.join(permuter.perm(k)))


if __name__ == '__main__':
    test('abcd')

这个算法比无序置换制造者有更多的开销:它需要提前计算阶乘,当然阶乘非常快。而且,每个内环需要一个额外的分区。因此,一旦找到第i个元素,从内循环中拯救的时间可能会被这些开销所抵消。


FWIW,你问题中的代码还有改进的余地。特别是,k /= n应写为k //= n以确保使用整数除法;你的代码在Python 2上工作正常,但在Python 3上没有。但是,由于我们需要商和余数,所以使用内置的divmod()函数是有意义的。此外,通过重新组织一些东西,我们可以避免n - 1

的多次计算
#!/usr/bin/env python

def kth_permutation(n, k):
    p = range(n)
    while n:
        k, j = divmod(k, n)
        n -= 1
        p[n], p[j] = p[j], p[n]
    return p

def test(n):
    last = range(n)
    k = 0
    while True:
        p = kth_permutation(n, k)
        print k, p
        if p == last:
            break
        k += 1

test(3)

<强>输出

0 [1, 2, 0]
1 [2, 0, 1]
2 [1, 0, 2]
3 [2, 1, 0]
4 [0, 2, 1]
5 [0, 1, 2]