我很难确定简单递归方法的大O.如何计算这些方法的大O?
案例1)找到方法f的大O:
int f(int x){
if(x<1) return 1;
return f(x-1)+g(x);
}
int g(int x){
if(x<2) return 1;
return f(x-1)+g(x/2);
}
案例2)
int test(int n){
if(x<=2) return 1;
return test(n-2) * test(n-2);
}
案例3)
int T(int n){
if(n<=1) return 1;
return T(n/2)+T(n/2);
}
答案 0 :(得分:2)
案例1
将基本案例放在一边(g(1) = g(0) = 1
等),您可以用g
重写f
:
f(n) = f(n-1) + g(n) <=> g(n) = f(n)-f(n-1)
我们知道g
定义为:
g(n) = f(n-1) + g(n/2)
如果我们用上面重写的表单替换g(n/2)
,我们会得到:
g(n) = f(n-1) + f(n/2) + f(n/2-1)
这意味着我们可以在不f
的任何引用的情况下重写g
,将g(n)
的原始定义中的f
替换为上述公式:
f(n) = f(n-1) + f(n-1) + f(n/2) + f(n/2-1)
要仔细检查这是否等效,可以运行此程序,该程序接受整数n
作为第一个参数,并打印原始f(n)
的结果,后跟重写的{{ {1}}(在代码中称为f(n)
):
f2
一些例子:
#include <stdio.h>
int g(int x);
int f(int x) {
if (x < 1)
return 1;
return f(x-1)+g(x);
}
int g(int x) {
if (x < 2)
return 1;
return f(x-1)+g(x/2);
}
int f2(int x) {
if (x < 1)
return 1;
return f2(x-1)+f2(x-1)+f2(x/2)-f2(x/2-1);
}
int main(int argc, char *argv[]) {
int n;
sscanf(argv[1], "%d", &n);
printf("%d\n", f(n));
printf("%d\n", f2(n));
return 0;
}
现在,如果你想象递归树,每个节点分支成4个子树($ ./a.out 10
1952
1952
$ ./a.out 11
3932
3932
$ ./a.out 12
7923
7923
$ ./a.out 13
15905
15905
$ ./a.out 14
31928
31928
$ ./a.out 15
63974
63974
,f(n-1)
,f(n-1)
和f(n/2)
各一个子树。 。每个子树的大小不相同,例如,如果我们下降到子树并且总是跟随最右边的2个分支中的任何一个,我们有一个深度为f(n/2-1)
的二叉树。但是还有其他分支(如果我们始终遵循log(N)
路径),其深度为f(n-1)
,并且分支为n
两次。因此,我们可以说它绝对是指数级的。
获得确切的数字有点困难,但明显的上限是n-1
- 虽然这忽略了一些分支仅O(4^N)
深的事实,所以实际上它&# 39;比log(N)
好一点。
案例2
再次考虑递归树。在每个点,我们分支两次(O(4^N)
和test(n-2)
)。因为我们在每次调用时将test(n-2)
减少2,所以树将n
深,所以我们需要O(n/2)
时间来遍历树 - 再次,指数增长。不是特别有趣。
(旁注:如果你在这里使用memoization,这将是线性的!)。
案例3
与案例2类似的逻辑,但这次树的深度为O(2^(n/2))
(因为这需要将log(N)
除以2的次数才能达到基本情况),所以我们得到N
。所以它是线性的。