递归—排列组合的实现

                                                                                        排列组合的实现

         排列组合是组合数学的基础,从n个不同元素中任取m个,约定1<m≤n,按任意一种次序排成一列,称为排列,其排列种数记为A(n,m)。从n个不同元素中任取m个(约定1<m<n)成一组,称为一个组合,其组合种数记为C(n,m)。计算A(n,m)与C(n,m)只要简单进行乘运算即可,要具体展现出排列的每一列与组合的每一组,决非轻而易举。

本节应用递归设计来具体实现排列与组合。

4.6.1  实现基本排列A(n,m)与组合C(n,m)

1.实现基本排列A(n,m)

对指定的正整数m,n(约定1<m≤n),具体实现从n个不同元素中任取m个元素A(n,m)的每一排列。

2. 设计要点

设置a数组在n个整数1——n中选取m个数。

递归函数p(k)的变量k从1开始取值。当k≤m时,第k个数a(k)取i(1——n),并标志量u=0。

(1)若a(k)与其前面已取的数a(j)(j<k)比较,出现a(k)=a(j),即第k个数取i不成功,标志量u=1。

(2)若a(k)与所有前面已取的a(j)比较,没有一个相等,则第k个数取i成功,标志量u=0,然后判断:

         1) 若k=m,即已取了m个数,输出这m个数即为一排列,并用s统计排列的个数;输出一个排列后,a(k)继续从i+1开始,在余下的数中取下一个数。直到全部取完,则返回上一次调用p(k)处,即回溯到p(k-1),第k-1个数继续往下取值。

        2) 若k<m,即还未取m个数,即在p(k)状态下调用p(k+1)继续探索下一个数,下一个数a(k+1)又从(1——n)中取数。

        3)标志量u=1,第k个数取i不成功,则接着从i+1开始中取下一个数。若在1——n中的每一个数都取了,仍是u=1,则返回上一次调用p(k)处,即回溯到p(k-1),第k-1个数继续往下取值。

        可见递归具有回溯的功能,即p(k)在取所有n个数之后,自动返回调p(k)的上一层,即回溯到p(k-1),第k-1个数继续往下取值。这也是递归能把所有排列一个不剩全部展示的原因所在。

在主程序,只要调用p(1)即可,所有排列在递归函数中输出。最后p(1)的a(1)取完所有数,返回s,即输出排列的个数后结束。

3. 实现排列A(n,m)程序设计

// 实现排列A(n,m)  

#include <stdio.h>

int m,n,a[30]; long s=0;

void main()

{

  int p(int k);

  printf(" input n  (n<10):"); scanf("%d",&n);

  printf(" input m(1<m<=n):"); scanf("%d",&m);

  p(1);                  // 从第1个数开始  

  printf("\n 总数为:%ld \n", s);    // 输出A(n,m)的值  

}

// 排列递归函数p(k)  

#include <stdio.h>

int p(int k)

  int i,j,u;

  if(k<=m)

    { 

      for(i=1;i<=n;i++)     

       { 

a[k]=i;            // 探索第k个数赋值i  

         for(u=0,j=1;j<=k-1;j++)

        if(a[k]==a[j])  //  若出现重复数字  

u=1;        // 若第k数不可置i,则u=1  

  if(u==0)           // 若第k数可置i,则检测是否到m个数  

           {

 if(k==m)       // 若已到m个数时,则打印出一个解 

               {

   s++; 

                   printf(" ");

                   for (j=1;j<=m;j++)

                       printf("%d",a[j]);

                   if(s%10==0)

 printf("\n");

               

             else  

    p(k+1);     // 若没到m个数,则探索下一个数 p(k+1) 

            }

        }

}

return s;

}

 4.  程序运行示例

input n  (n<10):4

 input m(1<m<=n):3

 123 124 132 134 142 143 213 214 231 234

 241 243 312 314 321 324 341 342 412 413

 421 423 431 432

 总数为: 24

5.  实现基本组合C(n,m)

注意到组合与组成元素的顺序无关,约定组合中的组成元素按递增排序。因而,把以上程序中的约束条件作简单修改:

a[j]==a[i] 修改为 a[j]>=a[i]

或(a[k]==a[j])修改为 a[k]>=a[j]

即可实现从n个不同元素中取m个(约定1<m<n)的组合C(n,m)。

运行修改后的程序示例:

input n  (n<10):6

 input m(1<m<=n):3

 321 421 431 432 521 531 532 541 542 543

 621 631 632 641 642 643 651 652 653 654

 总数为:20

6.  实现可重复的组合

      注意到可重复的组合组成元素可以相同,因而,把以上程序中的约束条件作简单修改:

      a[j]==a[i]修改为 a[j]>a[i]   

      或a[k]==a[j]修改为 a[k]>a[j]

     即可实现从n个不同元素中取m个(约定1<m<n)可重复的组合。

 运行修改后的程序示例:

 input n  (n<10):5

 input m(1<m<=n):3

 111 211 221 222 311 321 322 331 332 333

 411 421 422 431 432 433 441 442 443 444

 511 521 522 531 532 533 541 542 543 544

 551 552 553 554 555

 总数为:35

