看了“白话算法(7) 生成全排列的几种思路(一)”和“白话算法(7) 生成全排列的几种思路(二) 康托展开”。在此,将以前本人推导的全排列算法介绍一下,和广大的网友交流一下。
以例子说明,用0、1、2、3,四个数组成全排列。
首先可以知道,这四个数组成的全排列一共有4!=24个。那么给这24个全排列编号,分别为0、1、2……23。再给定一个算法,通过编号计算出一个全排列。本文的目的就是找到这样的算法。
把所有的全排列列举出来可以发现,0在末位的有6个,1在末位的有6个,等等。
观察0在末位的六个,分别是
1、2、3、0
1、3、2、0
2、1、3、0
2、3、1、0
3、1、2、0
3、2、1、0
可以看出这6个全排列,除了末位是0外,前面三个数正好是1、2、3的全排列
再观察1在末位的六个,分别是
0、2、3、1
0、3、2、1
2、0、3、1
2、3、0、1
3、0、2、1
3、2、0、1
也可以看出这6个全排列,除了末位是1外,前面三个数正好是0、2、3的全排列
类似的,末位是2和3的6个全排列,除了末位是一样的以外,前面三个数正好是剩下的三个数的全排列。
于是该问题就可以用下面的步骤来解决。
1、根据编号确定末位数字
2、确定末位数字后,获得剩余的数字
3、对编号适当的处理,得到新的编号
4、问题演化成除了末位数字后,用新的编号和剩余的数字,计算剩余数字的全排列。
再用一个具体的例子来说明。比方说,用编号13来计算0、1、2、3的一个全排列。
1、先给这4个数字排好序。是0、1、2、3
2、计算[13/3!]+1=3,表示末位数是第3个数。注:[X]表示取X的整数部分。
3、把第3个数和第4个数(未排的最后1个数)交换。此时,数字的顺序是0、1、3、2。蓝色的表示已经排好。
4、新的编号为13 mod 3!=1
5、计算[1/2!]+1=1,表示末位数是第1个数。
6、把第1个数和第3个数(未排的最后1个数)交换。此时,数字的顺序是3、1、0、2。蓝色的表示已经排好。
7、新的编号为1 mod 2!=1
8、计算[1/1!]+1=2,表示末位数是第2个数。
9、把第2个数和第2个数(未排的最后1个数)交换。此时,数字的顺序是3、1、0、2。蓝色的表示已经排好。
10、因为只剩下一个数,所以编号13对应的全排列就是3、1、0、2
其他的编号计算方法和此一样。
后面的这个表格就是按照上面的算法得到所有的编号和全排列的关系
A(0) | A(1) | A(2) | A(3) | 编号 |
1 | 2 | 3 | 0 | 0 |
2 | 1 | 3 | 0 | 1 |
2 | 3 | 1 | 0 | 2 |
3 | 2 | 1 | 0 | 3 |
1 | 3 | 2 | 0 | 4 |
3 | 1 | 2 | 0 | 5 |
3 | 2 | 0 | 1 | 6 |
2 | 3 | 0 | 1 | 7 |
2 | 0 | 3 | 1 | 8 |
0 | 2 | 3 | 1 | 9 |
3 | 0 | 2 | 1 | 10 |
0 | 3 | 2 | 1 | 11 |
1 | 3 | 0 | 2 | 12 |
3 | 1 | 0 | 2 | 13 |
3 | 0 | 1 | 2 | 14 |
0 | 3 | 1 | 2 | 15 |
1 | 0 | 3 | 2 | 16 |
0 | 1 | 3 | 2 | 17 |
1 | 2 | 0 | 3 | 18 |
2 | 1 | 0 | 3 | 19 |
2 | 0 | 1 | 3 | 20 |
0 | 2 | 1 | 3 | 21 |
1 | 0 | 2 | 3 | 22 |
0 | 1 | 2 | 3 | 23 |
通过这样的算法,通过指定的编号就能算出一个全排列。
如果要遍历所有的全排列,则只要遍历编号就能完成。
如果要随机获得一个全排列,则随机生成一个编号,再计算出全排列就可以了。
具体的代码在我之前的文章“遍历排列的实现——VB2005”中,这里就不重复贴出了。