| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2913 人关注过本帖
标题:[原创]递归的艺术-递归程序的非递归化
只看楼主 加入收藏
stnlcd
Rank: 1
等 级:新手上路
帖 子:177
专家分:1
注 册:2004-11-21
收藏
得分:0 
我看了你的帖子,你的最后一个(非递归算法那个)和我的comb_w_if和comb_w_w函数在本质上是一样的:在你的函数f中
你采用了栈a[10](10有点小了)保存了参数n,你的递归形式为:while-while形式,这种算法不需要自己动手设计,参照递归程序进行单纯转化就可以得到.这种算法由于栈的存在,必然影响程序的执行效率,如果改用迭代回溯算法,就不需要栈的支持,因为迭代回溯在本质上是对解空间树进行搜索,所以只需要定义树的深度不需要保存任何参数,在函数comb_dd中也可以看到:我并没有用任何的数组来保存任何参数,所以这种算法的效率非常的高,至少在空间复杂度上占很大优势.而且用迭代回溯写出的算法在可度性和可理解性上要比comb_w_if,comb_w_w和你的f函数好.谢谢你对我的函数的质疑,欢迎提出宝贵意见!
    随便帮你改正一下你的函数:你的原函数为:
f(int n,int m){int i=j=0,a[10],s[10],l;
do{
     for(;j<n;j++){
       s[i]=j;
       if(i==m-1){for(l=0;l<m;l++)printf("%d",s[l]);printf("\n");}
       else a[i]=j,i++;
        j=a[--i]+1;}while(a[0]!=n-1);}
因为疏忽,你在语句"else a[i]=j,i++;"后面少了个'}'.在进行退栈(j=a[--i]+1)的时候,最好加if(i)判断栈是否为空.这是一个良好的习惯,因为有的时候栈确实可能为空(虽然看起来不像为空的样子).

要让一个男人破产,请给他一架相机,要让一个男人倾家荡产,请给他一架望远镜。
2005-06-11 00:22
stnlcd
Rank: 1
等 级:新手上路
帖 子:177
专家分:1
注 册:2004-11-21
收藏
得分:0 
    在非递归的过程中,的确可以只保存一个参数.例一,例二(hanoi函数)和例三(akm函数)已经证明了这点,特别时hanoi函数:它有四个参数:n,x,y,z,可是我只保存了参数n,当然你也可以将着四个参数全都保存下来(这样效率会降低很多).你在你的非递归组合函数f中不也是只保存了参数n吗(a[i]=j,j就是n的循环)?
    谢谢你对我帖子的支持,如果我对你的"非递归组合函数f"理解有误,请明确指出!
我的帖子中还有其他几个例子,请高手作评价!谢谢!

要让一个男人破产,请给他一架相机,要让一个男人倾家荡产,请给他一架望远镜。
2005-06-11 00:36
stnlcd
Rank: 1
等 级:新手上路
帖 子:177
专家分:1
注 册:2004-11-21
收藏
得分:0 

为了弄清到底那个组合算法的效率高,我连夜编写了对comb,comb_w_w,comb_w_if,comb_dd,comb_fz和 simpley的f函数的测试程序,该测试程序采用了权威和准确的算法测试方法(对该算法如有任何质疑请发送到dianzi8801@sina.com与我单独讨论)。我很遗憾的告诉simpley板主:正如我所料,你的f函数效率的确不高。下面是测试结果(排在前面的效率高): comb_dd>comb_w_w>comb_w_if>f>comb>comb_fz,comb_dd函数不仅在运行时间上大获全胜,在占用空间上也是上述所有算法中最少的!由于不同的计算机数据可能不同,所以我没把测试数据帖出来。下面是测试函数,大家可以到自己的计算机上进行测试,看看到底哪个算法的效率高: 以下函数必须在visual c++或visual stdio.net下运行,在tc2.0下运行得到的测试结果没有任何意义!(因为tc工作在“控制台”模式下): #include <stdio.h> #include <time.h> #include <stdlib.h> #include <string.h> #include <math.h>

#define MAXN 100

#define M 5 /*默认为5选3(可以改为其他)*/ #define K 3 #define MIN_DURATION 10.0 #define MIN_ITER 10 #define SIGDIGIT 5 #ifndef CLOCKS_PER_SEC #define CLOCKS_PER_SEC CLK_TCK #endif

int a[MAXN]; int stack[MAXN]; int top;

static clock_t op_start, op_end; static double baseline, usec, resolution; static int i_s, prec;

/*以下为测试宏*/ #define BEGIN_TEST(name) printf("%20s ", name); fflush(stdout); i_s = 0; op_start = clock(); while(clock() == op_start); op_start = clock(); while ((op_end = clock()) - op_start < CLOCKS_PER_SEC * MIN_DURATION) { int q; for (q=0; q < MIN_ITER; q++) { #define END_TEST() i_s++; } } usec = 1000000.0 * (double) (op_end - op_start - baseline * i_s) / (double) CLOCKS_PER_SEC / (double) i_s; if (usec <= 0.0) usec = 0.0; printf("%9.3f usec\n", usec);

/*以下函数是simpley版主编写的非递归组合函数*/ void f(int n,int m){int i=0,j=0,a[10],s[10]; do{ for(;j<n;j++){ s[i]=j; if(i==m-1){/*for(l=0;l<m;l++)printf("%d",s[l]);printf("\n");*/} else a[i]=j,i++; } j=a[--i]+1;}while(a[0]!=n-1);}

/*以下采用“递归-分治策略”实现组合算法(这个算法的效率低)*/ void comb(int m,int k) { /*comb函数不是我编的,是从我以前的高程教材中找出来的*/ int i; for(i=m;i>=k;i--) { a[k]=i; if(k>1) comb(i-1,k-1); else { /* for(j=a[0];j>0;j--) printf("%4d",a[j]); printf("\n"); */ } } }

void comb_w_if(int m,int k) { top=0; while(m>=k||top) { /*从comb函数可以看出:递归执行条件为:m>=k||top*/ if(m>=k) { a[k]=m; if(k>1) {stack[top++]=m--; --k;} /*进栈,改变参数。(从comb函数中看出递归体执行条件为:m>=k&&k>1)*/ else { /*否则为递归结束条件*/ /*for(i=a[0];i>0;i--) printf("%4d",a[i]); printf("\n"); */ --m; /*--m:参数(递归体)回溯*/ } }else {m=stack[--top]-1; ++k;} /*满足回溯条件:出栈,恢复参数*/ } }

void comb_w_w(int m,int k) { /*该函数(及上面的函数)最大栈深为m-k,效果相当不错!*/

top=0; while(m>=k||top) { while(m>=k) { a[k]=m; if(k>1) stack[top++]=m--,--k; else { /*for(i=a[0];i>0;i--) printf("%4d",a[i]); printf("\n"); */ --m; } } if(top) m=stack[--top]-1,++k; } }

void comb_dd(int m,int k) { int i=1; if(m<k) return; while(i>0) { /*i为解空间树的高度*/ a[i]=m; if(i==k) { /*搜索达到“预定”深度*/ /*for(j=1;j<=a[0];j++) printf("%4d",a[j]); printf("\n");*/ while(a[i]==k-i+1) --i; /*从树的底层回溯*/ if(i!=k) m=a[i]-1; else --m; }else {++i; --m;} /*继续搜索(深度优先)*/ } }

void comb_fz(int m,int k) { struct{ int data; int parent; int level; }queue[10*MAXN]; /*定义队列:parent记录父结点,level记录树的层数(深度)*/ int front=0,rear=0; int i,t,data,level; if(m<k) return; for(i=m-k+1,t=m;i>0;--i,--t) { /*初始化队列*/ queue[rear].data=t; queue[rear].parent=-1; queue[rear].level=1; ++rear; } while(rear!=front) { data=queue[front].data; /*出队*/ level=queue[front].level; ++front; if(level==k) { /*输出结果*/ /* printf("%4d",data); parent=queue[front-1].parent; while(parent!=-1) { data=queue[parent].data; printf("%4d",data); parent=queue[parent].parent; } printf("\n");*/ } else { /*扩展:进行广度搜索*/ t=data-m-k+1; /*t定义了搜索范围*/ if(t<1) t=1; while(--data>=t) { queue[rear].data=data; queue[rear].parent=front-1; queue[rear].level=level+1; ++rear; } } } } int main(void) { double tempres; printf("clocks_per_sec = %d\n", CLOCKS_PER_SEC); resolution = 1000000.0 / CLOCKS_PER_SEC / MIN_ITER; printf("worst case resolution = %5.4f usec\n", resolution);

prec = 0; for (tempres = resolution; tempres < SIGDIGIT; tempres *= 10) { prec ++; } printf("meaningful precision = %d decimal digits\n", prec);

baseline = 0; BEGIN_TEST("(cache & vm load)"); END_TEST(); baseline = (double) (op_end - op_start) / (double) i_s; a[0]=K; /*以下进行对各种组合函数进行测试*/ BEGIN_TEST("comb testing..."); comb(M,K); END_TEST();

BEGIN_TEST("comb_fz testing..."); comb_fz(M,K); END_TEST();

BEGIN_TEST("comb_w_w testing..."); comb_w_w(M,K); END_TEST();

BEGIN_TEST("f testing..."); f(M,K); END_TEST();

BEGIN_TEST("comb_w_if testing..."); comb_w_if(M,K); END_TEST();

BEGIN_TEST("comb_dd testing..."); comb_dd(M,K); END_TEST(); return 0; }

以上的程序大概运行1分钟左右(我的机器要40s左右,不同的机器运行速度不一样),请耐心等待! 我不知道我编的这些函数是否有人读懂,如果有读懂的同学请“举手”,我们可以进一步交流。




要让一个男人破产,请给他一架相机,要让一个男人倾家荡产,请给他一架望远镜。
2005-06-11 09:39
stnlcd
Rank: 1
等 级:新手上路
帖 子:177
专家分:1
注 册:2004-11-21
收藏
得分:0 
以上测试函数在运行时建议关闭其他所有无关的应用程序!

要让一个男人破产,请给他一架相机,要让一个男人倾家荡产,请给他一架望远镜。
2005-06-11 09:50
likuan
Rank: 1
等 级:新手上路
帖 子:1
专家分:0
注 册:2005-6-5
收藏
得分:0 
good

程序中,生命在飞扬……
2005-06-11 10:35
牛虻
Rank: 1
等 级:新手上路
威 望:1
帖 子:472
专家分:0
注 册:2004-10-1
收藏
得分:0 
楼主适合搞学术报告

有个疑问,为什么就不能在TC下测试?你说的TC是在“控制台”下模式是什么意思?
是说输出无法控制么?

土冒
2005-06-11 10:58
stnlcd
Rank: 1
等 级:新手上路
帖 子:177
专家分:1
注 册:2004-11-21
收藏
得分:0 
你拿去试试不就知道了吗

要让一个男人破产,请给他一架相机,要让一个男人倾家荡产,请给他一架望远镜。
2005-06-11 11:45
simpley
Rank: 1
等 级:新手上路
帖 子:262
专家分:0
注 册:2005-2-23
收藏
得分:0 
我的测试结果和你相反。我把你的最快的程序试了一下,是8秒种。而我的,就是一闪而过,说一秒可以,说半秒也可以,总之没法用表测量。 我是在TURBOC2。0测试的。因为数字太小无法比较,所以我把组合数定为(30,3) 这样要打印的话要好几屏,程序一边打印一边计算,还是比较不出来,所以要把打印的步骤去掉,只显示他最后的结果。但打印的循环还保留。 我的程序就变为: int y=0; void f(int n,int m){int i=0,j=0,l,a[10],s[10]; do{ for(;j<n;j++){ s[i]=j; if(i==m-1){y++;for(l=0;l<m;l++);} 这里去掉了打印结果 else a[i]=j,i++; } j=a[--i]+1;}while(a[0]!=n-1);} main(){f(30,3);printf("%d",y);} 楼主的是: int y=0,a[5]; void comb_dd(int m,int k) { int i=1; if(m<k) return; while(i>0) { /*i为解空间树的高度*/ a[i]=m; if(i==k) {y=0; /*搜索达到“预定”深度*/ /*for(j=1;j<=a[0];j++) ; 这里去掉了打印结果 while(a[i]==k-i+1) --i; /*从树的底层回溯*/ if(i!=k) m=a[i]-1; else --m; }else {++i; --m;} /*继续搜索(深度优先)*/ } } main(){a[0]=3;comb_dd(3,30);printf("%d",y);} 打出来都是4060 楼主认为省了栈就会快捷,但却忘了回溯,搜索也是要耗时的,出栈是一步到位,而回溯要好几个步骤。

myQQ::445750010
2005-06-11 17:24
simpley
Rank: 1
等 级:新手上路
帖 子:262
专家分:0
注 册:2005-2-23
收藏
得分:0 
我大致看了一下汉诺塔程序,有一定创意,不错。
不过要指出的是,
1)这个程序同样也有效率问题。就是说它不一定会比几个参数都进站的效率高,它的优点是可以节省内存。(我认为效率一样)
2)这个方法只可用于参数变化有一定规律的情况。

