(兔子繁殖问题)斐波那契数列:递归&非递归解法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afei__/article/details/83082572

题目

假设一对幼年兔子需要一个月长成成年兔子,一对成年兔子一个月后每个月都可以繁衍出一对新的幼年兔子。不考虑死亡的情况,问第 N 个月时共有多少对兔子?

这是一个典型的斐波那契数列问题,即

第一个月有一对兔子;

第二个月兔子长大了,但还是只有一对兔子;

第三个月多了一对幼年兔子,共有两对兔子了;

第四个月又生了一对幼年兔子,同时第三个月的幼年兔子长成了成年兔子;

……

那么兔子数量的规律即:1,1,2,3,5,8,13,21……

设定第 0 个月数量为 0,观察可得,从第 2 个月开始,每个月的兔子数量都等于前两个月兔子数量的和。这就是著名的斐波那契数列。

 

递归解法

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        System.out.print("请输入月份:");
        int n = new Scanner(System.in).nextInt();
        System.out.println("兔子的对数为:" + getNums(n));
    }
    
    public static int getNums(int n) {
        if (n <= 1) return n;
        return getNums(n - 1) + getNums(n - 2);
    }

}

这个比较简单易懂,设定 n 为 0 或者 1 的时候为递归的跳出条件,其余情况直接使用递归返回上两个月的数量和即可。

 

非递归解法

如果不使用递归,那我们需要开辟一块数据,来存储我们“前面月份”得到的数量。

我们使用 动态规划 的思想,即我们需要求解第 N 个月的兔子数量,依赖于求解第 N-1 个月和第 N-2 个月的兔子数量这两个子问题,我们可以自底向上的求解出每个子问题的解,并保存起来,然后最终求解第 N 个月的兔子数量时直接使用已知的子问题的解即可。

代码如下:

    public static int getNums(int n) {
        int[] nums = new int[n + 1];
        nums[1] = 1; // 初始状态:nums[0] = 0, nums[1] = 1
        for (int i = 2; i < nums.length; i++) {
            nums[i] = nums[i - 1] + nums[i - 2];
        }
        return nums[n];
    }

我们使用了 nums 这个数组保存了每个月兔子数量的值,其实我们也大可不必这么奢侈,在这个问题中我们其实只需要知道前两个月的情况即可,那么我们可以只创建一个长度为 2 的数组分别保存 N-2 个月和 N-1 个月的情况即可。

代码如下:

    public static int getNums(int n) {
        if (n <= 1) return n;
        // 使用一个长度为 2 的数组存储前两位的值
        int[] nums = new int[] { 0, 1 };
        int result = 0;
        for (int i = 2; i <= n; i++) {
            result = nums[0] + nums[1];
            nums[0] = nums[1];
            nums[1] = result;
        }
        return result;
    }

 

总结

使用非递归的解法(动态规划),效率上来说也会比递归好很多,不仅仅是因为递归会使得栈的深度变得很大,而且在递归的过程中,求解 getNums(n - 1) + getNums(n - 2) 时,在不同的 n 值下,还会产生很多重复的计算。而非递归的方式仅仅只是获取到之前的值使用,不存在重复的计算消耗。

猜你喜欢

转载自blog.csdn.net/afei__/article/details/83082572