| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 7166 人关注过本帖
标题:【分享】三种“聪明”的全排列函数
只看楼主 加入收藏
yu_hua
Rank: 2
等 级:论坛游民
帖 子:222
专家分:95
注 册:2006-8-10
结帖率:71.43%
收藏
已结贴  问题点数:20 回复次数:11 
【分享】三种“聪明”的全排列函数
在24点游戏编程过程中,在“全排列”方面偶有所得,在此与大家分享一下。

第一种“聪明”的全排列函数
int quan_pai_lie(int a[ ],int n)
//这个俺前几年自编的全排列函数很有特色。功能如下
//int型数组 a[ ](n个元素)既是入口参数,也是出口参数。
//进入时要求从大到小排列,如{5,4,3,2,1}。调用1次就变成
//{5,4,3,1,2},再调用就变成{5,4,2,3,1},依此类推。函数
//的返回值通常是1,表示还有未尽的排列;如果返回0,则表示
//给出的排列已经全了。于本例,就是到达{1,2,3,4,5}状态。
//如果初始状态为{1,2,4,5,3},调用后相继出现{1,2,4,3,5}
//{1,2,3,5,4}最终{1,2,3,4,5}。该函数还能正确对待重复
//元素: 设n=4,最初{5,5,5,1},调用后相继出现{5,5,1,5}、
//{5,1,5,5}最终定格在{1,5,5,5}。
//欲改变排序方向,请按末尾带////的行中的提示修改代码
{ //from 5,4,3,2,1 to 1,2,3,4,5
    int i,j,k,temp,t;
    for(k=n-1;k>0;k--)
     if(a[k-1]>a[k])break; ////改为a[k-1]<a[k]
    if(k==0)return 0;
    temp=a[k-1];i=k;
    for(j=k+1;j<n;j++)
     if(temp>a[j]&&a[j]>a[i])i=j; ////两个">"都改为"<"
    a[k-1]=a[i];a[i]=temp;
    for(i=k;i<n-1;i++)
    for(j=k;j<n-1+k-i;j++)
      if(a[j+1]>a[j])      ////改为a[j+1]<a[j]
      {t=a[j];a[j]=a[j+1];a[j+1]=t;}
    return 1;
}   

第二种“聪明”的全排列函数
这个全排列函数permutation()是专门为24点游戏设计,所以它
只能实现4个不同或部分相同元素的全排列。说它“聪明”是指它
能够正确处理雷同元素;而且不要求初始排序。模仿本函数,不难
写出处理5个或更多个元素的全排列代码。
void use2(void(*fun)( ),int a,int b,int c,int d)
{   
     fun(a,b,c,d);if(c-d)
     fun(a,b,d,c);
}   
void use6(void(*f)(),int a,int b,int c,int d)
{   
     use2(f,a,b,c,d);if(b-c)
     use2(f,a,c,d,b);if(b-d)if(c-d)
     use2(f,a,d,b,c);
}   
void permutation(void(*f)(),int a,int b,int c,int d)
{   
     use6(f,a,b,c,d);if(a-b)
     use6(f,b,c,d,a);if(a-c)if(b-c)
     use6(f,c,d,a,b);if(a-d)if(b-d)if(c-d)
     use6(f,d,a,b,c);
}   
//下面的main(void)提示您怎样调用这个全排列函数
#include<stdio.h>
void print(int a,int b,int c,int d)
{   
printf("%d%d%d%d\n",a,b,c,d);
}   
int main(void)
{   
    permutation( print,1,2,3,2 );
    return 0;
}   
屏幕输出如下(vc6.0)
1232
1223
1322
2321
2312
2213
2231
2132
2123
3212
3221
3122
Press any key to continue

