【问题标题】:Recursively calling the same method twice递归调用同一个方法两次
【发布时间】:2010-09-28 10:41:12
【问题描述】:

在斐波那契数列中,我见过递归调用相同方法两次的常规实现:

public void fibonacci(int i)
{
fibonacci(1) + fibonacci(2);
}

现在这种方法不是我所看到的完全复制或解决问题的正确方法,但我已经看到两种方法像上面一样加在一起。所以该方法不是递归调用的,而是递归调用两次。在 C# 中编写这样的代码时究竟会发生什么?这两种方法是否在单独的线程上运行?幕后发生了什么?

【问题讨论】:

    标签: c# .net fibonacci


    【解决方案1】:

    不,他们一个接一个地被调用。除非您明确要求(使用 System.Threading 的东西),否则不会创建其他线程。我不确定它们被调用的顺序,但我猜是从左到右。 C#规范肯定有它。

    【讨论】:

      【解决方案2】:

      它们是按顺序计算的。即fibonacci(1) 在fibonacci(2) 之前被评估。如果您想将其可视化,则可以先下第一个调用树(使用它自己的“拆分递归”),然后备份,然后再下第二个三个。

      顺便说一句,这通常被用作不如何使用递归的示例,因为这是评估斐波那契数列的一种明显但低效的方式。首选的选项是迭代方法(计算出第一个数字,然后是下一个数字,等等,直到所需的数字),或者更好的是封闭形式的方程。

      【讨论】:

        【解决方案3】:

        这是考虑计算机的工作方式很有用的时候之一。

        让我们从函数开始。我打算用 Python 风格的伪代码编写它,因为我希望你暂时不要考虑语言。

        def fib(n):
            if n == 0: return 0
            if n == 1: return 1
            if n >  1: return fib(n-2) + fib(n-1)
        

        现在让我们来看看 fib(2)

        fib(2)有n>1,所以需要第三个分支,

           return fib(2-2) + fib(2-1)
        

        所以它用0调用fib(),也就是0

           return 0 + fib(2-1)
        

        用 1 调用 fib()

           return 0 + 1
        

        我们看到结果 1. 现在考虑 fib(3):

        n>1,所以

        return fib(3-2)+fib(3-1)
        

        由于 3-2 为 1,我们得到第一项的 fib(1),即 1:

        return 1 + fib(3-1)
        

        现在当我们调用 fib(3-1),也就是 fib(2) 时,我们还没有直接的答案; n>1。于是就变成了

        return 1 +
            return fib(2-2) + fib(2-1)
        

        变成了

            return 0 + 1
        

        我们会回到之前的调用

        return 1 + 1
        

        并得到值 2。所以,你看,没有单独的线程,它只是在表达式中运行。我将把它作为一个练习来制定 fib(4) 的示例;不过,我敢打赌,如果你这样做,你会成功的。

        小测验:为什么迭代版本的效率如此之高?

        【讨论】:

        • 迭代版本的复杂度是O(n),而递归版本的复杂度是O(Fib(n)),因为后者总是以1 + 1 + 1 + ...跨度>
        • ...封闭式版本是 O(1)
        • @dancavallaro:从上面的数据中不能轻易得出结论,还是我遗漏了什么?在这里找到它:en.wikipedia.org/wiki/Fibonacci_number#Closed_form_expression
        • Piskvor,将算术运算(包括求幂和下限)计数为单个运算是 O(1),因为它可以通过 floor(pow(φ,n) 精确计算/sqrt(5)+1/2)。每次进行 3 次操作。 ...
        • ... 这也是个骗局,因为在真正的计算机中,随着 n 变大,您必须使用 bignums 或更糟;您还需要一个任意大的常数 φ,这意味着您必须有效地计算相同的东西来拖出一个值。现在它比实际指令中的 O(1) 差得多。
        【解决方案4】:

        在 C# 中,您可以按照您的建议轻松实现该方法 - 它可能看起来像这样

        public static int Fib(int i) {
           if (i == 0) return 0;
           if (i == 1) return 1;
           return Fib(i - 2) + Fib(i - 1);
        }
        

        但是,这里没有魔法。它只是一次递归调用,然后是另一个。 IE。所有代码在同一个线程上按顺序运行。

        由于每次迭代需要两次递归调用,性能也很差,因此即使i 的值非常小,此实现也可能没有用。如果您需要性能,最好自己处理状态来避免递归调用。

        【讨论】:

          猜你喜欢
          • 2022-08-18
          • 2018-02-06
          • 2014-09-10
          • 1970-01-01
          • 2012-02-23
          • 2018-01-23
          • 2015-08-14
          • 1970-01-01
          • 2018-11-07
          相关资源
          最近更新 更多