| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 7296 人关注过本帖, 8 人收藏
标题:[分享]全排列问题浅谈
只看楼主 加入收藏
nuciewth
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:我爱龙龙
等 级:贵宾
威 望:104
帖 子:9786
专家分:208
注 册:2006-5-23
结帖率:50%
收藏(8)
 问题点数:0 回复次数:12 
[分享]全排列问题浅谈

1.5全排列的生成算法
全排列的生成算法就是对于给定的字符集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。

这里介绍全排列算法四种:
(A)字典序法
(B)递增进位制数法
(C)递减进位制数法
(D)邻位对换法

1.5.1字典序法
对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。

[例]字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是:123,132,213,231,312,321。

[注意] 一个全排列可看做一个字符串,字符串可有前缀、后缀。

1)生成给定全排列的下一个排列 所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

[例]839647521是1--9的排列。1—9的排列最前面的是123456789,最后面的是987654321,从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。 /

1.5.2递增进位制数法
1)由排列求中介数

在字典序法中,中介数的各位是由排列数的位决定的.中介数位的下标与排列的位的下标一致。
在递增进位制数法中,中介数的各位是由排列中的数字决定的。即中介数中各位的下标与排列中的数字(2—n)一致。可看出n-1位的进位链。 右端位逢2进1,右起第2位逢3进1,…, 右起第i位逢i+1进1,i=1,2,…,n-1. 这样的中介数我们称为递增进位制数。 上面是由中介数求排列。

由序号(十进制数)求中介数(递增进位制数)如下:
m=m1,0≤m≤n!-1
m1=2m2+kn-1,0≤kn-1≤1
m2=3m3+kn-2,0≤kn-2≤2
……………
mn-2=(n-1)mn-1+k2,0≤k2≤n-2
mn-1=k1,0≤k1≤n-1
p1p2…pn←→(k1k2…kn-1)↑←→m

在字典序法中由中介数求排列比较麻烦,我们可以通过另外定义递增进位制数加以改进。

为方便起见,令ai+1=kn-1,i=1,2,…,n-1
(k1k2…kn-1)↑=(anan-1…a2)↑
ai:i的右边比i小的数字的个数
在这样的定义下,
有839647521←→(67342221)↑
(67342221)↑+1=(67342300)↑←→849617523
6×8+7)×7+3)×6+4)×5+2)×4+2)×3+2)×2+1 =279905

由(anan-1…a2)↑求p1p2…pn。

从大到小求出n,n-1,…,2,1的位置

_ ... _ n _ _ …_ (an个空格)
n的右边有an个空格。
n-1的右边有an-1个空格。
…………
2的右边有a2个空格。
最后一个空格就是1的位置。

1.5.3递减进位制数法
在递增进位制数法中,中介数的最低位是逢2进1,进位频繁,这是一个缺点。把递增进位制数翻转,就得到递减进位制数。

(anan-1…a2)↑→(a2a3…an-1an)↓
839647521→ (12224376)↓


(12224376)↓=1×3+2)×4+2)×5+2)×6+4)×7+3)×8+7)×9+6=340989
[注意]求下一个排列十分容易

1.5.4邻位对换法
递减进位制数法的中介数进位不频繁,求下一个排列在不进位的情况下很容易。这就启发我们,能不能设计一种算法,下一个排列总是上一个排列某相邻两位对换得到的。递减进位制数字的换位是单向的,从右向左,而邻位对换法的换位是双向的。

这个算法可描述如下:
对1—n-1的每一个偶排列,n从右到左插入n个空档(包括两端),生成1—n的n个排列。
对1—n-1的每一个奇排列,n从左到右插入n个空档,生成1—n的n个排列。
对[2,n]的每个数字都是如此。

839647521
字典序法 递增进位制法 递减进位制法 邻位对换法
下一个 839651247 849617523 893647521 836947521
中介数 72642321↑ 67342221↑ 12224376↓ 10121372↓
序 号 297191 279905 340989 203393

/*以上转载*/


下面我来说说全排列问题的递归与非递归解法.

1.递归(分治法思想):
设R={r1,r2,..rn} 是要进行排列的n个元素,Ri=R-{ri}.集合X中元素的全排列记为perm(X);
设(ri)perm(X)表示每一个全排列前加上前缀ri得到的排列.

当n=1时,perm(R)=(r) 其中r是唯一的元素,这个就是出口条件.
当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),...(rn)perm(Rn)构成.

void Perm(list[],int k,int m) //k表示前缀的位置,m是要排列的数目.
{
if(k==m-1) //前缀是最后一个位置,此时打印排列数.
{
for(int i=0;i<m;i++)
{
printf("%d",list[i]);
}
printf("\n");
}
else
{
for(int i=k;i<m;i++)
{
Swap(list[k],list[i]); //交换前缀,使之产生下一个前缀.
Perm(list,k+1,m);
Swap(list[k],list[i]); //将前缀换回来,继续做上一个的前缀排列.
}
}
}

inline void Swap(int &a,int &b) //此处为引用,交换函数.函数调用多,故定义为内联函数.
{
int temp=a,a=b,b=temp;
}

函数Perm(int list[],int k,int m)是求将list的第0~k-1个元素作为前缀、第k~m个元素进行全排列得到的全排列,如果k为0,且m为n,就可以求得一个数组中所有元素的全排列。其想法是将第k个元素与后面的每个元素进行交换,求出其全排列。这种算法比较节省空间。

非递归:

