首页 > 学技术 > 技术网文 > C/C++ > 正文

[保留] 求助一个用堆栈来模拟fibr递归函数实现


来源 chinaunix.net kuqin整理

利用一个堆栈来模拟递归函数的实现,以fibr函数为例。
要求效率比递归实现高,空间使用率尽量的高,不知道各位大侠有什么好的点子?在下愿闻赐教



 feiyang21687 回复于:2007-02-02 22:18:03

我自己顶,希望大家来谈谈自己的想法。


 feiyang21687 回复于:2007-02-03 01:24:03

来个具体的吧,对于这个递归函数用堆栈来模拟:f(n) = f(n/2) + f(n/2 +1) + n
堆栈里面的数据结构应该是这样的(设n=7):7,3,1,2,1,1,4,2,1,1,2,1,1。就是一个二叉树,不过不知道怎么写代码可以高效的实现这种入栈。


 feiyang21687 回复于:2007-02-03 01:38:12

更正上面的式子,原本的式子是要计算f( more(n/2) ) + f( less(n/2) ) + n,其中more(n/2)表示不大于n/2的最大的整数,less(n/2)表示大于n/2最小的整数,上面的描述有误,不好意思。大家来讨论一下思路。


 feiyang21687 回复于:2007-02-03 22:19:16

怎么没有人啊?自己顶


 tyc611 回复于:2007-02-03 23:03:10

>> 来个具体的吧,对于这个递归函数用堆栈来模拟:f(n) = f(n/2) + f(n/2 +1) + n
>> 更正上面的式子,原本的式子是要计算f( more(n/2) ) + f( less(n/2) ) + n,其中more(n/2)表示不大于n/2的最大的整数,less(n/2)表示大于n/2最小的整数,上面的描述有误,不好意思。

我觉得,根据你下面的描述,上面的递归公式没错啊


 tyc611 回复于:2007-02-03 23:14:47

如果是上面那个意思的话,由递推公式:f(n) = f(n/2) + f(n/2 +1) + n
可知,f(2) = f(1) + f(2) + 2,无限递归了,所以应该给f(1)和f(2)赋初值
然后可以按下面的方法直接递推计算:
int f[NUM];
f[1] = some_value;
f[2] = some_value;

for (int i = 3; i <= n/2 + 1; ++i)
    f = f[i/2] + f[i/2 + 1] + i;

int fn = f[n/2] + f[n/2 + 1] + n;

空间需要 n/2 + 1 + 1 个数组元素(下标为0的数组元素没用),时间上比递归节约了很多,因为用表保存了中间结果,避免了不少重复计算

另外,我并没有直接把递归转换成栈实现,方法是可行的,但是这种转换方法比较难懂,如果需要自己可以查找资料,讲算法的书上有些有

[ 本帖最后由 tyc611 于 2007-2-3 23:17 编辑 ]


 feiyang21687 回复于:2007-02-03 23:34:44

忘记了,f(1) = 1。
对于你说的:
引用:原帖由 tyc611 于 2007-2-3 23:14 发表
如果是上面那个意思的话,由递推公式:f(n) = f(n/2) + f(n/2 +1) + n
可知,f(2) = f(1) + f(2) + 2,无限递归了,所以应该给f(1)和f(2)赋初值
然后可以按下面的方法直接递推计算:
int f[NUM];
f[1] = some ... 


这个是用迭代的方法。不过我想用堆栈来模拟一下递归的过程。现在关键的问题没有好方法来实现:(7,3,1,2,1,1,4,2,1,1,2,1,1)这种进栈顺序。不知道老大有没有好的方法?


 tyc611 回复于:2007-02-03 23:54:02

>> 7,3,1,2,1,1,4,2,1,1,2,1,1
这个是怎么得来的?

如果是一个返回点还好转换一点,有两个点转换起来比较麻烦,但是有一种模式可套用,我也说不清楚,建议找资料看吧

可以看看这本书,下面的链接能免费试读,在45页有讲递归转非递归的栈实现方法(需要下载阅读器)
http://www.china-pub.com/computers/common/info.asp?id=11749

[ 本帖最后由 tyc611 于 2007-2-3 23:55 编辑 ]


 cobras 回复于:2007-02-04 11:13:12

如下是模拟栈的递归实现(如果学过编译原理就不难理解)
fibr函数是C递归代码,main中的是模拟递归代码

#include <stdio.h>
#include <stdlib.h>

#define ALEN(v) (sizeof(v)/sizeof((v)[0]))

int fibr(int n){
printf("%d ", n);
return n<=1?1:fibr(n/2)+fibr((n+1)/2)+n;
}

int ip;
int ax;
int bp;
int ss[100];
int sp = ALEN(ss);

void push(int n){
if(--sp<0){
sp=ALEN(ss)-1;
printf("\nStack Overflow!!\n");
exit(-1);
}
ss[sp]=n;
}