myQQ::445750010
2005-06-11 19:05
stnlcd
Rank: 1
等 级:新手上路
帖 子:177
专家分:1
注 册:2004-11-21
收藏
得分:0 
在tc下测试,在tc下测试,结果是什么都有可能,在tc下的测试结果根本没有任何参考价值!必要要到vc下才可以!! 这句:“栈是一步到位”,我不是很清楚,请详细说明一下。还有,你理解的回溯是:在解空间树上查找/搜索不到问题的解的时候需要进行回溯重试,所以标准的回溯算法有相应的“剪枝函数”来去除树上不合理的分支,但在comb_dd上使用的迭代回溯本质上是对树的遍历,它只有到达叶子结点时才产生回溯(而一般的回溯问题:比如0/1背包问题,在树的任何一个不合理的结点上都有可能产生回溯)。事实上上面所有函数:comb(递归),comb_w_w,comb_w_if,comb_dd(comb_fz除外)和你的f函数,在本质上都是对解空间树的深度优先搜索(comb_fz是对树的广度优先),也就是进行树的先序遍历,都涉及到从树叶子结点回溯的问题,而不是我的comb_dd所特有的!这些个函数只是在具体实现的时候有差别,而这差别就体现了效率的高低。 还有你说我的hanoi程序“这个方法只可用于参数变化有一定规律的情况”的确如此!但是就hanoi程序而言(其它的我们不肯定),它的效率绝对比同样的“全部参数”进栈高!!!因为它的“全参数进栈”的非递归形式和hanoi_w_if,hanoi_w_w的形式是完全一样的!我就是从“全参数进栈”形式删除x,y,z的进栈语句才得到的,其他的问根本没改,事实上,就hanoi程序而言(有许多函数也是如此):x,y,z的进栈是根本不需要的! 我很喜欢和你这样的人交流,希望继续保持!谢谢你评论我的帖子! 最后:请用vc从新测试。

要让一个男人破产,请给他一架相机,要让一个男人倾家荡产,请给他一架望远镜。
2005-06-11 20:00
快速回复:[原创]递归的艺术-递归程序的非递归化
数据加载中...
 
   



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

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