编程 1
给定一个字符串,输出给定字符串所有的组合的函数,例如:字符串 abc 所有组合 abc acb bac b ca cab cba六种组合
编程 2
给定一个数组大小m和一个数组array
m = 10; array = {1,2,3,4,5,6,7,8,9,10}
求从array中任意取得n(n<=m)个数,使得和为m,总共有多少种取法,例如:10 1,9 2,3,5
方法原型 :int getotalNum (int[] array, int m);
要求有输入输出,而不是仅仅做一个函数出来..
- *名称: problem01.c *
- *描述: 给定一个字符串,输出给定字符串所有的组合的函数 *
- *假设: 假设字符串中的字母不重复 *
- *思路: 对输入字符数组下标全排列再按照排列顺序输出即可 *
- *环境: Code::Blocks & Windows 7 & x86 *
- *备注: davelv于09年光棍节19点 *
- *************************************************************/
- #include
- #include
- #include
- #define BUF_LEN 1024
- //排列输出函数
- void perm(char *buf, int start, int end);
- int main(void)
- {
- char buf[BUF_LEN];
- printf("Please input string less than %d letters:", BUF_LEN);
- fgets(buf, BUF_LEN, stdin);
- if (buf[strlen(buf)-1] == '/n')
- buf[strlen(buf)-1] = '/0';//去除控制台附加的/n
- perm(buf, 0, strlen(buf));//排列输出
- system("PAUSE");
- return 0;
- }
- //buf 待字符串;start 排列开始位置;end 排列结束位置
- void perm(char *buf, int start, int end)
- {
- int i;
- char temp;
- if (start == end)
- {//当排列到最后一个字符时输出
- puts(buf);
- }
- else
- {//否则继续排列
- for (i=start; i
- {
- //交换
- temp = buf[start];
- buf[start] = buf[i];
- buf[i] = temp;
- //递归排列
- perm(buf, start+1, end);
- //换回原位置
- buf[i] = buf[start];
- buf[start] = temp;
- }
- }
- }
Code:
- /************************************************************
- *名称: problem02.c *
- *描述: 整形数组中取n个元素和等于元素个数m(m>=n) *
- *假设: 不能重复取某个元素 *
- *思路: 从1到m 组和数组元素并求和判断是否等于m *
- *改进: 见107行注释 *
- *环境: Code::Blocks & Windows 7 & x86 *
- *备注: davelv于09年光棍节20点 *
- *************************************************************/
- #include
- #include
- #include
- #define BUF_LEN 1024
- //比较函数,用于qsort
- int compare ( const void *a , const void *b );
- //求有几种取法的函数
- int getotalNum (int array[], int m);
- //数字组合函数
- void combine(int array[], int result[], int m, int n);
- int main(void)
- {
- int buf[BUF_LEN], m, i;
- printf("Please input NO. of integer(s) no more than %d:", BUF_LEN);
- scanf("%d",&m);
- printf("Please input integer(s):");
- for(i=0; i
- {
- scanf("%d",buf+i);
- }
- printf("Totle %d way(s)/n", getotalNum(buf,m));
- system("PAUSE");
- return 0;
- }
- int compare ( const void *a , const void *b )
- {
- return *(int *)a - *(int *)b;
- }
- //返回值为取法的总数,如果是-1则表示函数失败
- int getotalNum (int array[], int m)
- {
- int i, totle;
- int *result;
- qsort(array, m, sizeof(array[0]), compare);//排序
- //排除大于m的数字
- for(i=0; i
- {
- if (array[i] > m)
- {
- m = i;
- break;
- }
- }
- result = (int*)malloc(m * sizeof(int));//为combine函数分配缓存空间
- if (result == NULL)
- return -1;
- //循环从1组和到m
- totle=0;
- for(i=1; i<=m; i++)
- {
- combine(array, result, m, i);
- totle += *result;
- }
- free(result);
- return totle;
- }
- //array:存储数字,result:存储本次结果,m:array长度即组合下标,n:组合上标
- void combine(int array[], int result[], int m, int n)
- {
- int i,t;//计数器,begin变量的中间变量
- static int totle=0,level=0,begin=0,num=0;//已经组合数字和,已递归深度,组合开始元素的下标,符合要求组合的个数
- for (i=begin; i<=m-n+level; i++)
- {
- if ( totle+array[i] <= m)//判断当前组合数字和是否已经超出m限制
- {
- totle += array[i];
- result[level] = array[i];//保存当前数字
- //判断当前层数是不是N-1
- if (level < n-1)
- {
- level++;
- t = begin;begin = i+1;
- combine(array, result, m, n); //下层递归
- begin= t;
- level--;
- }
- else
- {
- if(totle == m)//判断当前数字和是否符合要求
- {
- int j;
- for(j=0; j<=level; j++)//符合则输出数字
- printf("%d ",result[j]);
- puts("");
- num++; //计数器加一
- }
- }
- totle -= array[i];
- }
- else
- break;//超出限制则跳出
- /**************************************************
- *若使用longjmp直接跳回getotalNum可获得更好的性能*
- **************************************************/
- }
- if (level == 0)
- {
- result[0] = num;
- totle = num = 0;//将static变量归零
- }
- }
原始代码优化的不是很到位,于是今天早上重写了下。
时间复杂度从O(sigma(1,n,A(i,n)))降低到O(A(n,n))也就是O(n!)。
主要方法是把combine函数改写,把外部调用combine的循环移入combine函数内部,这样减少了combine函数中某些结果重复计算的冗余,从而改进了时间复杂度。
新代码如下
Code:
- /************************************************************
- *名称: problem02.c *
- *描述: 整形数组中取n个元素和等于元素个数m(m>=n) *
- *假设: 不能重复取某个元素 *
- *思路: 从1到m 组和数组元素并求和判断是否等于m *
- *环境: Code::Blocks & Windows 7 & x86 *
- *备注: davelv于09-12-06 *
- *************************************************************/
- #include
- #include
- #include
- #define BUF_LEN 1024
- //比较函数,用于qsort
- int compare ( const void *a , const void *b );
- //求有几种取法的函数
- int getotalNum (int array[], int m);
- //数字组合函数
- void combine(int array[], int result[], int m);
- int main(void)
- {
- int buf[BUF_LEN], m, i;
- printf("Please input NO. of integer(s) no more than %d:", BUF_LEN);
- scanf("%d",&m);
- printf("Please input integer(s):");
- for(i=0; i
- {
- scanf("%d",buf+i);
- }
- printf("Totle %d way(s)/n", getotalNum(buf,m));
- return 0;
- }
- int compare ( const void *a , const void *b )
- {
- return *(int *)a - *(int *)b;
- }
- //返回值为取法的总数,如果是-1则表示函数失败
- int getotalNum (int array[], int m)
- {
- int i, totle;
- int *result;
- qsort(array, m, sizeof(array[0]), compare);//排序
- //排除大于m的数字
- for(i=0; i
- {
- if (array[i] > m)
- {
- m = i;
- break;
- }
- }
- result = (int*)malloc(m * sizeof(int));//为combine函数分配缓存空间
- if (result == NULL)
- return -1;
- combine(array, result, m);
- totle=*result;
- free(result);
- return totle;
- }
- //array:存储数字,result:存储结果数量,m:数字个数
- void combine(int array[], int result[], int m)
- {
- int i,t;//计数器,begin变量的中间变量
- static int totle=0,level=0,begin=0,num=0;//已经组合数字和,已递归深度,组合开始元素的下标,符合要求组合的个数
- for (i=begin; i<=m; i++)
- {
- totle += array[i];
- result[level] = array[i];//保存当前数字
- if ( totle < m)//判断当前组合数字和是否已经超出m限制
- {
- //判断当前层数是不是M-1
- if (level < m-1)
- {
- level++;
- t = begin;
- begin = i+1;
- combine(array, result, m); //下层递归
- begin= t;
- level--;
- }
- }
- else if(totle > m)//超过m则跳出本次循环
- {
- totle -= array[i];
- break;
- }
- else
- {
- int j;
- for(j=0; j<=level; j++)//符合则输出数字
- printf("%d ",result[j]);
- puts("");
- num++; //计数器加一
- }
- totle -= array[i];
- }
- if (level == 0)
- {
- result[0] = num;
- }
- }
利用自己写的测试程序测试新旧两个程序getotleNum()所耗时间结果如下:
Code:
- E:/cbwork/testc/bin/Release>driver.exe
- generate_test_data_file ok!
- run tested programs
- data old(ms) new(ms)
- 50 75 2
- 60 289 5
- 70 998 15
- 80 3278 44
- 90 9409 107
- 100 26747 261
- 110 69330 601
gcc (GCC) 3.4.5 (mingw-vista special),O2开关优化,Win7, Intel 奔腾双核 1.8GHz,1G内存。
排除当时间较少时的噪音干扰,可以得到如下结论。
1、旧程序在数据量每增加10的时候时间增长为原来的约3倍
2、新程序在数据量每增加10的时候时间增长为原来的约2+倍
3、新程序比旧程序在同等数据量的情况下快约100倍。
同时给出测试驱动程序:
Code:
- #include
- #include
- #include
- #include
- #define PROGRAM_NAME_TYPE "xxx.exe"
- #define IN_PIPE "<"
- void generate_test_data_file(int from, int to, int step);
- void run_programs(int from, int to, int step);
- int main()
- {
- int from=50,to=110,step=10;
- printf("generate_test_data_file ");
- generate_test_data_file(from,to,step);
- puts("ok!");
- puts("run tested programs");
- puts("data/t/told(ms)/t/tnew(ms)");
- run_programs(from,to,step);
- return 0;
- }
- void run_programs(int from, int to, int step)
- {
- char cmd[FILENAME_MAX];
- int len;
- for(;from<=to;from+=step)
- {
- len=strlen(PROGRAM_NAME_TYPE)+strlen(IN_PIPE);
- itoa(from,cmd+len,10);
- printf(cmd+len);
- printf("/t/t");
- memcpy(cmd,"old.exe<",strlen("old.exe<"));
- system(cmd);
- printf("/t/t");
- memcpy(cmd,"new.exe<",strlen("new.exe<"));
- system(cmd);
- puts("");
- }
- }
- void generate_test_data_file(int from, int to, int step)
- {
- FILE * fp;
- int i;
- char filename[FILENAME_MAX];
- for(;from<=to;from+=step)
- {
- fp=fopen(itoa(from,filename,10),"w");
- if(fp==NULL)
- {
- puts("generate test data file error!");
- exit(1);
- }
- fprintf(fp,"%d/n",from);
- for(i=0;i
- {
- fprintf(fp,"%d ",i);
- }
- fclose(fp);
- }
- }