2

So many loops, I stuck at counting how many times the last loop runs. I also don't know how to simplify summations to get big Theta. Please somebody help me out!

    int fun(int n) {
        int sum = 0
        for (int i = n; i > 0; i--) {
                for (int j = i; j < n; j *= 2) {
                        for (int k = 0; k < j; k++) {
                                sum += 1
                        }
                }
        }
        return sum
    }
Liafonx
  • 31
  • 4
  • As a rough estimate, you could run this for many values of `n` and plot `sum` as a function of `n`. The shape of the graph may tell you if it's linear, quadratic or in-between. – anatolyg Oct 07 '21 at 15:45
  • I see potential for optimization. :) – Neil Oct 07 '21 at 16:00

1 Answers1

2

Any problem has 2 stages:

  1. You guess the answer
  2. You prove it

In easy problems, step 1 is easy and then you skip step 2 or explain it away as "obvious". This problem is a bit more tricky, so both steps require some more formal thinking. If you guess incorrectly, you will get stuck at your proof.


The outer loop goes from n to 0, so the number of iterations is O(n). The middle loop is uncomfortable to analyze because its bounds depend on current value of i. Like we usually do in guessing O-rates, let's just replace its bounds to be from 1 to n.

    for (int i = n; i > 0; i--) {
            for (int j = 1; j < n; j *= 2) {
                perform j steps
            }
    }

The run-time of this new middle loop, including the inner loop, is 1+2+4+...+n, or approximately 2*n, which is O(n). Together with outer loop, you get O(n²). This is my guess.

I edited the code, so I may have changed the O-rate when I did. So I must now prove that my guess is right.


To prove this, use the "sandwich" technique - edit the program in 2 different ways, one which makes its run-time smaller and one which makes its run-time greater. If you manage to make both new programs have the same O-rate, you will prove that the original code has the same O-rate.

Here is a "smaller" or "faster" code:

    do n/2 iterations; set i=n/2 for each of them {
            do just one iteration, where you set j = i {
                perform j steps
            }
    }

This code is faster because each loop does less work. It does something like n²/4 iterations.

Here is a "greater" or "slower" code:

    do n iterations; set i=n for each of them {
            for (int j = 1; j <= 2 * n; j *= 2) {
                perform j steps
            }
    }

I made the upper bound for the middle loop 2n to make sure its last iteration is for j=n or greater.

This code is slower because each loop does more work. The number of iterations of the middle loop (and everything under it) is 1+2+4+...+n+2n, which is something like 4n. So the number of iterations for the whole program is something like 4n².

We got, in a somewhat formal manner:

n²/4 ≤ runtime ≤ 4n²

So runtime = O(n²).


Here I use O where it should be Θ. O is usually defined as "upper bound", while sometimes it means "upper or lower bound, depending on context". In my answer O means "both upper and lower bound".

anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • 1
    For the part that gets the running time of the new middle loop is 2*n, you may not consider that j must be less than n. This means j only changes like 1, 2, 4, 8,... n, it can hardly run 2*n times, even less than n times. – Liafonx Oct 08 '21 at 11:30
  • Middle loop is certainly log(n). Also, in your explanation `do n iteration: do 2n iterations: perform j steps` amounts to O(n³), not O(n²). – Aivean Oct 08 '21 at 19:48
  • 1
    I distinguish between number of iterations and running time. Number of iterations is O(log(n)), but it's hardly a useful metric, because each iteration takes different time. So I look at running time instead. Running time of middle loop includes the inner loop. I omitted some calculations; maybe it was wrong to do so. – anatolyg Oct 11 '21 at 07:43
  • I've checked again your updated answer and I believe you're right. @Liafonx, can you please mark this answer as accepted, instead of mine? – Aivean Oct 15 '21 at 21:34