【问题标题】:What is recursion [duplicate]什么是递归[重复]
【发布时间】:2012-11-30 13:47:19
【问题描述】:

可能重复:
Examples of Recursive functions

我一直在尝试将编程中的递归作为一个概念来研究(尽管我专门研究 Java),这是我最好理解的:

例如,在现实生活中,递归是指我们将两个镜子放在彼此的前面,并且它们之间产生的图像是递归的。

但是我在编程中没有得到这个算法? 有人可以给我一个简化的例子来理解递归吗?

【问题讨论】:

  • 递归编程(不管它是什么)可能在很大程度上基于递归编程。
  • 好的,CodingBarfield 和 Oded。非常有趣。但是您的递归不会终止,因此它在形式上是不正确的。说真的,让我们帮助原始海报。
  • @OllieJones - 那么你的答案在哪里?

标签: java recursion


【解决方案1】:

递归是一种编程技术,其中方法可以调用自身作为其计算的一部分(有时您可以拥有多个方法 - 这些方法通常会循环调用彼此)。

一个流行的例子是计算Fibonacci numbers

public static long fib(int n) {
    if (n <= 1) return n;
    else return fib(n-1) + fib(n-2);
}

两个基本组件是基本情况(示例中为n&lt;=1)和递归情况。

在创建递归算法时,您需要考虑基本情况以及如何使用递归情况来达到基本情况(否则最终会出现无限递归并破坏堆栈)。

【讨论】:

  • 但是一个不好的做法可能会导致 StackOverFlowError :-)
【解决方案2】:

基本上,函数是递归的

  1. 一个函数有一个简单的基本情况,当
  2. 所有其他情况都有简化为基本情况的规则。

例如,计算阶乘:

public static long factorial(int i)
{
    // This is the base case
    if(i == 0)
    {
         return 1;
    }
    else
    {
        // This reduces the problem to something closer to the base case
        return i * factorial(i - 1);
    }
}

【讨论】:

  • 递归函数,严格来说,不需要有基本情况。在许多语言(例如 Lisp)中,无限递归是实现无限循环的一种常见(有时是唯一的)技术。无限循环对于大多数运行时间不确定的现实世界程序(例如服务器)当然很有用
  • 我把这个问题理解为基本的递归,所以我假设是线性递归。
  • @slebetman 我不同意忙碌等待“对于大多数运行时间不确定的现实世界程序(例如服务器)很有用”
  • @GlaciesofPacis 您错过了示例中的递归...
  • @Puce:无限循环不等于忙等待。所有实际的服务器都是用无限循环和阻塞函数编写的(无论是 I/O 还是像套接字或轮询这样的多路复用函数)。唯一的例外是具有隐式事件循环的语言,如 Tcl 和 javascript。但在这种情况下,您仍然在解释器级别实现了无限事件循环。
【解决方案3】:

一些计算问题可以这样描述,即问题可以分解为更小的子问题。使用与主要问题相同的方法解决较小的子问题。如果较小的子问题只是较大问题的较小情况,那么它本身可以进一步分解。

最终,问题是如此之小,以至于无需进一步分解即可解决。这被称为基本情况。有了基本案例的解决方案,您就可以构建更大问题的解决方案。

假设你想找到 ab,其中 a 和 b 是正整数。可以看到,这和 * a(b-1) 是一样的。也就是说,a(b-1) 是比原始问题更小的问题,但仍需要与原始问题相同的技术来解决。要解决 a(b-1),您会看到它是 * a(b-2)

等等。

最终你会得到 a * a * a * ... * a(b-b)。我们知道 b-b = 0 和 a0 = 1。所以我们不必计算最后一点,因为我们已经知道答案。最终,ab = a * a * a * ... * 1。

所以 24 = 2 * 23 = 2 * 2 * 22 = 2 * 2 * 2 * 2 1 = 2 * 2 * 2 * 2 * 20 = 2 * 2 * 2 * 2 * 1。

要编写此程序,您首先要处理基本情况,然后使用递归来处理其他所有情况。

   pow(a, b){
       if(b == 0){
          return 1;
       }else{

          return a * pow(a, b - 1);
       }
   }

需要注意的是,这只是递归的基本思想。您在诸如斐波那契数问题的各种答案中看到的这些示例非常低效。您可以使用动态编程技术构建更高效的程序,该技术使用递归作为解决问题的机制之一。

【讨论】:

    【解决方案4】:

    递归编程是一种基于mathematical induction 思想的技术,其中方法或函数重复调用自身。因此,可以按如下方式实现阶乘函数:

    int fact(int n) {
        if (n < 2) {
                return 1;
        }
    
        return n * fact(n-1);
    }
    

    请注意,为了确保递归终止,您应该确保处理基本情况,即您为一些已知的简单输入定义了常量输出,并且您应该确保使函数的参数更简单每次迭代(在上面的示例中,将 n 减 1)。

    【讨论】:

      【解决方案5】:

      非常简单的“递归”代码。

      处理列表的顶部项目。从列表中删除它并调用代码来处理列表的顶部项目。

      树根有一定的长度,每 2/3 个根分裂成单独的根。每2/3根分裂成单独的根。裂片的裂片裂片每隔..等等。

      【讨论】:

        【解决方案6】:

        递归

        方法可以调用自己,这就是递归。经常使用方法的递归实现,因为它们会产生简洁优雅的代码,这比不使用递归的对应实现更容易理解。

        递归编程技术知道三个重要规则(经验法则):

        1. 基本情况:递归有一个基本情况。第一个语句总是有条件的并且有一个“return”。
        2. 子问题:递归调用解决在某种意义上更小的子问题,使其收敛到基本情况
        3. 无重叠:递归调用不应处理重叠的子问题。

        从性能的角度来看,非递归解决方案更快,并且通常需要更少的内存。 (例如二分查找)
        但有些任务非常复杂,以至于只有递归解决方案才能产生(或多或少易于理解的)代码。

        递归二分查找示例:

        public static int binSearch(int[] a, int key) {
           return binSearch0(a, key, 0, a.length - 1);
        }
        
        public static int binSearch0(int[] a, int key, int from, int to) {
            if (from > to) return -1;
            // looks strange but (from + to) / 2 can oveflow
            // (java bug which was active more than 10 years)
            int mid = from + (to - from) / 2;
            if (key < a[mid]) 
                return binSearch0(a, key, from, mid - 1);
            else if (key < a[mid]) 
                return binSearch0(a, key, mid + 1, to);
            else return mid;
         }
        

        在该示例中,您会看到所有三个规则(基本、子、非重叠)。
        并不是说递归函数通常有一个启动函数,例如示例中的“binSearch”。其中 'binSearch0' 是递归函数。

        【讨论】:

          猜你喜欢
          • 2019-11-14
          • 1970-01-01
          • 2019-06-02
          • 1970-01-01
          • 2011-01-25
          • 2011-06-02
          • 2013-08-02
          • 2011-04-10
          • 2010-10-30
          相关资源
          最近更新 更多