我正在观看有关Big O和算法复杂性的在线课程,并对一个例子表示怀疑:
private static int func(long n) {
int result = 0;
for(int i = 0; i <= n; i++) {
result = i*i;
}
return result;
}
讲师得出它的复杂性是O(n)。我想,因为循环是O(n)但是乘法是O(n ^ 2),而 i 依赖于 n ,它应该是O(n ^ 2)
然后我写了一个示例Java应用程序来检查实际执行时间:
private static long N1 = 10;
private static long N2 = 100000;
public static void main(String[] args) {
long startTime1 = System.nanoTime();
System.out.println(func(N1));
long stopTime1 = System.nanoTime();
long difference1 = stopTime1 - startTime1;
long startTime2 = System.nanoTime();
System.out.println(func(N2));
long stopTime2 = System.nanoTime();
long difference2 = stopTime2 - startTime2;
System.out.println("N2 / N1 = " + N2 / N1);
System.out.println("difference2 / difference1 = " + difference2 / difference1);
}
结果是:
100
1410065408
N2 / N1 = 10000
difference2 / difference1 = 5
因此,如果N2大10 ^ 4倍,执行时间只会大5倍,即它是对数复杂度。这是为什么?是否有可能在没有实际测试的情况下先验地推导出这个?
更新感谢大家,我看到我错过或误解了一些事情。如果可以,我会接受每个答案。谢谢你们!
答案 0 :(得分:4)
您可以通过查找最常用的指令(主导指令)或其中一个来计算时间复杂度。
在您的情况下,这是:i <= n
或仅执行一次的result = i*i;
或i++
。虽然评估复杂性-1
或+1
并不重要,但请选择您想要的。
然后,您尝试通过n
变量制定主导指令的执行次数。在您的示例中,i<=n
的执行时间恰好n+2
次,因此复杂性为O(n+2) = O(n)
,因为在计算复杂性时您不应该关心常量。
最后说明:
乘以不是O(n^2)
,您可能会将其与乘以BigIntegers
而不是普通ints
相混淆。
答案 1 :(得分:3)
首先尝试重新运行测量。不要计算打印价值所需的时间,以消除一些不准确之处。
long startTime1 = System.nanoTime();
int res = func(N1);
res = func(N1);
...
res = func(N1);
long stopTime1 = System.nanoTime();
System.out.println(res);
其次,您应该在测量时间的同时运行8次计算,因此运行时间更长,甚至可以提高精度。我喜欢运行30秒或更长时间的计算。并确保输入n足够大。即使这样,至少进行三次整体测试并对结果取平均值。
编辑:RealSkeptic对热点热身是正确的,他给了nice link一个解释
下一个乘法可以在现代CPU上的几个时钟周期内运行。从这个角度来看,乘法只是一个操作。大O符号可以让您专注于算法的一般速度感,因此您可以首先选择复杂度较低的算法。之后,您可以执行更多代码,也可以进行硬件特定的优化。
答案 2 :(得分:3)
复杂性是O(n)因为你的函数执行n操作,所以你做什么样的操作并不重要。
在这种情况下,当您将较大的n传递给func时,操作数将线性增长,这就是O(n)复杂度。如果嵌套循环从1到n,则为O(n ^ 2)。
基准并不重要,因为它们是特定于实施的,并且取决于其他因素。为了获得更准确的结果,将func(N1)和func(N2)的值保存到变量中,并在最后打印出这些变量。
答案 3 :(得分:1)
大O是asymptotic时间,而不是实际时间。我们不计算运营,我们正在显示输入数量与总运行时间之间的关系。
虽然测量是正确的,但您还没有做出足够的测量来得出有用的结论。尝试数以千计的 n 的不同值,而不只是两个,并绘制您获得的运行时间。在图中,您将找到异常值,但最终您会看到 n 与执行时间之间的直线(线性)关系,因此为O(n)。如果图形向上弯曲,则可以是O(log n),O(n log n),O(n ^ 2),O(n!)等。
答案 4 :(得分:0)
不,因为,
result = i*i;
即使可能需要很长时间,因为我可以为长数字进行非常大的位操作。这被排除在外,因为这被认为是一台机器指令,并且它被认为需要花费一定的时间。请记住,时间复杂度并不能准确地告诉您代码执行的时间。我们需要看的只是用于告诉该指令执行多少次并将其映射到图形上的函数。它也不关心您的图表 看起来像是来自原点或y = 2的线性图。它关心的是它是线性的。 这就是时间复杂度仍为O(n)的原因。
答案 5 :(得分:0)
只是旁注:
即使如果乘法在这里相关,重要的一点是如何执行乘法。对于原始类型,它在硬件中实现,因此我们可以将其视为常量O(1)
。但即使忽略这一事实,CPU也不会使用你在学校学到的纸笔法实现乘法,而是russian-peasant algorithm(又名古埃及乘法等),它会在O(log max(n , m))
的{{1}}。