7.  程序变通

         把以上程序中的输出语句printf("%d",a[j])改为printf("%c",a[j]+64);排列(或组合)输出由前n个正整数改变为前n个大写英文字母输出。

         把以上程序中的输出语句printf("%d",a[j])改为printf("%c",n+65-a[j]);排列(或组合)输出由前n个正整数改变为前n个大写英文字母逆序输出。

当排列的元素超过10个时,为区别12是一个元素12还是两个元素1、2,可在输出排列的每一个元素后加空格。

2  实现复杂排列

应用递归探讨实现从n个不同元素中取r(约定1<r≤n)个元素与另外m个相同元素组成的复杂排列。

1.  设计要点

设n个不同元素为1,2,…,n,m个相同元素为0。

应用递归回溯探索从n个不同元素(1——n)中取r个元素与另外m个相同元素0组成的排列。

递归函数p(k)的变量k从0开始取值。当k≤r+m时,第k个数a(k)取i(0——n),并标志量u=0。

(1)若a(k)与其前面已取的正整数a(j)(j<k)比较,出现a(k)=a(j),即第k个数取i不成功,标志量u=1。

(2)若a(k)与所有前面已取的正整数a(j)比较,没有一个相等,则第k个数取i成功,标志量u=0,然后判断:

          1) 若k=r+m,即已取了r+m个数。此时需统计“0”的个数是否为m,若“0”的个数h=m,输出这r+m个数即为一排列,并用s统计排列的个数。

 输出一个排列后,a(k)继续从i+1开始,在余下的数中取下一个数。

 若“0”的个数h≠m,a(k)继续从i+1开始,在余下的数中取下一个数。

直到全部取完,则返回上一次调用p(k)处,即回溯到p(k-1),第k-1个数继续往下取值。

       2) 若k<r+m,即还未取m个数,即在p(k)状态下调用p(k+1)继续探索下一个数,下一个数a(k+1)又从(0——n)中取数。

       3)标志量u=1,第k个数取i不成功,则接着从i+1开始中取下一个数。若在0——n中的每一个数都取了,仍是u=1,则返回上一次调用p(k)处,即回溯到p(k-1),第k-1个数继续往下取值。

可见递归的回溯功能,是递归能把所有排列既不重复又不遗漏全部展示的原因所在。

在主程序,只要调用p(1)即可,所有排列在递归函数中输出。

最后p(1)的a(1)取完所有数,返回s,即输出排列的个数后结束。

 2.  复杂排列程序设计

// 从n个不同元素取r个与另m个相同元素的复杂排列  

#include <stdio.h>

int m,n,r,a[30]; long s=0;

void main()

  int p(int k);

  printf(" input n: "); scanf("%d",&n);

  printf(" input r(1<r<=n): "); scanf("%d",&r);

  printf(" input m: "); scanf("%d",&m);

  printf(" 从%d个不同元素取%d个与另%d个相同元素的排列:\n",n,r,m);

  p(1);                       // 从第1个数开始  

  printf("\n s=%ld \n",s);    // 输出复杂排列的个数  

}

// 复杂排列递归函数  

#include <stdio.h>

int p(int k)

  int h,i,j,u;

  if(k<=r+m)

    { 

       for(i=0;i<=n;i++)     

       { 

            a[k]=i;                    // 探索第k个数赋值i  

         for(u=0,j=1;j<=k-1;j++)

       if(a[j]!=0 && a[k]==a[j]) // 若出现非零元素相同,则u=1   

u=1;              

if(u==0)              // 若第k数可置i,则检测是否r+m个数  

            { 

if(k==r+m)       // 若已到r+m个数则检测0的个数h 

               { 

for(h=0,j=1;j<=r+m;j++) 

                    if(a[j]==0)

 h++;

                 if(h==m)      // 若相同元素0的个数为m个,输出一排列        

     { 

    s++;

 printf(" ");

                    for(j=1;j<=r+m;j++)

                      printf("%d",a[j]);

                if(s%10==0) printf("\n");

      }

                } 

             else  

p(k+1);     // 若没到r+m个数,则探索下一个数 p(k+1) 

           }

   }

   }

  return s;

}

3.  程序运行示例

input n: 4

input r(1<r<=n): 2

input m: 2

从4个不同元素取2个与另2个相同元素的排列:

0012 0013 0014 0021 0023 0024 0031 0032 0034 0041

0042 0043 0102 0103 0104 0120 0130 0140 0201 0203

0204 0210 0230 0240 0301 0302 0304 0310 0320 0340

0401 0402 0403 0410 0420 0430 1002 1003 1004 1020

1030 1040 1200 1300 1400 2001 2003 2004 2010 2030

2040 2100 2300 2400 3001 3002 3004 3010 3020 3040

3100 3200 3400 4001 4002 4003 4010 4020 4030 4100

4200 4300

s=72

变通以上算法,实现n个相同元素与另m个相同元素的排列。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值