int pop(void){
if(sp>=ALEN(ss)){
sp=0;
printf("\nStack Overflow!!\n");
exit(-1);
}
return ss[sp++];
}

void call(void){
push(ip+1);
ip=0;
}

void ret(void){
ip = pop();
}

int main(void){
printf("= %d\n", fibr(7));
push(7);
ip=-2;
call();
while(ip>=0){
switch(ip){
case 0:
push(bp);
bp=sp;
printf("%d ", ss[bp+2]);
if(ss[bp+2]<=1){
ax=1;
ip=4;
}else{
ip=1;
}
break;
case 1:
push(ss[bp+2]/2);
call();
break;
case 2:
sp+=1;
push(ax);
push((ss[bp+2]+1)/2);
call();
break;
case 3:
sp+=1;
ax+=pop()+ss[bp+2];
ip=4;
break;
case 4:
bp=pop();
ret();
break;
}
}
sp+=1;
printf("= %d\n", ax);
return 0;
}



 feiyang21687 回复于:2007-02-04 11:15:44

引用:>> 7,3,1,2,1,1,4,2,1,1,2,1,1
这个是怎么得来的?


这个序列就是递归调用时候堆栈里面的存储数据的顺序,我是通过把递归式展开得到的这个序列。这个序列可以用一棵二叉树来描述。
PS.你也是北邮的吗?


 cobras 回复于:2007-02-04 11:18:46

修正LZ递推式f(n)=f(n/2)+f((n+1)/2)+n,f(1)=1


 tyc611 回复于:2007-02-04 11:54:17

引用:原帖由 cobras 于 2007-2-4 11:18 发表
修正LZ递推式f(n)=f(n/2)+f((n+1)/2)+n,f(1)=1 


楼主的递推式是正确的
引用:
原本的式子是要计算f( more(n/2) ) + f( less(n/2) ) + n,其中more(n/2)表示不大于n/2的最大的整数,less(n/2)表示大于n/2最小的整数


例如:
当 n = 2 时,more(n/2) = more(2/2) = 1, less(n/2) = less(2/2) = 2,所以 f(2) = f(1) + f(2) + 2 (无限递归)
而你的递推式有:f(2) = f(1) + f(1) + 2,所以你的是不正确
(注意:上面递推式中n/2作了向下取整计算)

另外,按照楼主的要求,必须给定 f(1) 和 f(2) 的初值才行
引用:more(n/2) <= n/2 <less(n/2),more和less相差1



 tyc611 回复于:2007-02-04 11:56:01

引用:原帖由 feiyang21687 于 2007-2-4 11:15 发表

这个序列就是递归调用时候堆栈里面的存储数据的顺序,我是通过把递归式展开得到的这个序列。这个序列可以用一棵二叉树来描述。
PS.你也是北邮的吗? 


不知道你是按哪个公式展开的,我展开结果不是这样的

PS:不是的^_^


 cobras 回复于:2007-02-04 11:58:57

用我的递推式才可得到楼主需要的序列


 tyc611 回复于:2007-02-04 12:21:12

引用:原帖由 cobras 于 2007-2-4 11:13 发表
如下是模拟栈的递归实现(如果学过编译原理就不难理解)
fibr函数是C递归代码,main中的是模拟递归代码

#include <stdio.h>
#include <stdlib.h>

#define ALEN(v) (sizeof(v)/sizeof(( ... 


很不错,赞一个
引用:
用我的递推式才可得到楼主需要的序列


恩,不过,就怕他序列推错了,呵呵


 upstorm 回复于:2007-02-04 16:06:05

引用:原帖由 cobras 于 2007-2-4 11:13 发表
如下是模拟栈的递归实现(如果学过编译原理就不难理解)
fibr函数是C递归代码,main中的是模拟递归代码

#include <stdio.h>
#include <stdlib.h>

#define ALEN(v) (sizeof(v)/sizeof(( ... 



强:D

如果按楼主所说的,要求效率高,空间利用率高,这不是摆明了要再去增加一个寄存器吗?


 feiyang21687 回复于:2007-02-05 00:07:17

引用:

        while(ip>=0){
                switch(ip){
                case 0:
                        push(bp);
                        bp=sp;
                        printf("%d ", ss[bp+2]);
                        if(ss[bp+2]<=1){
                                ax=1;
                                ip=4;
                        }else{
                                ip=1;
                        }
                        break;
                case 1:
                        push(ss[bp+2]/2);
                        call();
                        break;
                case 2:
                        sp+=1;
                        push(ax);
                        push((ss[bp+2]+1)/2);
                        call();
                        break;
                case 3:
                        sp+=1;
                        ax+=pop()+ss[bp+2];
                        ip=4;
                        break;
                case 4:
                        bp=pop();
                        ret();
                        break;
                }
        }


[size=3]小弟愚笨,这段代码不甚明白,是否可以说明一下。多谢了。[/size]




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=895249
转载请注明作者名及原文出处



收藏本页到:      

收藏本页到: