排列 组合 算法(一)

排列组合算法

我们都知道排列与组合的个数可以利用公式很容易的求出来,但是要是把这些排列组合的序列一一输出怎么办呢?

下面结合《组合数学》(第四版)卢开澄卢华明编著,好好总结排列与组合的算法。

一.排列的生成算法

1.1  序数法(也被称作递增进位数法)

我们都知道n个数的全排列个数是n!个。其实任何一个数都可以用阶乘的二项式来表示(严格的证明可以参考书中,连我这种菜鸟都能看懂),如

4000 = 5*6! + 3*5! + 4! + 2*3! + 2*2!  

其中{5,3,1,2,2}就是他们的系数,系数与整数一一对应。每个整数有唯一的系数和其对应。

看到这里我们也许会猜想,要是我们每个全排列的序列都对应一个系数,那该多好!

设序数   

                       (an-1,an-2,-----a2,a1         (1)

对应的规则如下:式1中第一个数an-1表示排列中数n的右(或左)端比n小的个数,将n排列的位置确定下来,接着取an-2,它表示在排列中n-1这个数在排列中的右(或左)端比n-1小的个数,依次类推。

   1234,的排列4213为例,排列42134的右方比它小的数有3位,故a3=33的右方比3小的数为0,故a2=02的右方比2小的数为1,故a1=1.故排列4213对应于序数(301),反过来从(301)可推的排列4213.此时,(301)被称作中介数。

下面是从网络上转载的资料,讲的比较详细:

例如排列839647521的中介数是72642321726......分别是排列中数字839......的右边比它小的数字个数。
    中介数是计算排列的中间环节。已知一个排列,要求下一个排列,首先确定其中介数,一个排列的后继,其中介数是原排列中介数加1,需要注意的是,如果中介数的末位kn-1+1=2,则要向前进位,一般情形,如果ki+1=n-i+1,则要进位,这就是所谓的递增进位制。例如排列839647521的中介数是72642321,则下一个排列的中介数是67342221+1=67342300(因为1+1=2,所以向前进位,2+1=3,又发生进位,所以下一个中介数是67342300)。
得到中介数后,可根据它还原对应得排列。算法如下:
    中介数k1k2......kn-1的各位数字顺序表示排列中的数字nn-1......2在排列中距右端的的空位数,因此,要按k1k2......kn-1的值从右向左确定nn-1......2的位置,并逐个放置在排列中:i放在右起的ki+1位,如果某位已放有数字,则该位置不算在内,最后一个空位放1
     因此从67342300可得到排列849617523,它就是839647521的后一个排列。因为9最先放置,k1=69放在右起第7位,空出6个空位,然后是放8k2=78放在右起第8位,但9占用一位,故8应放在右起第9位,余类推。

1.2  递减进位制数法

在递增进位制数法中,中介数的最低位是逢21,进位频繁,这是一个缺点。把递增进位制数翻转,就得到递减进位制数。
   
839647521
的中介数是67342221(k1k2…kn-1),倒转成为12224376(kn-1…k2k1),这是递减进位制数的中介数:ki(i=n-1,n-2,…,2)位逢iki-1位进1。给定排列pp的下一个排列的中介数定义为p的中介数加1。例如p=839647521p的中介数为12224376p的下一个排列的中介数为12224376+1=12224377,由此得到p的下一个排列为893647521
给定中介数,可用与递增进位制数法类似的方法还原出排列。但在递减进位制数中,可以不先计算中介数就直接从一个排列求出下一个排列。具体算法如下:
1
)如果p(i)=ni<>n,则p(i)p(i-1)交换
2
)如果p(n)=n,则找出一个连续递减序列98......i,将其从排列左端删除,再以相反顺序加在排列右端,然后将i-1与左边的数字交换
例如p=893647521的下一个排列是983647521。求983647521的下一个排列时,因为9在最左边且第2位为8,第3位不是7,所以将89从小到大排于最右端364752189,再将7与其左方数字对调得到983647521的下一个排列是367452189。又例如求987635421的下一个排列,只需要将9876从小到大排到最右端并将5与其左方数字3对调,得到534216789

1.3  字典序法

假如四位数1234,我们按照从后往前开始变化,也就是按我们通常的做法进行排列:

12341243132413421432,····4321

我们称它是按字典的排列顺序排列的。可以观察到初始状态是1234,每一个数都比后面一个小。最终状态为4321,每一个数都比后面一个数大。那么12344321之间的数肯定不是有序的.

