[英]How do I determine the Big O of a recursive algorithm that includes an optimization?
我知道斐波那契算法的常规递归函数是 O(2^n),因为它在每次后续调用中调用自己两次,使其成本加倍。 但是,在添加了我所看到的优化(序列解决方案的哈希表)之后,您如何确定它降低了多少复杂性(如果有的话)?
例如:
import java.util.*;
public class Solution {
static Hashtable<Integer, Integer> numbers = new Hashtable<Integer, Integer>();
public static int fibonacci(int n) {
if(n == 0 || n == 1){
return n;
}else if(numbers.containsKey(n)){
return numbers.get(n);
}else {
int result = fibonacci(n-1) + fibonacci(n-2);
numbers.put(n, result);
return result;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
scanner.close();
System.out.println(fibonacci(n));
}
}
你的算法是 O(n)。 你实现的是所谓的记忆。 这真正意味着当将问题分解为两个(或更多)部分重叠的子问题时(例如F(5) = F(4) + F(3)
),两者都需要计算 F(2) 所以它们重叠)当一个值被计算出来时,它会被存储起来,所以下次需要时它已经被计算出来了。
这意味着为了计算F(n)
你将递归计算所有F(i) ,i<n
并且如果一些F(i)
需要不止一次,它将只计算一次并且可以在O(1)
(由于 o 哈希表)。所以总体上将是O(n)
。
这与动态算法版本非常相似(有一点不同,即不是构建解决方案,例如 F(0),F(1),F(2) ...F(n) 而是向后进行跟踪你已经计算(记忆))。 虽然我没有检查你的记忆算法是否有任何错误......只是解释记忆算法的概念和复杂性。
正如评论中指出的,这个函数的复杂度是 Theta(2^n):
Fibonacci(n) {
if (n < 0) return 0;
if (n < 2) return 1;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
我们可以用归纳法证明这一点。 基本情况:对于n = 0
和n = 1
, 0.5 * 2^n <= Fibonacci(n) = 1 <= 2 * 2^n
。 归纳假设:假设这适用于n
直到并包括k
。 归纳步骤:证明0.5 * 2^(k+1) <= Fibonacci(k+1) <= 2 * 2^(k+1)
。 代入,我们得到0.5 * 2^(k+1) = 2*2^(k-1) <= 2*Fibonacci(k-1) <= Fibonacci(k) + Fibonacci(k-1) <= 2*Fibonacci(k) <= 2 * 2^k <= 2 * 2^(k+1)
。 至此,证明完毕。
解决方案的哈希表(有时称为memo ,因此memoization )防止每个k
多次调用Fibonacci(k)
。 由于Fibonacci(n)
仅依赖于Fibonacci(0)
, Fibonacci(1)
, ..., Fibonacci(n-1)
,并且由于哈希表和检查阻止了这些中的任何一个被多次调用,每个都被精确地调用一次,并且由于每个都为任何给定的n
执行恒定量的工作,因此总工作量是O(n)
。 现在很难考虑递归关系(我们有副作用并且需要条件),所以我必须利用这种“技巧”论证。 不幸的是,大多数证明都需要某种“技巧”,归纳法是一个例外。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.