java - 比较递归斐波那契与递归阶乘
问题描述
这是Sedgewick 和 Wayne的《计算机科学跨学科方法》一书中的练习 2.3.2 :
编写一个以整数 n 作为参数并返回 ln(n!) 的递归函数。
我用Java写了一个递归方法如下:
public static double lnFactorial(int n)
{
if (n == 1) return 0;
return Math.log(n) + lnFactorial(n-1);
}
它在 n 高达大约 10000 时运行得非常快。但是,用于计算第 n 个斐波那契数的看起来相同的递归方法(如下)需要永远计算第 50 个斐波那契数。
public static long fibonacci(int n)
{
if (n == 1) return 1;
if (n == 2) return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
为什么会有如此巨大的差异?是不是因为斐波那契的递归方法在每次迭代中都会调用自己两次?我仍然无法理解其中的区别!
解决方案
第一种方法递归调用自己一次,因此复杂度为O(n)。第二种方法递归调用自身两次,因此每个递归深度调用的数量加倍,这使得方法O(2 n )。
O(n)和O(2 n )之间的差异是巨大的,这使得第二种方法更慢。
使用第二种方法计算第 50 个数字需要2 50 = 1125899906842624 次递归调用。使用第一种方法只需要50次递归调用。(注意:这些数字不一定准确。我只是添加它们来说明线性和指数方法的大小。)
这里要理解的重要一点是,第二种方法多次计算相同n s 的斐波那契数。查看使用n - 1和n - 2递归调用自身的初始调用。当您使用n-1查看调用时,您会看到它使用n-2和n-3调用自身。你注意到问题了吗?该方法已经用n - 2调用了两次。当你用n - 2查看第一次调用时,它甚至已经用n - 3调用了自己两次。随着递归深度的增加,这种情况会变得越来越糟。
请注意,第一种方法不会以任何相同的值调用自身两次。
推荐阅读
- javascript - 是否可以使用 JavaScript 将行号打印到控制台或 NetSuite 上的执行日志?
- xamarin - 如何使用共享 VM 将属性值从一个选项卡屏幕共享到另一个选项卡屏幕?
- c# - EF6 - 为什么我的所有 DbSet 在枚举它们时都会抛出 InvalidOperationException?
- mongodb - 如何在mongodb的一次执行中更新具有不同值的多行?
- amazon-web-services - 使用 AWS Glue 作业将缺失的列值设置为默认值
- dynamics-crm - 无法从 UCI 的子网格中隐藏查看所有记录按钮
- java - 读取pdf文件中的文本并拆分为多个pdf文件
- shell - 将多个文件合并为一个没有标题
- python - 杀死服务后 Systemd 写入文件
- java - 如何在 Java 中获取类的所有子类?