字典序法的定义:

S1.求满足关系式pj-1<pjj的最大值,设为i,即

                i = max{ j| pj-1 < pj}

S2.求满足关系式pi-1<pkk的最大值,设为h,即

h = max{k| pi-1 < pk}

S3.pi-1ph互换得p1p2---pn

S4.p1p2···pi-1pipi+1···的pipi+1···pn的顺序逆转便得下一个排列

P1p2···pn-1pn···pi

 

例如p1p2p3p4=3421

S1. i = max{ j| pj-1 < pj}=2

S2. h = max{k| pi-1 < pk}=2

S3.p1p2互换得4321

S4.4321中的321的顺序逆转得下一个排列4123

 

1.4  换位法(又称临位对换法)(我在用代码实现时,123和1234的时候是正确的,当超过4的时候,就错了,我想应该是这个算法的问题吧!)

邻位对换法中下一个排列总是上一个排列某相邻两位对换得到的。以4个元素的排列为例,将最后的元素4逐次与前面的元素交换,可以生成4个新排列:
1 2 3 4  1 2 4 3  1 4 2 3  4 1 2 3
然后将最后一个排列的末尾的两个元素交换,再逐次将排头的4与其后的元素交换,又生成四个新排列:
  4 1 3 2  1 4 3 2  1 3 4 2  1 3 2 4
再将最后一个排列的前两个元素交换,将4从后往前移:
3 1 2 4  3 1 4 2  3 4 1 2   4 3 1 2
如此循环既可求出全部排列。

 

1.5元素增值法(n进制法)
1
)从原始排列p=p1p2......pn开始,第n位加n-1,如果该位的值超过n,则将它除以n,用余数取代该位,并进位(将第n-1位加1
2
)再按同样方法处理n-1位,n-2位,......,直至不再发生进位为止,处理完一个排列就产生了一个新的排列
3
)将其中有相同元素的排列去掉
4
)当第一个元素的值>n则结束
3个数123的排列为例:原始排列是1  2  3,从它开始,第3个元素是33+2=55 Mod 3=2,第2个元素是22+1=3,所以新排列是1 3 2。通过元素增值,顺序产生的排列是:1  2  31  3  22  1  12  1  32  2  22  3  12  3  33  1  23  2  1
有下划线的排列中存在重复元素,丢弃,余下的就是全部排列。


1.6.递归类算法
全排列的生成方法用递归方式描述比较简洁,实现的方法也有多种。
1
)回溯法
回溯法通常是构造一颗生成树。以3个元素为例;树的节点有个数据,可取值是123。如果某个为0,则表示尚未取值。
初始状态是(000),第1个元素值可以分别挑选123,因此扩展出3个子结点。用相同方法找出这些结点的第2个元素的可能值,如此反复进行,一旦出现新结点的3个数据全非零,那就找到了一种全排列方案。当尝试了所有可能方案,即获得了问题的解答。
2
)递归算法
如果用P表示n个元素的排列,而Pi表示不包含元素i的排列,(i)Pi表示在排列Pi前加上前缀i的排列,那么,n个元素的排列可递归定义为:
如果n=1,则排列P只有一个元素i
如果n>1,则排列P由排列(i)Pi构成(i=12....n-1)。

根据定义,容易看出如果已经生成了k-1个元素的排列,那么,k个元素的排列可以在每个k-1个元素的排列Pi前添加元素i而生成。例如2个元素的排列是1  22   1,对与个元素而言,p12  33  2,在每个排列前加上1即生成1 2 31 3 2两个新排列,p2p3则是1  33  11  22  1,按同样方法可生成新排列2 1 32 3 13 1 23 2 1
3
)循环移位法
如果已经生成了k-1个元素的排列,则在每个排列后添加元素k使之成为k个元素的排列,然后将每个排列循环左移(右移),每移动一次就产生一个新的排列。
例如2个元素的排列是1 22 1。在1 2后加上3成为新排列1 2 3,将它循环左移可再生成新排列2 3 13 1 2,同样2 1可生成新排列2 1 31 3 23 2 1

 

参考文献:

《组合数学》(第四版)卢开澄卢华明编著

 http://hi.baidu.com/applelove1990/item/ccc11937b45e6efe2684f405

http://blog.csdn.net/sharpdew/article/details/755074

http://blog.csdn.net/joylnwang/article/details/7064115

http://dongxicheng.org/structure/permutation-combination/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值