找到以下程序的复杂性上限:

时间:2019-06-24 17:10:37

标签: c time-complexity big-o

找到该程序复杂性的上限。

我尝试过。我认为这段代码的时间复杂度为O(n 2 )。

void function(int n) 
{ 
    int count = 0; 
    for (int i=0; i<n; i++) 
        for (int j=i; j< i*i; j++) 
            if (j%i == 0) 
            { 
                for (int k=0; k<j; k++) 
                    printf("*"); 
            } 
} 

但是给出的答案是O(n 5 )。怎么样?

3 个答案:

答案 0 :(得分:3)

编辑: Yikes,花了一些时间回答这个问题之后,发现这是我三年前也回答过的先前问题的重复。糟糕!


此函数运行时的最严格界限是Θ(n 4 。这是推导。

在这里很好地说明了确定代码的big-O的出色通用策略:

  

“如有疑问,请由内而外!”

让我们接受您的代码:

for (int i=0; i<n; i++)
{
    for (int j=i; j< i*i; j++) 
    {
        if (j%i == 0) 
        { 
            for (int k=0; k<j; k++) 
            {
                printf("*"); 
            }
        } 
    }
}

我们分析运行时复杂性的方法是重复执行最内层的循环,并用其完成的工作量代替它。完成后,我们将拥有最终的时间复杂度。

让我们从最里面的循环开始:

 for (int k=0; k<j; k++) 
 {
     printf("*"); 
 }

这里完成的工作量为Θ(j),因为循环迭代的次数与j成正比,并且每次循环迭代的工作量都是恒定的。因此,让我们用更简单的“做Θ(j)来代替”这个循环,为我们提供简化的循环嵌套:

for (int i=0; i<n; i++) 
{
    for (int j=i; j< i*i; j++) 
    {
        if (j%i == 0) 
        { 
            do Θ(j) work 
        } 
    }
}

现在,让我们瞄准现在最里面的循环:

    for (int j=i; j < i*i; j++) 
    {
        if (j%i == 0) 
        { 
            do Θ(j) work 
        } 
    }

此循环很不寻常,因为它的工作量在一次迭代到下一次迭代中会发生很大变化。具体来说:

  • 大多数迭代仅会执行O(1)工作,但是
  • i个迭代中有一个将完成Θ(j)工作。

为分析此循环,我们将把工作分成两个组成部分,并查看每个部分对总数的贡献。

首先,让我们看一下循环的“简单”迭代,该迭代仅执行O(1)。该循环总共进行Θ(i 2 )次迭代(该循环从j = i开始计数,并在j = i 2 和i 2时停止-i =Θ(i 2 )。因此,我们可以在O(i 2 )工作中约束这些“简单”循环迭代的贡献。

现在,“硬”循环迭代又如何呢?这些迭代发生在j = i,j = 2i,j = 3i,j = 4i等时。此外,这些“硬”迭代中的每一个在迭代期间确实与j成正比。这意味着,如果我们将所有这些迭代的工作加在一起,则完成的总工作量为

  

i + 2i + 3i + 4i + ... +(i-1)i。

我们可以简化如下:

  

i + 2i + 3i + 4i + ... +(i-1)i

     

= i(1 + 2 + 3 + ... + i-1)

     

= i·Θ(i 2

     

=Θ(i 3 )。

这利用了1 + 2 + 3 + ... + k = k(k + 1)/ 2 =Θ(k 2 )的事实,这是高斯最著名的和。

所以现在我们看到这里的内循环完成的工作是

  

O(i 2 )用于简单的迭代,并且

     

Θ(i 3 )用于艰难的迭代。

总结起来,我们看到这个内循环完成的总功是Θ(i 3 )。继续执行由内而外的过程,我们可以用“ doΘ(i 3 )work”代替此内循环,以得到以下信息:

for (int i=0; i<n; i++) 
{
    do Θ(i^3) work 
}

从这里,我们看到完成的工作

  

1 3 + 2 3 + 3 3 + ... +(n-1) 3

,该和为Θ(n 4 )。 (具体来说,它是n 2 (n-1) 2 / 4。)

因此,总的来说,该理论预测运行时间应为Θ(n 4 ),这比限制您的O(n 5 )的因数低n上文提到的。理论与实践如何匹配?

我在n的各种值上运行了此代码,并计算了打印星星的次数。这是我得到的值:

n =  500: 7760510375
n = 1000: 124583708250
n = 1500: 631407093625
n = 2000: 1996668166500
n = 2500: 4876304426875
n = 3000: 10113753374750
n = 3500: 18739952510125
n = 4000: 31973339333000
n = 4500: 51219851343375

如果运行时为Θ(n 4 ),则如果将输入大小加倍,则应将输出缩放16倍。如果运行时为Θ(n > 5 ),然后将输入大小加倍应将输出缩放32倍。这是我发现的内容:

Ratio of n = 1000 to n = 500: 16.0535
Ratio of n = 2000 to n = 1000: 16.0267
Ratio of n = 3000 to n = 1500: 16.0178
Ratio of n = 4000 to n = 2000: 16.0133

这强烈表明此函数的运行时确实是Θ(n 4 )而不是Θ(n 5 )。

希望这会有所帮助!

答案 1 :(得分:0)

第一个循环

for (int i=0; i<n; i++)

给出您的第一个O(n)乘数。

接下来,第二个循环

for (int j=i; j< i*i; j++)

因为i在这里“像” n,所以自身乘以O(n ^ 2)复杂度。

第三个循环

for (int k=0; k<j; k++)

与另一个O(n ^ 2)相乘,因为此处j就像n^2

所以您会得到复杂度O(n ^ 5)。

答案 2 :(得分:0)

我同意其他张贴者的观点,除了最里面的循环是O(n ^ 2),因为k的范围是0到j,其本身的范围是n ^ 2。这为我们提供了O(n ^ 5)的理想答案。