C 确定特定函数的时间复杂度

C 确定特定函数的时间复杂度,c,time-complexity,C,Time Complexity,这是我最近遇到的一个关于算法和数据结构的旧考试中的一个问题。我很难理解这个解决方案。 我需要找到函数的big-O、big-ϴ和big-Ω边界: void recursion(int n) { int i; if (n == 0) { return; } for (i = 0; i < n; i++) { recursion(i); } } void递归(int n){ int i; 如果(n==0){ 返回; } 对于(i=0;i0,给定递归的迭代实现,因此可以归纳地证明T

这是我最近遇到的一个关于算法和数据结构的旧考试中的一个问题。我很难理解这个解决方案。 我需要找到函数的
big-O
big-ϴ
big-Ω
边界:

void recursion(int n) {
 int i;
 if (n == 0) {
 return;
 }
 for (i = 0; i < n; i++) {
 recursion(i);
 }
}
void递归(int n){
int i;
如果(n==0){
返回;
}
对于(i=0;i

这三个问题的解决方案都是
2^n
,我不明白为什么。我试着把事情写下来,但我甚至无法接近解决方案。如果有人能解释一下
2^n
从何而来,我将不胜感激。

因为这闻起来像是一个家庭作业问题,这个答案是不完整的

这类问题背后的惯用伎俩是建立一个递推方程。也就是说,
递归(k+1)
的时间复杂度在某种程度上与
递归(k)
的复杂度有关。仅仅写下重复性本身不足以证明复杂性,你必须证明为什么重复性是真实的。但是,对于2n,这表明
递归(k+1)
所花费的时间是
递归(k)
的两倍

设T(k)表示
递归(k)
的时间复杂度。因为递归(0)
立即返回,所以让T(0)=1。对于k>0,给定递归的迭代实现,因此可以归纳地证明T(k)=2k


让我们将总运行时间表示为
f(n)
。由于函数中的循环,
f(n)
实际上是
i
f(i)
之和,介于0和n-1之间。这是
n
项的总和。让我们试着简化这个表达式。在这种情况下,一个标准的技巧是找到一个互补的等式。让我们看看
f(n-1)
的值是多少。与前一种情况类似,对于
i
,它是介于0和n-2之间的
f(i)
之和。现在我们有两个方程:

f(n)=f(1)+...+f(n-1)
f(n-1)=f(1)+...+f(n-2)
让我们从第一个减去第二个:

f(n)-f(n-1)=f(n-1)
--> f(n)=2f(n-1)
现在这是一个很好的例子。 解决方案是即时的(有关更多详细信息,请参阅链接):


f(n)=f(1)*2n=2n

让我们看看一个更简单的递归,它被称为O(2^n)

有8(2^3)个递归调用,因为每个n>2的调用会产生两个以上的递归调用,所以fib(n+1)的递归调用数是fib(n)的两倍

以你为例:

n = 3
    n = 2
        n = 1
            n = 0
        n = 0
    n = 1
        n = 0
    n = 0
当n=3时,我们得到7个递归调用

对于n=4

n = 4
    n = 3
        n = 2
            n = 1
                n = 0
            n = 0
        n = 1
            n = 0
        n = 0
    n = 2
        n = 1
            n = 0
        n = 0
    n = 1
        n = 0
    n = 0
这里,我们有15个电话。查看上面的执行树,您可以看到递归(4)基本上是递归(3)+递归(3)+1

所以一般来说,递归(n+1)比2*递归(n)多一个递归调用,2*递归(n)基本上是每+1到n增加一倍,这是O(2^n)

所以

因此=>

2^(n-1) * (n - (n-1))
那就是

2^n calls...

也许观察调用
递归(2)
递归(3)
之间的差异会对你有所帮助。递归在这里不是无界的吗?@information\u interchange:No。调用
递归(n)
中的每个递归调用都传递了一个小于
n
的参数。啊,是的,我现在明白了;Fibonacci的朴素递归计算不是Φ(2^n)。是Φ(ψ^n),其中ψ是(sqrt(5)+1)/2,约为1.618。换句话说,它是Φ(fib(n)),这在递归本身中是清楚的。OP的递归实际上是Φ(2^n),但您的答案忽略了这一事实的简单证明。
n = 4
    n = 3          // + 1
        n = 2                //
            n = 1            //
                n = 0        // recursion(3)
            n = 0            //
        n = 1                //
            n = 0            //
        n = 0                //
    n = 2           //
        n = 1       //
            n = 0   // recursion(3)
        n = 0       //
    n = 1           //
        n = 0       //
    n = 0           //
r(n)   = r(n-1)+r(n-2)+...+r(0) // n calls.
r(n-1) = r(n-2)+r(n-3)+...+r(0) // n-1 calls.
r(n-2) = r(n-3)+r(n-4)+...+r(0) // n-2 calls.
.
.
.
r(1)   = r(0)                   // 1 call.
r(0)   = return;                // 0 call.
r(n)   = r(n-1)+r(n-2)+...+r(0)         // n calls.
       = 2 * (r(n-2)+...+r(0))          // 2 * (n - 1) calls.
       = 2 * ( 2 * (r(n-3)+...+r(0)) )  // 2 * 2 * (n - 2) calls.
.
.
.
2^(n-1) * (n - (n-1))
2^n calls...