我目前正在了解Big O Notation的运行时间。我尝试计算一些代码的时间复杂度:
int i = 1;
int n = 3; //this variable is unknown
int j;
while (i<=n)
{
for (j = 1; j < i; j++)
printf_s("*");
j *= 2;
i *= 3;
}
我认为该代码的复杂度为О(log n)。但是,即使是正确的,我也无法解释原因。
答案 0 :(得分:2)
内部循环的主体将被执行1、3、9、27,...,3 ^ k次,其中k = ceil(log 3 (n))。
在这里我们可以使用Σ 0 <= i
所以我们可以说内部循环执行的次数不超过2 * 3 ^ k次,其中3 ^ k <3n(在n中呈线性,即O(n)
)。
答案 1 :(得分:2)
时间复杂度是 not O(log n),它是 O(n)。
我们可以按结构化方式进行计算。首先,我们检查内部循环:
for (j = 1; j < i; j++)
printf_s("*");
这里j
从1
迭代到i
。因此,对于给定的i
,这将需要i-1
个步骤。
现在我们可以看一下外部循环,我们可以抽象出内部循环:
while (i<=n)
{
// ... i-1 steps ...
j *= 2;
i *= 3;
}
因此,while
循环的每次迭代,我们都会执行i-1
个步骤。此外,每次迭代i
都会加倍,直到大于n
。因此,我们可以说该算法的步骤数为:
log3 n
---
\ k
/ 3 - 1
---
k=0
我们在这里使用k
作为从0
开始并每次递增的额外变量。因此,它计算了我们执行while
循环主体的次数。它会在3^k > n
时结束,因此我们将迭代log 3 ( n )次,并且每次循环,内部循环将恢复为 3 k -1 步骤。
以上金额等于:
log3 n
---
\ k
-log3 n + / 3
---
k=0
上面是geometric series [wiki],它等于:(1-3 log 3 n )/(1-3)< / em>,或者简化后,它等于(n log 3 3 -1)/ 2 ,因此等于(n -1)/ 2 。
步骤总数因此受以下限制:(n-1)/ 2-log 3 n ,或更简单地表示为 O(n) 。
答案 2 :(得分:0)
首先,您实际上是在计算运行时间,但要计算耗时的操作的数量。在这里,对printf_s
的每次调用都是一个。
有时候,如果您不擅长数学,仍然可以通过实验找到数字。用-O3编译的算法非常快,可以用各种n
进行测试。我用一个简单的增量将printf_s
替换为一个计数器,然后从该函数返回该计数器,并使用unsigned long long
作为类型。有了这些更改,我们得到了
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <inttypes.h>
unsigned long long alg(unsigned long long n) {
unsigned long long rv = 0;
unsigned long long i = 1;
unsigned long long j;
while (i <= n) {
for (j = 1; j < i; j++)
rv += 1;
i *= 3;
}
return rv;
}
int main(void) {
unsigned long long n = 1;
for (n = 1; n <= ULONG_MAX / 10; n *= 10) {
unsigned long long res = alg(n);
printf("%llu %llu %f\n", n, res, res/(double)n);
}
}
该程序在0.01秒内运行,因为GCC非常聪明,可以完全消除内部循环。输出是
1 0 0.000000
10 10 1.000000
100 116 1.160000
1000 1086 1.086000
10000 9832 0.983200
100000 88562 0.885620
1000000 797148 0.797148
10000000 7174438 0.717444
100000000 64570064 0.645701
1000000000 581130714 0.581131
10000000000 5230176580 0.523018
100000000000 141214768216 1.412148
1000000000000 1270932914138 1.270933
10000000000000 11438396227452 1.143840
100000000000000 102945566047294 1.029456
1000000000000000 926510094425888 0.926510
10000000000000000 8338590849833250 0.833859
100000000000000000 75047317648499524 0.750473
1000000000000000000 675425858836496006 0.675426
从中我们可以看到,打印数量与n
的比率并没有真正收敛,但是似乎受两面常数(即O(n))的限制。