为什么这段代码的运行时间为O(n ^ 5)?

时间:2016-02-18 02:38:53

标签: big-o time-complexity

我被要求确定此代码的大O时间复杂度:

function(int n) {
    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 :(得分:6)

分析这样的代码的一种方法是从最里面的循环开始并向外工作。那个循环是

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

这具有时间复杂度Θ(j)。现在让我们看看下一个循环:

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

这个很有意思。该循环将运行Θ(i 2 )次。大多数迭代将执行O(1)工作,但每次迭代都将执行Θ(j)工作。因此,我们可以将这里完成的工作分成两部分:基线Θ(i 2 )工作仅仅是通过循环完成,加上间歇性地进行Θ(j)工作所做的额外工作。

我们做Θ(j)工作的那部分每次迭代都会发生。这意味着完成的工作将大致

  

i + 2i + 3i + 4i + ... + i 2

     

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

     

=iΘ(i 2

     

=Θ(i 3

总的来说,这个循环做Θ(i 3 )工作。这支配了来自外环的Θ(i 2 )工作,因此这里完成的总工作是Θ(i 3 )。

最后,我们可以通过最外层循环,如下所示:

for (int i=0; i<n; i++)
{
    // Do Theta(i^3) work
}

这里完成的工作大致是

  

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

     

=Θ(n 4

总的来说,这里完成的总工作是Θ(n 4 )。这比O(n 5 更紧密。 )在问题陈述中给出,所以要么

  1. 我在这里的某处有数学错误,或
  2. 你的束缚不紧张。
  3. 请记住,big-O表示法用于给出一段代码的运行时的上限,因此如果运行时实际上是Θ,则表示运行时为O(n 5 ) n 4 )没错;它只是不紧张。说它是O(n 100 )也没有错,尽管这不是一个非常有用的界限。

    我们可以检查这个问题的一种方法是编写一个程序,该程序只计算最内层循环运行的次数,并将其与n 4 进行比较,得出各种n值。我写了一个程序就是这么做的。如下所示:

    #include <iostream>
     #include <cstdint>
     #include <cmath>
     using namespace std;
    
     uint64_t countWork(int n) {
       uint64_t result = 0;
    
       for (int i = 0; i < n; i++) {
         for (int j = 1; j < i * i; j++) {
           if (j % i == 0) {
            for (int k = 0; k < j; k++) {
              result++;
            }
          }
        }
       }
    
       return result;
     }
    
     int main() {
       for (int n = 100; n <= 1000; n += 100) {
         cout << "Ratio of work to n^4 when n = "
              << n << " is " << countWork(n) / pow(double(n), 4.0) 
              << endl;
       }
     }
    

    这是输出:

    Ratio of work to n^4 when n = 100 is 0.120871
    Ratio of work to n^4 when n = 200 is 0.122926
    Ratio of work to n^4 when n = 300 is 0.123615
    Ratio of work to n^4 when n = 400 is 0.123961
    Ratio of work to n^4 when n = 500 is 0.124168
    Ratio of work to n^4 when n = 600 is 0.124307
    Ratio of work to n^4 when n = 700 is 0.124406
    Ratio of work to n^4 when n = 800 is 0.12448
    Ratio of work to n^4 when n = 900 is 0.124537
    Ratio of work to n^4 when n = 1000 is 0.124584
    

    由此看起来运行时大致接近0.125n 4 ,大致为n 4 / 8.这实际上是有意义的 - 隐藏的常数因子来自最内圈是1/2(因为1 + 2 + 3 + ... + i = i(i + 1)/ 2),最外圈的隐藏常数因子是1/4(因为1 3 < / sup> + 2 3 + ... + n 3 = n 2 (n + 1) 2 / 4)。换句话说,这个理论与实践非常接近!

答案 1 :(得分:2)

因此,O时间复杂度是每个循环的最大单独迭代计数的乘积,这些循环嵌套在任意感兴趣的变量n中。

你有

function (int n) // as in O(n)
{
    for(int i=0;i<n;i++) //  n
    {
    for(int j=i;j<i*i;j++) // n ^ 2
       {

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

     }
}

n * n ^ 2 * n ^ 2 = n ^ 5

有趣的是,你会发现&#34; *&#34;打印不是n ^ 5,这是一个结果,如果if条件,你将找到给定的正整数n的计数将是&lt; n ^ 5并且可能&gt; n ^ 4。

通过使用每个循环的最大迭代次数的乘积,我们可以快速得到函数结束行为的上限,因为n随意增加。

答案 2 :(得分:0)

在最坏的情况下,这个问题的总时间复杂度的数学形式如下:

enter image description here

  

所以复杂性是O(n^5)

修改:在上面的回答中,我并不关注if表现。

我们可以按照以下方式更改您的代码:

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

因此,正如templatetypedef所提到的,很明显总运行时为O(n^4)