Why does this O(N^2) algorithm run so quickly?

2019-08-28 02:49发布

问题:

This algorithm is O(n2), however it runs in less than a second. Why is it so quick?

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 );
   }
}

EDIT:

I modified the code to the following and now it runs very slowly.

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 );
    }

}

回答1:

I think the issue here is that everything is getting optimized away. Here's my reasoning:

  1. The value of x can be known without having to do any computation - your array is all 0s, so x will always take the value 0.
  2. The local variable x is unused and can be optimized away.
  3. The inner loop does nothing, so can be optimized away.
  4. The outer loop does nothing, so can be optimized away.

You're left with not all that much code, which is why it probably runs so quickly!

For reference, I tried this on my own machine and varied the array size by constantly multiplying it by a factor of 10 and saw absolutely no change in performance - it always finished and outputted that 0 seconds were required. This is consistent with the optimization hypothesis, which states the runtime should be O(1).

EDIT: Your edited code further supports my idea since now the body of the loop has side-effects and therefore cannot be optimized away. Specifically, since m and p are updated inside the loop, the compiler cannot easily optimize the loop away in its entirety, so you would begin to see O(n2) performance. Try varying the size of the array and watch what happens.

Hope this helps!



回答2:

The order of an algorithm does not tell you how quickly it runs. It tells you how its speed evolves when the size of n changes.

Being O(n) = n^2 means that if you try this with 10,000,000 elements, you will need (approx) 4 times the current time needed.



回答3:

Could be that JIT kicks in and optimized things away, since the double loop does not actually do anything.

Note this is too specific for each version and implementation of Java and JVM - on my version it's running for about a minute now.

You might add a

System.out.println(x)

after and outside the for loops just to stop the optimization, if you want to see O(N^2) behavior.



回答4:

I think this is irrelevant.O(N^2) is faster but in reference to what. You can only say something is fast, Only when you compare it with something. doesn't it ?

Morevers, the timing depends on Input size too. try a large input and you can see the differnce between O(N) and O(N^2).

For Some Practical Reference. SPOJ is a Programming contest site that uses two processor to check you code against input file.

  1. (Intel Pentium III 733 MHz) - consists of old solid Pentium III machines that serve SPOJ since its very beginning in 2004. Thanks to the fact that these machines are slow, judges created by SPOJ problem setters can more easily test the complexity of the algorithms used in your solution without creating enormous datasets (you can easily tell the difference between O(n) and O(nlogn) on them).

  2. (Intel Pentium G860 3GHz) - this new cluster consists of modern and fast Intel Pentium G860 CPUs. On This your submissions will run from 30 to 50 times faster than on previous one. so you can expect that if you test your solution at home then it will have similar execution time on SPOJ. On this cluster memory limit for submissions is 1536 MB.

More : generally loop upto 10^7 runs in 1 sec on Intel Pentium G3860 3GHZ. so your O(N) algorithm for 10^7 takes just 1sec. now take N=10^7 and do O(N^2) and try it.. you can experience the time difference on your own

Compiler Optimization also plays an important role in this too !! Your code is too simple too ! your array is empty !