Algorithm 带堆栈的斐波那契递归
我已经问过了,但我还是很困惑。我想将递归函数转换为不带递归的基于堆栈的函数。以斐波那契函数为例:Algorithm 带堆栈的斐波那契递归,algorithm,Algorithm,我已经问过了,但我还是很困惑。我想将递归函数转换为不带递归的基于堆栈的函数。以斐波那契函数为例: algorithm Fibonacci(x): i = 0 i += Fibonacci(x-1) i += Fibonacci(x-2) return i (是的,我知道我没有设置基本情况,斐波那契的递归效率非常低) 如何使用显式堆栈实现这一点?例如,如果堆栈作为while循环,我必须跳出循环以计算第一个递归,并且我无法在第一个递归后返回到行并继续第二个递归。算法斐波那契(x):
algorithm Fibonacci(x):
i = 0
i += Fibonacci(x-1)
i += Fibonacci(x-2)
return i
(是的,我知道我没有设置基本情况,斐波那契的递归效率非常低)
如何使用显式堆栈实现这一点?例如,如果堆栈作为while循环,我必须跳出循环以计算第一个递归,并且我无法在第一个递归后返回到行并继续第二个递归。算法斐波那契(x):
algorithm Fibonacci(x):
stack = [1,1]
while stack.length < x
push to the stack the sum of two topmost stack elements
return stack.last
堆栈=[1,1]
而stack.length
您可以将调用之间的堆栈保留为某种缓存
这个堆栈不是“真正的堆栈”,因为您可以做的不仅仅是推送、弹出和检查它的空性,但我相信这就是您计划要做的。在伪python中
def fib(x):
tot = 0
stack = [x]
while stack:
a = stack.pop()
if a in [0,1]:
tot += 1
else:
stack.push(a - 1)
stack.push(a - 2)
return tot
如果不需要外部计数器,则需要推送元组来跟踪累计和,以及这是a-1
还是a-2
可能值得您花时间为代码运行(比如fib(3))显式地编写调用堆栈(手工编写,在纸上编写)(尽管先修复代码,以便它处理边界条件)。一旦您这样做了,就应该清楚如何在没有堆栈的情况下完成它
编辑:
让我们分析以下斐波那契算法的运行情况
def fib(x):
if (x == 0) or (x == 1):
return 1
else:
temp1 = fib(x - 1)
temp2 = fib(x - 2)
return temp1 + temp2
(是的,我知道这甚至不是一个低效算法的有效实现,我声明了比必要的更多的临时性。)
现在,当我们使用堆栈进行函数调用时,我们需要在堆栈上存储两种东西
(assign temp1, x = 1, -, -)
(assign temp1, x = 2, -, -)
(out , x = 3, -, -)
现在我们返回1并执行第二个调用并获取
(assign temp2, x = 0, -, -)
(assign temp1, x = 2, temp1 = 1, -)
(out , x = 3, -, -)
现在再次返回1
(assign temp1, x = 2, temp1 = 1, temp2 = 1)
(out , x = 3, -, -)
这返回2,我们得到
(out , x = 3, temp1 =2, -)
现在我们回到
(assign temp2, x = 1, -, -)
(out , x = 3, temp1 =2, -)
我们可以从中找到出路。
/0你的问题启发我写了一段代码,起初让我感到害怕,但现在我真的不知道该怎么想,所以这里是供你消遣的。也许这对理解事物有点帮助
// 0<x<100
int fib[100];
fib[1]=1;
fib[2]=1;
if(x<=2)
cout<<1;
else{
for(i=3;i<=x;i++)
fib[i]=fib[i-1]+fib[i-2];
cout<<fib[x];
}
这是对递归斐波那契函数实现执行的公然模拟。语言是C#。根据罗纳德·格雷厄姆、唐纳德·克努斯和奥伦·帕塔什尼克在《具体数学》中给出的斐波那契序列定义,对于参数0,函数返回0。维基百科也是这样定义的。忽略对否定参数的检查
通常,返回地址存储在堆栈上,执行只跳到正确的地址。为了模拟这一点,我使用了enum
enum JumpAddress
{
beforeTheFirstRecursiveInvocation,
betweenRecursiveInvocations,
afterTheSecondRecursiveInvocation,
outsideFibFunction
}
还有一个小小的状态机
堆栈上存储的帧定义如下:
class Frame
{
public int argument;
public int localVariable;
public JumpAddress returnAddress;
public Frame(int argument, JumpAddress returnAddress)
{
this.argument = argument;
this.localVariable = 0;
this.returnAddress = returnAddress;
}
}
这是一种C#class-a引用类型。堆栈保存对放置在堆上的对象的引用,因此在执行此操作时:
Frame top = stack.Peek();
top.localVariable = lastresult;
我正在修改堆栈顶部的引用仍然引用的对象,而不是副本
我对函数调用进行建模,方法是在堆栈上推一个帧,并将状态机中的执行地址设置为firstrecursiveinvocation之前的开始处
要从函数返回,我将lastresult
变量、pointOfExecution
变量设置为存储在顶部框架中的返回地址,并从堆栈中弹出该框架
这是代码
public static int fib(int n)
{
Stack<Frame> stack = new Stack<Frame>(n);
//Constructor uses the parameter to reserve space.
int lastresult = 0;
//variable holding the result of the last "recursive" invocation
stack.Push(new Frame(n, JumpAddress.outsideFibFunction));
JumpAddress pointOfExecution = JumpAddress.beforeTheFirstRecursiveInvocation;
// that's how I model function invocation. I push a frame on the stack and set
// pointOfExecution. Above the frame stores the argument n and a return address
// - outsideFibFunction
while(pointOfExecution != JumpAddress.outsideFibFunction)
{
Frame top = stack.Peek();
switch(pointOfExecution)
{
case JumpAddress.beforeTheFirstRecursiveInvocation:
if(top.argument <= 1)
{
lastresult = top.argument;
pointOfExecution = top.returnAddress;
stack.Pop();
}
else
{
stack.Push(new Frame(top.argument - 1, JumpAddress.betweenRecursiveInvocations));
pointOfExecution = JumpAddress.beforeTheFirstRecursiveInvocation;
}
break;
case JumpAddress.betweenRecursiveInvocations:
top.localVariable = lastresult;
stack.Push(new Frame(top.argument - 2, JumpAddress.afterTheSecondRecursiveInvocation));
pointOfExecution = JumpAddress.beforeTheFirstRecursiveInvocation;
break;
case JumpAddress.afterTheSecondRecursiveInvocation:
lastresult += top.localVariable;
pointOfExecution = top.returnAddress;
stack.Pop();
break;
default:
System.Diagnostics.Debug.Assert(false,"This point should never be reached");
break;
}
}
return lastresult;
}
公共静态int fib(int n)
{
堆栈=新堆栈(n);
//构造函数使用参数来保留空间。
int lastresult=0;
//保存上次“递归”调用结果的变量
stack.Push(新帧(n,JumpAddress.outsideFibFunction));
JumpAddress Pointof Execution=JumpAddress.Before the FirstRecursive调用;
//这就是我建模函数调用的方式。我在堆栈上推一个框架并设置
//帧上方存储参数n和返回地址
//-外部功能
while(执行点!=JumpAddress.outsideFibFunction)
{
框架顶部=stack.Peek();
开关(执行点)
{
FirstRecursive调用之前的case JumpAddress.before:
if(top.argument)你应该澄清一点。递归也使用堆栈(程序的堆栈),而显式堆栈不是(在我看来)实现函数的最有效的方法。除此之外,语言的堆栈实现差异很大,因此,如果您真的在追求一种语言,那么了解您使用的是哪种语言将非常有用。Fibonacci可能不是最好的示例,因为(至少在概念上),它需要对递归调用的结果进行操作,以计算给定帧的结果。如果您实际尝试实现的内容不需要这一点,那么使用显式堆栈实现会更简单。事实上,我尝试做的事情确实需要这一点,这就是为什么我对如何实现它如此困惑的原因。
public static int fib(int n)
{
Stack<Frame> stack = new Stack<Frame>(n);
//Constructor uses the parameter to reserve space.
int lastresult = 0;
//variable holding the result of the last "recursive" invocation
stack.Push(new Frame(n, JumpAddress.outsideFibFunction));
JumpAddress pointOfExecution = JumpAddress.beforeTheFirstRecursiveInvocation;
// that's how I model function invocation. I push a frame on the stack and set
// pointOfExecution. Above the frame stores the argument n and a return address
// - outsideFibFunction
while(pointOfExecution != JumpAddress.outsideFibFunction)
{
Frame top = stack.Peek();
switch(pointOfExecution)
{
case JumpAddress.beforeTheFirstRecursiveInvocation:
if(top.argument <= 1)
{
lastresult = top.argument;
pointOfExecution = top.returnAddress;
stack.Pop();
}
else
{
stack.Push(new Frame(top.argument - 1, JumpAddress.betweenRecursiveInvocations));
pointOfExecution = JumpAddress.beforeTheFirstRecursiveInvocation;
}
break;
case JumpAddress.betweenRecursiveInvocations:
top.localVariable = lastresult;
stack.Push(new Frame(top.argument - 2, JumpAddress.afterTheSecondRecursiveInvocation));
pointOfExecution = JumpAddress.beforeTheFirstRecursiveInvocation;
break;
case JumpAddress.afterTheSecondRecursiveInvocation:
lastresult += top.localVariable;
pointOfExecution = top.returnAddress;
stack.Pop();
break;
default:
System.Diagnostics.Debug.Assert(false,"This point should never be reached");
break;
}
}
return lastresult;
}