当前位置: 首页 > 知识库问答 >
问题:

为什么这个递归函数的工作方式和另一个不一样呢?

贺奕
2023-03-14

我一直在做一个项目,在汇编中写一个递归函数,在那里它将计算斐波那契数。一开始我用Java代码写的:


public class Fibonacci {

    public static int fibonacci(int n) 
    {
        if(n <= 1)
            return n;

        return fibonacci(n-1) + fibonacci(n-2);
    }

这个递归函数工作得非常好。虽然当尝试在汇编代码中实现它时,我没有得到我所期望的结果。在排除了一段时间的故障后,我在Java编写了大致等价的代码:

    static int n;
    static int rec1; 
    static int rec2; 
    static int result; 

    public static int asmFibonacci(int n) 
    {
        if(n <= 1) {
            result = n;
            return 0;
        }
    
        n = n-1;
        asmFibonacci(n);
        rec1 = result;
    
        n = n-1;
        asmFibonacci(n);
        rec2 = result;

        result = rec1 + rec2;
        
        return 0;
    
    }

这个函数得到了和我在汇编代码中得到的一样错误的结果,虽然我仍然不明白为什么,我遗漏了什么?这两个函数重复的次数相同。

结果
ASMFibonacci
0 1 1 2 2 3 3 4 4 5
Fibonacci
0 1 1 2 3 5 8 13 21 34

任何帮助都将不胜感激。

更新

在将rec1和rec2也推到堆栈之后,我使汇编中的Fibonacci子程序按预期工作。

共有1个答案

东方俊力
2023-03-14

正确的递归代码不会使用静态存储;它不是可重入的,因此不适合递归。

“re-entrant”意味着当您在另一个调用的过程中可以调用它,例如,当您在Fib(5)的过程中计算Fib(3)时,不会弄乱Fib(5)以后想要重新读取的任何变量。例如静态int rec1;

只使用局部变量。

在asm中,这意味着堆栈空间或调用保留寄存器。(使用调用保留寄存器意味着将调用方的值保留在堆栈上)。

还要注意,静态int n被您的int n函数arg(至少是您在Java中编写它的方式)所遮蔽,因此您避免了尝试使用静态n的bug!static intrec2是无用的,因为您不需要跨任何内容保存它。

此外,静态int result;完全是疯狂的。递归函数返回它们的结果,而不仅仅是对全局/静态变量产生副作用!

在asm中,习惯使用寄存器;并不是所有的东西都应该从带有命名标签的静态存储中存储和重新加载。即使是调试模式的C编译器也不会这样做(它会将堆栈空间用于本地文件)

请注意,朴素的双递归Fibonacci只需要一个整数大小的空间就可以在调用中保存内容。在第一次调用中,保存n。在第二次调用中,您需要保存第一次调用的结果,但是n已经完成了。您可以为此回收相同的保留呼叫的注册表。

当然,递归Fibonacci对性能来说完全是垃圾,并且仅作为递归练习有用。简单迭代重复x+=y的性能近似为O(Fib(n))vs.O(n);交换(x,y)x+=y;y+=x;参见汇编语言(x86):如何创建一个循环来计算高效循环的斐波那契序列。

 类似资料:
  • 我正在尝试使用Cython来加速我的Python脚本的某些部分。一个关键部分将函数应用于Pandas数据框架;由于这已经完成了很多次,我想用Cython编写这些函数以加快计算速度。函数如下,并且在同一个Jupyter笔记本单元格中: 笔记本单元按编写的方式成功运行,因此我认为两个函数都编译成功。但是,此代码按预期运行: 鉴于本规范: 不运行,并返回"NameError: name'evenness

  • 很抱歉问了一个关于已经讨论过很多次的论点的非常基本的问题,我就是想不出答案。我试着在论坛上搜索已经在主题上提出的问题,但没有找到确切的答案(或者不理解)。 当以不同顺序调用时,为什么此函数会打印两次从i到10的数字?它不应该按同样的顺序打印出来吗?我一直听说递归就是这样工作的:每个函数在其代码中调用另一个相同的函数,只应用于较小的域,直到满足结束条件为止。此时,它应该返回(回溯)到原始函数;这就是

  • 我试图编写一个函数repeat(s:String,n:Int),它将串接字符串s n次并返回它,但由于某种原因,我没有得到正确的结果,并且得到了一个错误,即它不是尾部递归的,我在逻辑上很难理解为什么它不是尾部递归的。 在连接完成之前,是否必须处理递归?我该如何解决这个问题?使递归重复(s,n-1)不起作用,因为它会递归s太多次,但我不确定还有什么其他方法可以做到。 ps:我这么做主要是为了练习递归

  • 我是计算机视觉新手,还没有真正学习过阈值、模糊或其他过滤器的教程。我使用下面两段代码找出图像中的轮廓。一方面,这种方法是有效的,但另一方面,它不是。我需要帮助理解发生这种情况的原因,以便说服自己背景中发生了什么。 工作代码段: 不工作的代码段 如果有人能找出这里发生的错误的原因,我将不胜感激。 我所面对的错误是: 回溯(最近一次调用last):文件“convexhull.py”,第27行,在im2

  • 我在构建递归函数时继续遇到一个问题,其中返回的值与我期望返回的值不同。我很确定这与函数的递归性质有关,但我不知道发生了什么。 在这个缩小的例子中,我有一个带有字符串的函数foo和一个默认值为0的int。给定字符串“测试”并且没有整数,我希望递归函数为每个调用增加numberToBack并将新值传递给下一个调用。这一定是部分正确的,因为如果我在到达基本情况时cout numberToBack,我将获

  • 我想将工作簿的一页(包括样式)复制到新的工作簿。 我尝试对所有单元格进行迭代 抛出java.lang.IllegalStateException:超出了单元格样式的最大数目。在.xls工作簿中最多可以定义4000个样式 抛出java.lang.IllegalArgumentException:此样式不属于提供的工作簿。是否尝试将样式从一个工作簿分配到另一个工作簿的单元格? 正确的样式复制方法是什么