转自:http://www.cnblogs.com/dragonpig/archive/2010/01/21/1653680.html
http://www.notesandreviews.com/programming/algorithm-to-generate-permutations-explained
这个方法的思路在于按照从“小到大”的方式生成所有排列。假设每一个参与排列的项都是可以比较大小的,即使不很直观也总可以找到某种映射法(随便赋个数),字典序就是最左的位权重最高,向右依次比较大小。
1. 算法的初始步骤为,排成最小的序列。a1<a2<...<an (为了方便打字 假设每项唯一)
2. 然后从右向左,找到第一个 aj<aj+1,意味着aj+1>aj+2>...>an
3. 在[aj+1到an]中找到最小的ak并且aj<ak ,交换aj和ak
4. 镜像反转[aj+1到an]的元素,重复第二步直到序列最大,a1>a2>...>an
Example generating all of the permutations of 12345:
The first permutation is 12345 itself. Taking that and running through the steps described above, we can generate the next permutation 12354. Here’s how:
Step 1: Referring to step 1 above, we first need to find item list[i]. Going backwards through the list, the first number we come to that is less than the number to the right of it is 4 (4 is less than the number 5 which is to the right of it). Let’s keep a note of that by drawing a ring around the 4 and labelling its position i.
Step 2: Referring to step 2 above, we now need to find list[j]. Going backwards through the list, the first number we come to that is greater than the number we just circled in step 1 is the 5. Let’s keep a note of that by drawing a ring around the 5 and labelling its position j.
Step 3: Swapping the items at i and j, we now get 12354. (We have swapped the numbers at i and j but i and j themselves don’t move so now i points to 5 and j points to 4.
Step 4: In this step, we need to reverse the items from i+1, which is the 4 to the end of the list. Since 4 is already at the end of the list, there is nothing to do here. So the next permutation is 12354.
Taking 12354 and running through the steps again, we get 12435.
Continuing on would give the next permutation, and then the next and so on until the result is 54321.
相当直观好懂的算法,直接给出实现
[a1,a2 ... aN]
generate(int N)
{
if (N == 1) dosomething();
for (int i = 1; i <= N; i++)
{ swap(i, N); generate(N-1); swap(i, N); }
}
假设generate(N-1)能产生所有排列,那么通过和之前N-1个元素交换最后的第N个元素(或者不交换)可以产生新的排列,所产生的个数正好是N×generate(N-1),满足排列定义,所以是N!个。算法中的后一个swap是为了保证把最后一个元素换回原来位子,还原整个序列,注意所有递归操作施加在了同一个静态数组上。
或者叫做 Johnson-Trotter 算法。令人佩服的是这个算法可以追溯到17世纪的英国。当时的教堂有很多编钟,为了每次摆出不同的花样(为了my lord),需要对钟的排列作出调整。古人不容易啊要想算出5个钟120种变化已经很不容易了,而且更令人抓狂的是,这些编钟各个体形巨大、笨重,所以需要一种每次移动最少的方法。至理名言,最优秀的程序员是因为太懒了,为了“偷懒”ringers找到了好办法,后来被形式化成这个算法了(这里有相关历史介绍)
设[a1,a2 ... aN] 每一项都有向左或向右两个移动方向。
1.初始化所有移动方向向左
2.如果移动方向的值比自己小,就可移动,比如 <1 >2 <3, 3可以移动,2不可以因为3大
3.移动最大的可以动项
4.将所有比移动项大的项方向反转 重复第三步 直到不能移动为止
举个N=4的例子:
<1 <2 <3 <4
<1 <2 <4 <3
<1 <4 <2 <3
<4 <1 <2 <3
4> <1 <3 <2
<1 4> <3 <2
<1 <3 4> <2
<1 <3 <2 4>
<3 <1 <2 <4
<3 <1 <4 <2
<3 <4 <1 <2
<4 <3 <1 <2
4> 3> <2 <1
3> 4> <2 <1
3> <2 4> <1
3> <2 <1 4>
<2 3> <1 <4
<2 3> <4 <1
<2 <4 3> <1
<4 <2 3> <1
4> <2 <1 3>
<2 4> <1 3>
<2 <1 4> 3>
<2 <1 3> 4>
是对backtracking的改进,减少了第一次swap,效率渴望翻倍
[a1,a2 ... aN]
generate(int N)
{
if (N == 1) dosomething();
for (int i = 1; i <= N; i++)
{ generate(N-1); swap(N % 2 ? 1 : i, N); }
}
如果下标从零开始swap改为N % 2 ? i : 0或者N % 2 * i
该算法和index table有关,而这个方法又牵涉到cycle index是和group theory有关的东西了。