除了Fisher-Yates之外还存在什么改组算法并找到“下一个排列?”

时间:2010-08-29 17:52:27

标签: algorithm random shuffle

特别是在同一类型的一维项目集的域中,例如整数向量。

例如,假设您有一个大小为32,768的向量,其中包含0到32,767的有序整数。

我的意思是“下一个排列”是在词法排序系统中执行下一个排列。

维基百科lists two,我想知道是否还有更多(除了bogo:P)

5 个答案:

答案 0 :(得分:7)

O(N)实施 这是基于Eyal Schneider的映射Zn! - > P(n)的

def get_permutation(k, lst):
    N = len(lst)
    while N:
        next_item = k/f(N-1)
        lst[N-1], lst[next_item] = lst[next_item], lst[N-1]
        k = k - next_item*f(N-1)
        N = N-1
    return lst

通过将转换步骤与找到置换相结合,减少了他的O(N ^ 2)算法。它基本上与Fisher-Yates具有相同的形式,但在映射的下一步中将随机调用替换为随机。如果映射实际上是一个双射(我正在努力证明),那么这是一个比Fisher-Yates更好的算法,因为它只调用伪随机数发生器一次,因此效率更高。还要注意,这会返回置换的动作(N! - k)而不是置换k,但这没什么影响,因为如果k在[0,N!]上是均匀的,那么N也是如此! - k。

旧回答

这与“下一个”排列的想法有些相关。如果项目可以很好地排序,则可以在排列上构建词典排序。这允许您构建从整数到排列空间的映射。

然后找到随机排列相当​​于选择0到N之间的随机整数!并构建相应的排列。该算法与计算所讨论的集合的第n个排列一样有效(并且难以实现)。如果我们对n的选择是均匀的,那么这将简单地给出排列的统一选择。

关于排列排列的更多细节。给定一组S = {a b c d},数学家将S的排列组合作为一个组来操作。如果p是一个排列,让我们说(b a c d),那么p通过将b带到a,a到c,c到d和d到b来对S进行操作。如果q是另一种排列,请说(d b c a)然后pq首先应用q然后p获得(d a b)(c)。例如,q将d视为b,p将b视为a,以便pq将d视为a。您会看到pq有两个周期,因为它需要b到d并修复c。习惯上省略1个循环,但为了清楚起见,我将其留下了。

我们将使用群论中的一些事实。

  1. 不相交的周期通勤。 (a b)(c d)(c d)(a b)
  2. 相同
  3. 我们可以按任何循环顺序排列循环中的元素。那是(a b c) = (b c a) = (c a b)
  4. 因此,给定一个排列,对循环进行排序,以便最大的循环首先出现。当两个周期长度相同时,安排他们的项目,以便最大的(我们总是可以订购一个可数集,即使是任意的)项目首先出现。然后我们首先对周期的长度进行词典排序,然后是内容。这是有序的,因为由相同周期组成的两个排列必须是相同的排列,所以如果p > qq > p那么p = q

    该算法可以在O(N!logN!+ N!)时间内轻松执行。只是构建所有的排列(编辑:只是为了清楚,当我提出这个问题时,我的数学家戴上了帽子,无论如何它都是舌头),快速排序并找到第n个。这是一个与你提到的两个算法不同的算法。

答案 1 :(得分:5)

以下是关于如何改善 aaronasterling 的答案的想法。它避免产生所有N!排列并根据其词典顺序对它们进行排序,因此具有更好的时间复杂度。

在内部,它使用一种不寻常的排列表示,模拟选择&从缩小的数组中删除进程。例如,序列< 0,1,0>表示从[0,1,2]中删除项目#0,然后从[1,2]中删除项目#1,然后从[1]中删除项目#0所产生的排列。得到的排列是< 0,2,1>。利用该表示,第一置换将始终为< 0,0,... 0>,并且最后一个将始终为< N-1,N-2,... 0>。我将这个特殊表示称为“数组表示”。

显然,通过使用数组并在必要时缩小它,可以在O(N ^ 2)时间内将大小为N的数组表示转换为标准置换表示。

以下函数可用于在数组表示中返回{0,1,2 ...,N-1}上的Kth置换:

getPermutation(k, N) {
    while(N > 0) {
        nextItem = floor(k / (N-1)!)
        output nextItem
        k = k - nextItem * (N-1)!
        N = N - 1
    }
}

此算法在O(N ^ 2)时间内工作(由于表示转换),而不是O(N!log N)时间。

- 实施例 -

getPermutation(4,3)返回< 2,0,0>。该数组表示对应于< C,A,B>,它实际上是{A,B,C}上排列的有序列表中索引4处的排列:

ABC
ACB
BAC
BCA
CAB
CBA

答案 2 :(得分:3)

您可以调整合并排序,使其随机调整输入而不是对其进行排序。

特别是,在合并两个列表时,您可以随机选择新的head元素,而不是选择它作为最小的head元素。从第一个列表中选择元素的概率必须为n/(n+m),其中n是第一个的长度,m第二个列表的长度为此。

我在这里写了详细的解释:Random Permutations and Sorting

答案 3 :(得分:2)

另一种可能性是建立一个LFSR或PRNG,其周期等于你想要的项目数。

答案 4 :(得分:0)

从排序数组开始。选择2个随机索引,在这些索引处切换元素。重复O(n lg n)次。

您需要重复O(n lg n)次以确保分布接近均匀。 (您需要确保每个索引至少被选中一次,这是一个滚球问题。)