我知道斐波纳契算法的常规递归函数是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));
}
}
答案 0 :(得分:1)
您的算法是O(n)。你实现的是所谓的memoization。这真正意味着当在两个(或一般更多)部分重叠的子问题(例如F(5) = F(4) + F(3)
)中打破问题时,两者都需要计算F(2)以便它们重叠)当计算一个值时存储,以便下次需要它将被计算。
这意味着,为了计算F(n)
,您将递归计算所有F(i) ,i<n
,如果某些F(i)
不止一次需要,它将只计算一次,并且可用于O(1)
(由于哈希表)。总体而言是O(n)
。
这与动态算法版本非常相似(差别很小,而不是建立解决方案,例如F(0),F(1),F(2)... F(n)你做后退保持跟踪你的计算(memoization))。虽然我没有检查你的memoization算法是否有任何bug ...只是解释了memoization算法的概念和复杂性。
答案 1 :(得分:0)
正如评论中所指出的,这个函数的复杂性是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)
。这样就完成了证明。
解决方案的散列表(有时称为备忘录,其中 memoization )可防止Fibonacci(k)
每k
次调用一次。由于Fibonacci(n)
仅取决于Fibonacci(0)
,Fibonacci(1)
,...,Fibonacci(n-1)
,并且由于哈希表和检查会阻止任何这些被多次调用,因此每个都是只调用一次,并且由于每个都为任何给定的n
执行一定量的工作,因此工作总量为O(n)
。复发关系现在更难以思考(我们有副作用并且需要条件)所以我必须利用这种“技巧”论证。不幸的是,大多数证据需要某种“技巧”,归纳是一种例外。