第三种“聪明”的全排列函数
全排列函数 use24 为24点游戏设计,也不要求初始排序。
能够实现4个不同、相同或部分相同元素的全排列。特别是  
这个嵌套调用函数的层次感非常好。现在把它介绍给大家。
void use4(void(*fun)( ),int a,int b,int c,int d)
{  //第3层次 组内成员次序
     fun(a,b,  c,d);//甲组a先b后;乙组c先d后
     if(c-d)
     fun(a,b,  d,c);//甲组a先b后;乙组d先c后
     if(a-b)
     fun(b,a,  c,d);//甲组b先a后;乙组c先d后
     if(a-b && c-d)
     fun(b,a,  d,c);//甲组b先a后;乙组d先c后
}   
void use8(void(*f)(),int a,int b,int c,int d)
{  //第2层次 "组"的次序
     use4(f, a,b, c,d);//ab为甲组、cd为乙组
     if(a==c && b==d || a==d && b==c)return;
     use4(f, c,d, a,b);//cd为甲组、ab为乙组
}   
void use24(void(*f)(),int a,int b,int c,int d)
{  //第1层次 两两分组
     use8(f, a,b, c,d);//ab一组、cd另一组
     if(a-c && b-d)
     {  
     use8(f, a,d, b,c);//ad一组、bc另一组
     if(a-b && a-d && b-c && c-d)
     use8(f, a,c, b,d);
     }  
     else if(a-d && b-c)
     {  
     use8(f, a,c, b,d);//ac一组、bd另一组
     if(a-b && a-c && b-d && c-d)
     use8(f, a,d, b,c);//ad一组、bc另一组
     }  
}   
//下面的main(void)提示怎样调用全排列函数use24
#include<stdio.h>
void print(int a,int b,int c,int d)
{   
    printf("%d%d%d%d\n",a,b,c,d);
}   
int main(void)
{   
    use24( print,1,2,2,2 );
    return 0;
}   
屏幕输出如下(vc6.0)
1222
2122
2212
2221
Press any key to continue


搜索更多相关主题的帖子: 函数 排列 分享 
2010-12-05 15:48
夏天真美好
Rank: 1
等 级:新手上路
帖 子:4
专家分:4
注 册:2010-11-14
收藏
得分:4 
谢谢啦!!!!!!!!
2010-12-05 15:51
BlueGuy
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:29
帖 子:4476
专家分:4055
注 册:2009-4-18
收藏
得分:4 
楼主,你估计还在上中学吧?
你的三种算法感觉并不是聪明, 求全排列,for循环是最自然的解法了,/

我就是真命天子,顺我者生,逆我者死!
2010-12-05 15:51
御坂美琴
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:魔術の禁書目錄
等 级:小飞侠
威 望:9
帖 子:952
专家分:2929
注 册:2010-8-18
收藏
得分:4 
我说楼主,你应该让你的函数能支持任意个元素,而不是在参数里写死了多少个就多少
见 http://blog.

永远为正义而奋斗,锄强扶弱的Level 5 超能力者
とある魔術の禁書目錄インデックス__御み坂さか美み琴こと
http://bbs.bccn.net/space.php?action=threads&uid=483997
2010-12-05 16:02
BlueGuy
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:29
帖 子:4476
专家分:4055
注 册:2009-4-18
收藏
得分:0 
dfs 不过是for循环的一种变型, 把for循环精简了而已,/


我就是真命天子,顺我者生,逆我者死!
2010-12-05 16:05
conanlcg
Rank: 1
来 自:重庆市
等 级:新手上路
帖 子:1
专家分:4
注 册:2010-12-5
收藏
得分:4 
我是新来的。。!!!
2010-12-05 16:06
yu_hua
Rank: 2
等 级:论坛游民
帖 子:222
专家分:95
注 册:2006-8-10
收藏
得分:0 
其实,代码的长短应该是次要的。
一楼第1个函数长度短、又支持任意个元素的全排列。按理说,似乎已经很好。
但是,为什么要继续研究呢?这是因为,在24点游戏避免重复解的研究过程中
我发现:从全排列意义上,第1个函数给出的“全排列”当然是不带重复的,然
而对于24点那样的游戏就不够友好了。举例:1、2、6、9 四个数字的全排列,
有2、9、1、6;2、9、6、1;9、2、1、6;9、2、6、1;
有1、6、2、9;1、6、9、2;6、1、2、9;6、1、9、2 等等。
如果不另加判断地写“24点”的代码,就会导致“重复度”为8的雷人解
2*9+1*6;2*9+6*1;9*2+1*6;9*2+6*1;
1*6+2*9;1*6+9*2;6*1+2*9;6*1+9*2。
此“故障”的根源在于(a*b)+(c*d)模式的运算,括号内的乘法满足交换律、
括号外的加法也满足交换律!因此,遇见这种双重交换律成立的情况,即使四张
牌的点数不等,也只能停留在“第1层次”即:将它们分为3种组合,具体为
a、b;c、d 如上例中:2、9在一组;1、6在另一组
a、d;b、c 如上例中:2、6在一组;9、1在另一组
a、c;b、d 如上例中:2、1在一组;9、6在另一组
如果括弧外的运算不满足交换律,则应进行到“第2层次”。相当于说
“2、9在甲组;1、6在乙组” 是一种状态;而
“1、6在甲组;2、9在乙组” 是另外一种状态。
如果括弧内的运算也不满足交换律了,则应进行到“第3层次”--这时全排列给
出的所有状态就都得用上啰!“浅尝辄止”不利于研究算法问题。

