Fibonacci 数列:
指的是这样一个数列:1、1、2、3、5、8、13、21、34……即从第三项开始,每一项等于它的前两项之和。
递归
程序调用自身的编程技巧称为递归。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小
递归的两个必要条件
- 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
- 每次递归调用之后越来越接近这个
在讲Fibonacci数列之前,我们来看一个更简单的例子求N的阶乘有助于我们理解递归。
源代码:
public class Sum {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); //输入一个数字 求阶乘
System.out.println(factor(n));
}
public static int factor (int n) {
if (n==1) {
return 1;
}
return n* factor (n-1); //递归求阶乘
}
}
1!=1
2!=2* 1!
3!=3* 2!
4!=4* 3!
通过上面的总结可以发现当求3的阶乘时候,就是3x2x1,也是相当于求3的阶乘等于求3乘以2的阶乘。推广以后就是: F(N) = N * ( N - 1 )。那么在代码中我们可以写一个factor方法,通过不断递归,来求解阶乘,n=1的时候是递归的终止条件。下面通过图解来理解这个递归是如何做到阶乘的。
通过这个图可以看出想求3!那么就要等待一个返回值,这样一路"递"下去,直到遇见终止的条件才会按原路"归"回去。此时我想对递归的理解就更加透彻了。
回到问题的开始,求Fibonacci 数列
-可以直接写出求他的公式 F ( N ) = ( N - 1 ) + ( N - 2 )
递归源代码:
public class Fibonacci {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println(n);
}
public static int fib(int n) {
if (n == 1 || n == 2) {
return 1;
}
return fib(n - 1) + fib(n - 2);
}
}
但是我们发现这个代码效率似乎没那么高效,原因总是有重复的计算,仅是计算一个第六项,F(2)就被重复计算了5次。显然不够高效。
迭代源代码:
public class FibonacciP {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println(fib(n));
}
public static int fib(int n) {
int a = 1;
int b = 1;
int c = 1;
for (int i = 3; i <= n; i++) {
c = b + a;
a = b;
b = c;
}
return c;
}
}
此时通过不断地将第一个数与第二个数相加,再将和赋给新一轮的第二个数,原来的第二个数变成新的一轮的第一个数。这样优化使得代码更加高效,不会有大量重复的计算。
总结
- 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰
- 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
- 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。