在我的大学里,我被要求为斐波那契数列写一个JAVA程序。我使用递归来编写这个程序。
但是,助教讲师说我的算法不够高效,并要求我进行分析。他补充说,按照惯例,迭代比递归更适合在该程序中使用。
如何分析我们的算法?如何检查迭代和递归的空间和时间复杂度?就在那时,我发现这些东西和程序的正确性一样重要。
算法的复杂度与此无关: 当您使用递归时,每次调用都会在堆栈上创建一个新框架-因此,如果递归太深,则可能遇到堆栈溢出:)
通过使用迭代-您正在循环运行(潜在地)相同的空间(覆盖参数以前的值),这更快,从StackOverflow角度来看也更安全。
int fib(int n) {
if (n < 2) { return 1; }
return fib(n-1) + fib(n-2)
}
你将计算fib(n-1),这将计算fib(n-2)和fib(n-3)。但是为了计算fib(n),你将已经计算了fib(n-2)。为了改进此问题,您需要存储临时结果。通常使用迭代更容易,并从i = 0开始逐步增加到n。这样,您可以轻松存储最后两个结果并避免重复计算相同的值。
一个判断算法效率的简单方法是尝试解决一些越来越难的例子,你也可以更精确地计算。以斐波那契数列为例。调用fib(n)需要复杂度O(fib(n)) = O(fib(n-1)) +O(fib(n-2)) + 1
(我们只考虑加法的复杂度为1)。假设O(fib(0)) = O(fib(1)) = 1
,这意味着O(fib(2)) = 3, O(fib((3)) = 5, O(fib(4)) = 9
。正如你所看到的,这个系列增长得比斐波那契数列本身还要快!这意味着复杂性会大大增加。如果你有一个从0到n的for循环的迭代算法,你的复杂度将按照n的顺序缩放,这将好得多。
欲了解更多信息,请查看大O符号。