2010-12-05 16:51
BlueGuy
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:29
帖 子:4476
专家分:4055
注 册:2009-4-18
收藏
得分:0 
楼主,我建议你果断给 微软亚洲研究院 投简历 ,/

我就是真命天子,顺我者生,逆我者死!
2010-12-05 16:53
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:4 
楼主如果想讲算法的话,用语言描述其实更好一点。算法描述清楚了代码就不用注释太多了。
比如第一个,你只说了函数是干什么的,没说怎么干的,后面的代码也没有有用的注释。和这个同样功能的函数,STL 里就有一个,那个我倒会,不过你的算法没细看不知道用的算法一样不一样。另两个感觉很专用,我就不怎么看了。

我听说过一个生成全排列的算法,是递归的,不知道你这有没有。我只说下思路。
一个元素的排列只有一种:
1
两个的,就是把 2 在 1 前后“晃”一下:
   1 2
2 1
三个的,要借助两个的,把 3 在其中“穿梭”一下,一共6个:
   1    2 3
   1 3 2
3 1    2
3 2    1
   2 3 1
   2    1 3
四个的,就是在其中再“穿梭”,一共24个:
   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
五个的可以再算。
这个算法的特点是,只要知道了 n - 1 的全排列,就可以用之来生成 n 个的。

另一个比较著名的算法就是我说的 STL 里的那个,那个的特点是按字典序移动出全排列。
2010-12-06 22:01
点线面
Rank: 8Rank: 8
来 自:NO.-1
等 级:蝙蝠侠
帖 子:525
专家分:980
注 册:2011-1-3
收藏
得分:0 
我以前写过一个全排列,下面可以省一定速度,但是浪费空间,不用归递
程序代码:
#include<stdio.h>
#include<conio.h>

int RL(int *a,const int *ini);
void SP(int *a);
int main()
{
  int a[1000],c[1000],N;
  long sum=0;
  puts("请输入个数");
  scanf("%d",&N);
  for(c[0]=0;N-1&&c[0]<N;c[0]++){
     if(c[0]==0||c[0]==1)/*设0为停止值,0以上表示交换的次数*/
     {
      c[c[0]]=c[0];
     }
     else{
      c[c[0]]=c[0]+1;}
     a[c[0]]=c[0]+1;/*排列数*/
    }

 

 
   while(c[N-1])
  {
   for(c[0]=N-1;c[0]>-1;--c[0]){
     printf("%d ",a[c[0]]);/*显示初化范围*/
    }
    putchar('\n');

    SP(a);
    sum++;
   for(c[0]=N-1;c[0]>-1;--c[0])
     printf("%d ",a[c[0]]);/*显示*/
    putchar('\n');
    SP(a);
     --c[1];
   for(c[0]=1;c[N-1]&&c[0]<N;c[0]++)
    if(N>c[0]+1&&!c[c[0]])
        {c[c[0]]=(c[0]==1? c[0]:c[0]+1);
        RL(&a[c[0]+1],&a[0])&&--c[c[0]+1];}

  }
  printf("\n%ld\n",2*sum);
  getch();
}

/*移动部分*/
int RL(int *a,const int *ini)
{
  int t=*a;
  for(;a>ini;--a)
   *a=*(a-1);
  *a=t;
  return 1;
}

/*转换部分*/
void SP(int *a)
{
   a[0]^=a[1];
   a[1]=a[0]^a[1];
   a[0]=a[0]^a[1];
}


小代码,大智慧
2011-01-05 17:54
快速回复:【分享】三种“聪明”的全排列函数
数据加载中...
 
   



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

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