views:

212

answers:

6

I have the following code snippet:

sum = 0;
for (i = 0; i < n; i++)
    for (j = 0; j < i; j++)
        sum++;

The complexity would be O(n^2), but if I want to dig a little more for the internal loop complexity then would it be (n (n-1))/2 or (n-1)!?

A: 

Constants are irrelevant as far as big-O notation is concerned.

Anon.
+6  A: 

Yes, O(n^2), but actually 0+1+...+n-1=n(n-1)/2 = O(n^2), definitely not (n-1)!

Ramashalanka
A: 

What you have calculated (n(n-1)/2) is the exact number of iterations for the code. When asked for time complexity in terms if big O, you give an estimate which is just big enough to express the time taken.

In other words, you need to find THE SMALLEST power of n such that for some k (k>0), k * n^power will be greater than the exact number of iterations. In your case, k happens to be 1 and power happens to be 2. Then O(n^power) is your time complexity.

Ashwin
There's no need for it to be a power of n. Any function will do, such as log n, 2^n, n!.
Juan
@Juan: Not only is there no need for it to be a power of `n`, but in some cases it is impossible for it to be. For example, `n!` grows faster than any polynomial.
Jason
This is actually incorrect. A function that runs in n^2 time is in O(n^3). (I say is in O(n^3) because O(anything non-zero) is a simply a set with an infinite number of functions as members.). All Big-O requires is that it is an upper bound (by that I mean more formally that some kn^3 + c is an upper bound for some constant k and c). Big theta requires it to be both an upper and lower bound.
DivineWolfwood
Agreed. I replied the first thing that came to my mind. Time complexity can be any other expression. I was explaining from the point of view of the example at hand - no intentions of confusing the questioner. I just wanted to emphasize on 'smallest power' greater than the exact number of iterations. Also, what I've mentioned is for Big-O only. I will edit the post.
Ashwin
+3  A: 
time = n*(n-1)/2
     = (n*n - n)/2

Since big-O notation is an upper bound, the lesser order term (-n) and the constant factor (1/2) are both removed (because they aren't significant for representing the upper bound on time) to yield the big-O notation, O(n*n) better known as O(n^2).

SoapBox
A: 

First of all, (n-1)! means (n-1)(n-2)...(2)(1). This is clearly not what you want here.

If you count the actual number of iterations it's 0 + 1 + 2 + ... + (n-2) + (n-1). Note that there are n terms in the sum, and that we can pair them off in a way such that the average value of each pair is (n-1)/2. (Pair the highest and lowest, the second highest and second lowest, etc.) If n is odd, you'll have one left over that can't be paired, but conveniently its value is also (n-1)/2. Thus you have n terms and the average of all terms is (n-1)/2, so the total sum is n(n-1)/2.

Now, for big O notation it doesn't matter exactly how many iterations we have -- we just want to know the limit when n is very large. Note that our number of iterations can be written as (1/2)n^2 - (1/2)n. For very large n, the n^2 term is way, way bigger than the n term, so we drop the n term. That just leaves us with (1/2)n^2, but another rule of big O notation is we don't care about the constant factor, so we just write that it's O(n^2).

Tim Goodman
+1  A: 

You could have an algorithm that runs in time

22222222222222 + 4444444444444*n + 9999999999999999999999999*n^2 steps

And it would still be O(n^2).

It's an open problem to find a better description for algorithm running time than O.

bodacydo
What would qualify a description of running time as "better"?Clearly we could be more specific, like this algorithm takes place in `f(n)` steps, or this algorithm runs in an average time of 'X' milliseconds per 'n' on such-and-such hardware, but that doesn't necessarily make that description "better".
Tim Goodman
@Tim: well... Having used a correctly 16-threaded sort running on a 16-cores Mac that anyone can buy today and seeing how it outperforms the default Java mono-threaded sort, I'd sure love something like a "O(n log n) / nb cpus" notation or something. Doesn't change anything for the "upper bound" when n approaches infinity, but for most practical purposes we may need something 'better' for we're entering in a more and more parallelized world and big-O simply doesn't cut it to differentiate between a mono-threaded algo and one that scales well to 16 cores and up.
Webinator