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
(是的,我知道这甚至不是一个低效算法的有效实现,我声明了比必要的更多的临时性。)

现在,当我们使用堆栈进行函数调用时,我们需要在堆栈上存储两种东西

  • 返回结果的位置
  • 局部变量的空间
  • 在我们的例子中,我们有三个可能的地方可以返回

  • 外线的人
  • 分配给temp1
  • 分配给临时2
  • 我们还需要三个局部变量x、temp1和temp2的空间。让我们检查fib(3)

    当我们最初调用fib时,我们告诉堆栈我们希望返回到我们从何处开始的位置,x=3,并且temp1和temp2未初始化

    接下来,我们推到要分配temp1、x=2的堆栈上,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;
    }