计算程序的运行时间(Big O表示法)

时间:2013-12-11 18:29:48

标签: c++ recursion big-o time-complexity

void foo(float[] array, int start, int end){
    if((end-start) <=1) return;

    int x = (end-start) / 5;
    int y = 2*x;
    int z = 4*x;

    foo(array,start,start+y);
    for(index = y; index < z; index++){
        array[index]++;
    }
    foo(array, start+z, end);
}

我得到的是循环初始化执行N + 1次,而内部代码执行N次,这意味着时间复杂度将是O((N + 1)*(N))= O(n ^ 2 )。但是在这种情况下如何计算N?我该如何处理递归调用?如何将此信息转换为Big O表示法?

请不要说这是一个重复的问题。我理解其他例子也有类似的问题,但这个例子对我来说是抽象的,学习如何解决它会在很多其他情况下帮助我。

2 个答案:

答案 0 :(得分:1)

假设foo的输入是:

foo(array, 0, 1000)

因此,首先,x变为200,这是整个数据的五分之一。然后yz成为第二个quintiles。请注意,它们仍然是end - start(1000)的一小部分,而不是常量。

for

for(index = y; index < z; index++)

yzy为400(2 * 1000/5),z为800(4 * 1000/5)。所以for循环的次数是800 - 400 = 400 =(4 - 2)* 1000 / 5.也许那个为你分析这个的人叫它N,但这很荒谬。你在循环中做的是一个简单的增量,它需要恒定的时间(O(1))并且那里没有N

所以让我们自己说出一些东西。我会拨打N以上的1000。基本上,for循环2N / 5次。


递归怎么样?第一次递归在start上递归到start + x(即数据的前2/5),第二次递归在start + z上递归到end(即最后一次递归) 5个数据)。

假设你的函数总计花费f(N)时间来计算它正在做的事情,它可以分解为:

f(N) = f(2N/5) + 2N/5 + f(N/5)

现在您只需要分析这个递归函数并尝试理解订单是什么。


虽然有很多方法可以理解这一点,例如绘制一个显示f扩展方式的树以及使用什么参数,但您也可以尝试找到更简单的函数上限和下限。如果它们是相同的,那么你很幸运:

2N/5 + 2*f(N/5) < f(n) < 2*f(2N/5) + 2N/5

the master theorem,两个限制都属于案例3,两个限制都变为θ(N),因此您的foo函数实际上是θ(N)

另一种看待它的方法如下:

  1. 第一次递归触及数据的前2N / 5
  2. for循环触及第二个2N / 5数据
  3. 第二次递归触及数据的最后N / 5
  4. 如您所见,数组的每个元素只触及一次。因此函数的时间复杂度为θ(N)

答案 1 :(得分:0)

在我们看一下整个函数的实际Big-O之前,让我们快速更正您的代码片段。

// O(4 + N + 2*O(?)) = O(N + O(?))
void foo(float[] array, int start, int end){
    if((end-start) <=1) return; // O(1)

    int x = (end-start) / 5; // O(1)
    int y = 2*x; // O(1)
    int z = 4*x; // O(1)

    foo(array,start,start+y); // O(?)
    for(index = y; index < z; index++){ // O(N)
        array[index]++; // O(1)
    }
    foo(array, start+z, end); // O(?)
}

现在,在基本情况(end - start <= 1)中,foo立即返回,使其成为O(1)。在第二个到基本的情况下,这意味着此函数是O(N)(只是for循环)。在第三种到基本情况中,这意味着该函数是O(N + N + N) = O(3N) = O(N)。它以这种方式向上发展。

重要的是要记住,我们忽略了一些(可能很大的)常数因子,但整个算法仍然属于O(N)的范畴。

不可否认,这种方法缺乏严谨性,但只需最少的工作就可以得到很好的估计。