如何生成 a[i] != i 的排列?
假设我有一个整数数组 int a[] = {0, 1, ... N-1}
,其中 N
是 a< 的大小/代码>。现在我需要为所有
0 <= i
生成
a
的所有排列 a[i] != i
。 N。你会怎么做?
Suppose I have an array of integers int a[] = {0, 1, ... N-1}
, where N
is the size of a
. Now I need to generate all permutations of a
s that a[i] != i
for all 0 <= i < N
. How would you do that?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
下面是一些 C++ 实现的算法,该算法基于递归的双射证明,
其中
!n
是n
项的混乱数量。在我的机器上,我得到的
恕我直言是洗涤。可能双方都需要进行改进(例如,使用仅扫描已更改元素的混乱测试重新实现
next_permutation
);这留给读者作为练习。Here's some C++ implementing an algorithm based on a bijective proof of the recurrence
where
!n
is the number of derangements ofn
items.On my machine, I get
which IMHO is a wash. Probably there are improvements to be made on both sides (e.g., reimplement
next_permutation
with the derangement test that scans only the elements that changed); that's left as an exercise to the reader.如果您有权访问 C++ STL,请使用 next_permutation,并对[i] != i 在
do-while
循环中。If you have access to C++ STL, use next_permutation, and do an additional check of a[i] != i in a
do-while
loop.如果您想避免其他人建议的过滤方法(按字典顺序生成排列并跳过具有固定点的排列),那么您应该基于循环符号而不是单行符号(符号讨论)。
n
排列的循环类型是n
的划分,即总和为n
的正整数的弱递减序列。排列没有不动点的条件等价于其循环类型没有1
。例如,如果n=5
,则可能的循环类型为其中,只有
5
和3,2
对于此问题有效因为所有其他都包含1
。因此,策略是生成最小部分至少为 2 的分区,然后对于每个这样的分区,生成具有该循环类型的所有排列。If you want to avoid the filter approach that others have suggested (generate the permutations in lexicographic order and skip those with fixed points), then you should generate them based on cycle notation rather than one-line notation (discussion of notation).
The cycle-type of a permutation of
n
is a partition ofn
, that is a weakly decreasing sequence of positive integers that sums ton
. The condition that a permutation has no fixed points is equivalent to its cycle-type having no1
s. For example, ifn=5
, then the possible cycle-types areOf those, only
5
and3,2
are valid for this problem since all others contain a1
. Therefore the strategy is to generate partitions with smallest part at least2
, then for each such partition, generate all permutations with that cycle-type.您正在寻找的排列称为混乱。正如其他人所观察到的,均匀随机分布的排列可以通过生成均匀随机分布的排列然后拒绝具有固定点(其中 a[i] == i)的排列来生成。拒绝方法在时间 e*n + o(n) 中运行,其中 e 是欧拉常数 2.71828... 。与 @Per 类似的替代算法的运行时间为 2*n + O(log^2 n)。然而,我能找到的最快算法,即早期拒绝算法,运行时间为 (e-1)*(n-1)。不是等待排列生成然后拒绝(或不拒绝)排列,而是在构造排列时测试固定点,从而可以尽早拒绝。这是我在 Java 中实现的早期拒绝混乱方法。
有关替代方法,请参阅我的帖子 随机播放列表,确保没有任何项目保持在同一位置。
The permutations you are looking for are called derangements. As others have observed, uniformly randomly distributed derangements can be generated by generating uniformly randomly distributed permutations and then rejecting permutations that have fixed points (where a[i] == i). The rejection method runs in time e*n + o(n) where e is Euler's constant 2.71828... . An alternative algorithm similar to @Per's runs in time 2*n + O(log^2 n). However, the fastest algorithm I've been able to find, an early rejection algorithm, runs in time (e-1)*(n-1). Instead of waiting for the permutation to be generated and then rejecting it (or not), the permutation is tested for fixed points while it is being constructed, allowing for rejection at the earliest possible moment. Here's my implementation of the early rejection method for derangements in Java.
For an alternative approach, see my post at Shuffle list, ensuring that no item remains in same position.
只是一个预感:我认为词典排列可能可以通过修改来解决这个问题。
通过将奇数和偶数元素对交换为
2,1,4,3,6,5 来重新排列数组
以最低字典顺序构造排列。然后使用标准算法,并附加一个约束,即您不能将元素1,2,3,4,5,6,...
,...i
交换到位置i
。如果数组有奇数个元素,则必须在末尾再次进行交换,以确保元素
N-1
不在位置N-1
中。Just a hunch: I think lexicographic permutation might be possible to modify to solve this.
Re-arrange the array
1,2,3,4,5,6,...
by swapping pairs of odd and even elements into2,1,4,3,6,5,...
to construct the permutation with lowest lexicographic order. Then use the standard algorithm, with the additional constraint that you cannot swap elementi
into positioni
.If the array has an odd number of elements, you will have to make another swap at the end to ensure that element
N-1
is not in positionN-1
.这是 python 中的一个小型递归方法:
运行
perm(range(1,5))
将给出以下输出:Here's a small recursive approach in python:
Running
perm(range(1,5))
will give the following output: