找到该程序复杂性的上限。
我尝试过。我认为这段代码的时间复杂度为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 )。怎么样?
答案 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
}
}
此循环很不寻常,因为它的工作量在一次迭代到下一次迭代中会发生很大变化。具体来说:
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)的理想答案。