该算法为O(n 2 ),但运行时间不到一秒。为什么这么快?
public class ScalabilityTest {
public static void main(String[] args) {
long oldTime = System.currentTimeMillis();
double[] array = new double[5000000];
for ( int i = 0; i < array.length; i++ ) {
for ( int j = 0; j < i; j++ ) {
double x = array[j] + array[i];
}
}
System.out.println( (System.currentTimeMillis()-oldTime) / 1000 );
}
}
编辑:
我将代码修改为以下内容,现在运行速度非常慢。
public class ScalabilityTest {
public static void main(String[] args) {
long oldTime = System.currentTimeMillis();
double[] array = new double[100000];
int p = 2;
int m = 2;
for ( int i = 0; i < array.length; i++ ) {
p += p * 12348;
for ( int j = 0; j < i; j++ ) {
double x = array[j] + array[i];
m += m * 12381923;
}
}
System.out.println( (System.currentTimeMillis()-oldTime) / 1000 );
System.out.println( p + ", " + m );
}
}
答案 0 :(得分:11)
我认为这里的问题是一切都在逐步优化。这是我的理由:
x
的值 - 您的数组全为0,因此x
将始终取值0. x
未使用,可以进行优化。你没有那么多代码,这就是它可能运行得如此之快的原因!
作为参考,我在自己的机器上尝试了这个并且通过不断地将它乘以10倍来改变阵列大小并且看到绝对没有性能变化 - 它总是完成并且输出需要0秒。这与优化假设一致,该假设表明运行时应为O(1)。
编辑:您编辑的代码进一步支持我的想法,因为现在循环体有副作用,因此无法优化。具体来说,由于m
和p
在循环内部更新,编译器无法轻易地完全优化循环,因此您将开始看到O(n 2 )性能。尝试改变阵列的大小,看看会发生什么。
希望这有帮助!
答案 1 :(得分:6)
算法的顺序并不能告诉你它的运行速度。它会告诉你当n的大小发生变化时它的速度如何变化。
存在O(n)= n ^ 2意味着如果您尝试使用10,000,000个元素,则需要(大约)当前所需时间的4倍。
答案 2 :(得分:0)
可能是因为双循环实际上没有做任何事情,因此JIT开始并且优化了一些东西。
请注意,这对于Java和JVM的每个版本和实现来说都太具体了 - 在我的版本上它现在运行了大约一分钟。
您可以添加
System.out.println(x)
如果你想看到O(N ^ 2)行为,那么在for循环之后和之外只是为了停止优化。
答案 3 :(得分:0)
我认为这是无关紧要的.O(N ^ 2)更快但参考了什么。 你只能说一些东西很快,只有当你把它与某些东西进行比较时。不是吗?
Morevers,时间也取决于输入大小。尝试大输入,你可以看到O(N)和O(N ^ 2)之间的差异。
一些实用参考。 SPOJ是一个编程竞赛网站,使用两个处理器根据输入文件检查代码。
(英特尔奔腾III 733 MHz) - 由自2004年初开始为SPOJ提供服务的老式固态奔腾III机器组成。由于这些机器速度慢,因此SPOJ问题制定者创建的评委可以更轻松测试解决方案中使用的算法的复杂性,而无需创建大量数据集(您可以轻松地分辨O(n)和O(nlogn)之间的区别)。
(英特尔奔腾G860 3GHz) - 这个新集群由现代和快速的英特尔奔腾G860 CPU组成。在此您的提交将比前一个提交快30到50倍。所以你可以期待如果你在家里测试你的解决方案,那么它将在SPOJ上有类似的执行时间。在此群集上提交的内存限制为1536 MB。
更多: 通常在Intel Pentium G3860 3GHZ上在1秒内循环达10 ^ 7次运行。所以10 ^ 7的O(N)算法只需1秒。 现在取N = 10 ^ 7并做O(N ^ 2)并尝试..你可以自己体验时差
编译器优化也在这方面发挥了重要作用! 你的代码太简单了!你的数组是空的!