n个数的排列可以从1.2....n开始,至n.n-1....2.1结束。
也就是按数值大小递增的顺序找出每一个排列。
以6个数的排列为例,其初始排列为123456,最后一个排列是654321,
如果当前排列是124653,找它的下一个排列的方法是,从这个序列中
从右至左找第一个左邻小于右邻的数,如果找不到,则所有排列求解
完成,如果找得到则说明排列未完成。本例中将找到46,计4所在的
位置为i,找到后不能直接将46位置互换,而又要从右到左到第一个
比4大的数,本例找到的数是5,其位置计为j,将i与j所在元素交换
?125643,然后将i+1至最后一个元素从小到大排序得到125346,这
就是124653的下一个排列,如此下去,直至654321为止。算法结束。

int b[N];
int is_train(int a[],int n)
{
int i,j,k=1 ;

for(i=1;i<=n;i++)
{
for(j=i+1;j<=n;j++)
if(a[j]<a[i])b[k++]=a[j];
/*判断是否降序*/
if(k>1)is_train(b,k);
else return(1);
}

}


void train(int a[],int n)
{
int i,j,t,temp,count=1 ;
t=1 ;
printf("input the %3dth way:",count);
for(i=1;i<=n;i++)
printf("%3d",a[i]);
printf("\n");
while(t)
{
i=n ;
j=i-1 ;
/*从右往左找,找第一个左邻比右邻小的位置*/
while(j&&a[j]>a[i])
{
j--;
i--;
}
if(j==0)t=0 ;
else t=1 ;
if(t)
{
i=n ;
/*从右往左找,找第一个比front大的位置*/
while(a[j]>a[i])
i--;
temp=a[j],a[j]=a[i],a[i]=temp ;
quicksort(a,j+1,N);/*调用快速排序*/
/*判断是否符合调度要求*/
if(is_train(a,N)==1)
{
count++;
printf("input the %3dth way:",count);
for(i=1;i<=n;i++)
printf("%3d",a[i]);
printf("\n");
}

}

}
}

/*有待修正,谢谢各位*/

搜索更多相关主题的帖子: 排列 算法 字符 字典 进位制 
2006-09-15 16:38
cdmalcl
Rank: 7Rank: 7Rank: 7
等 级:贵宾
威 望:24
帖 子:4091
专家分:524
注 册:2005-9-23
收藏
得分:0 


最近正研究这个呢
有时间好好看看去

2006-09-15 16:43
honkerman
Rank: 2
等 级:新手上路
威 望:4
帖 子:3078
专家分:0
注 册:2006-8-25
收藏
得分:0 
顶,~~~加精~~

" target="_blank">God Bless You[GLOW=255,#00ff00,2]My Friends![/GLOW]
2006-09-16 00:33
Welton
Rank: 2
等 级:论坛游民
帖 子:65
专家分:38
注 册:2006-10-25
收藏
得分:0 
好!

只是喜欢编程而已!
2006-12-02 19:55
卧龙孔明
Rank: 9Rank: 9Rank: 9
等 级:贵宾
威 望:59
帖 子:3872
专家分:684
注 册:2006-10-13
收藏
得分:0 

GREAT!

具体内容已收藏,日后精读.

先说说我的算法:
从右向左搜索,当第N个数的值大于第N-1个数的值时,暂停搜索,这两交换,其后N+1...MAX进行排序(由小到大),这样就完成了一次,求出了下一个序列,重复以上过程直到为(MAX,MAX-1...5,4,3,2,1)时停止


My Blog: www.aiexp.info
虽然我的路是从这里开始的,但是这里不再是乐土.感谢曾经影响过,引导过,帮助过我的董凯,飞燕,leeco,starwing,Rockcarry,soft_wind等等等等.别了,BCCN.
2006-12-02 20:17
卧龙孔明
Rank: 9Rank: 9Rank: 9
等 级:贵宾
威 望:59
帖 子:3872
专家分:684
注 册:2006-10-13
收藏
得分:0 
加精

My Blog: www.aiexp.info
虽然我的路是从这里开始的,但是这里不再是乐土.感谢曾经影响过,引导过,帮助过我的董凯,飞燕,leeco,starwing,Rockcarry,soft_wind等等等等.别了,BCCN.
2007-01-14 20:58
pc新手
Rank: 1
等 级:新手上路
帖 子:93
专家分:0
注 册:2007-1-28
收藏
得分:0 

学习


不进则退
2007-02-12 16:53
天堂守护神
Rank: 1
等 级:新手上路
帖 子:45
专家分:0
注 册:2007-7-19
收藏
得分:0 

我的天丫。太详细了。谢谢。
发表一下个人的算法。还没有实验过。大家看看行不行。
就是用随机数的方法。
先把所有元素编号。然后先对第一个元素随机到一个号,第二个元素再来随机,但要不同于第一个。后面的依次类推。这样就可以形成第一个排列。后面的排列都是一样,但要检测是不是和前一个一样。
昨天睡觉做梦想到的想法。本人还是菜鸟。只能用这种简单的方法。
根据理论来讲是可以全部排出来的。
这几天我来试一试。大家也可以试一试。


2007-08-04 11:02
水漪儿
Rank: 2
来 自:shangshida
等 级:论坛游民
帖 子:147
专家分:10
注 册:2007-7-19
收藏
得分:0 
好东西啊,最近一直在考虑这方面的题,总觉得自己的方法比较烦琐,呵呵,看到这个一定要收藏,好好看看.
2007-08-04 20:31
StarWing83
Rank: 8Rank: 8
来 自:仙女座大星云
等 级:贵宾
威 望:19
帖 子:3951
专家分:748
注 册:2007-11-16
收藏
得分:0 
换回去???
傻了傻了啊……我想到了方法,在这个地方卡了一下午…………

专心编程………
飞燕算法初级群:3996098
我的Blog
2007-11-18 19:59
快速回复:[分享]全排列问题浅谈
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.016947 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved