使用回溯法求所有从n个元素中取m个元素的组合

来源:互联网 发布:淘宝花花家是正品吗 编辑:程序博客网 时间:2024/04/27 17:37

包含2个版本,第一个为递归版本,代码简洁,性能稍差。第二个为迭代版本,逻辑复杂,但性能更好。

 

#include <stdlib.h>#include <stdio.h>//#include <windows.h>typedef char ELE_TYPE;#define ELE_FMT "%c"//int g_count=0;//元素类型和格式符号使用宏定义,很容易改为其他数据类型,如数组类型改为int,则格式符改为"%d ".void printCombo(int idx_arr[], ELE_TYPE eArr[],int m){    int i;//g_count++;//return ;for (i=0;i<m;i++)        printf(ELE_FMT,eArr[idx_arr[i]]);    printf("\n");}// 递归形式的求组合数的函数combos,使用回溯法,求从n个元素中取m个元素的所有组合// 取到元素的序号保存在数组idx_arr[]中,以递增方式排列,每个序号的范围为从0到n-1// level为递归深度,取值范围为0到m-1,当level==m-1时, 所有的m个元素已经取到,打印这m个元素void combos(int n, int m, int idx_arr[], ELE_TYPE eArr[], int level ){    int i,begin,end;    if (level==0)        begin=0;    else        begin=idx_arr[level-1]+1;        end=n-m+level;    for (i=begin;i<=end;i++)    {        idx_arr[level]=i;        if ( level==m-1)            printCombo(idx_arr,eArr,m);//打印这m个个元素        else            combos(n,m,idx_arr,eArr,level+1); //继续取一个元素    }}// 迭代形式的求组合数的子程序,该函数用于求n个元素中取m个元素的所有组合// 取到元素的序号保存在数组idx_arr[]中,以递增方式排列,每个序号的范围为从0到n-1// 其算法实质是不断地生成一个又一个的组合数,每次迭代对idx_arr中某个元素做更新操作// 在该子程序中,2个重要的变量mode和i用于控制更新操作,//   mode表示更新模式,其值为M_FILL或者M_INC。//   M_FILL表示填充模式,当mode为此模式,元素idx_arr[i]的值总是被设置为比上级元素idx_arr[i-1]大1的数//   M_INC 表示增量模式,当mode为此模式,元素idx_arr[i]的值总是递增1个单位//关于“超限”,对于从n个元素中任取m个元素,取到的每个元素的序号是0到n-1// 我们把取到的元素的序号按照增序排列,存入idx_arr数组// 例,当n=5,m=3时,如果取到的最后一个元素的序号大于4,或者取到的倒数第2个元素大于3,我们称之为超限。// 更一般的,取到的idx_arr[i] > n-m+i,则为超限// 这个子程序用到5个if和一个while,总共6次比较,显然,其逻辑要比上面的那个递归版本复杂的多。// 凡事有利就有弊,这个子程序的性能要比上面的递归版本好,// 我的试验表明,将输出子程序printCombo改为只做一次整数加法,当n=28,m=14,迭代版本的性能是递归版本的130%#define M_FILL 0   //填充模式#define M_INC  1   //递增模式void IterativeCombos(int n, int m, int idx_arr[], ELE_TYPE eArr[] ){int i=0;  int mode=M_FILL;while (i>=0){if (mode==M_FILL)//填充模式{if (i==0)idx_arr[0]=0;elseidx_arr[i] = idx_arr[i-1]+1;if (i == m-1)//当前焦点已经达到最大深度{printCombo(idx_arr,eArr,m); //打印这个包含m个元素的组合mode=M_INC;//切换为增量模式}else//没有达到最大深度i++;//继续填充下级节点}else//增量模式{idx_arr[i]++;//焦点元素递增if ( idx_arr[i] > n-m+i ) //已经超限i--;else{if (i==m-1)//当前焦点已经达到最大深度printCombo(idx_arr,eArr,m); //打印这个包含m个元素的组合else{i++; //继续填充下级节点mode=M_FILL; //切换到填充模式}}}}}int main(int argc, char* argv[]){    int i;#define N  6#define M  3    ELE_TYPE eArr[N]; //定义6个数组的数组,    int idx_arr[M];   //取到的3个元素的需要放在数组idx_arr中for (i=0;i<sizeof(eArr)/sizeof(ELE_TYPE);i++) //数组的元素为'A'到'F'        eArr[i]='A'+i;combos(sizeof(eArr)/sizeof(ELE_TYPE),M,idx_arr, eArr, 0); //枚举所有6中取3的组合IterativeCombos(sizeof(eArr)/sizeof(ELE_TYPE),M,idx_arr, eArr); //枚举所有6中取3的组合    return 0;}


 

原创粉丝点击