我被要求确定此代码的大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 )。任何人都可以解释为什么,或如何确定这个?我们是否添加了最内层循环的次数,或者我们是否将每个循环的复杂性相乘?
答案 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 更紧密。 )在问题陈述中给出,所以要么
请记住,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)