3

Can anyone explain what the time complexity is for this algorithm?

for (i = 1; i <= n; i++){
    for(j = 1; j <= n; j += i) {   // note: not j++
        printf("Iteration %d : %d\n", i, j);   
    }
}
17
  • This looks exactly like O(n^2) to me Commented Jan 9, 2020 at 15:42
  • Is it (n)*(n+1)? So it's time complexity is O(n^2)? Commented Jan 9, 2020 at 15:46
  • Wait: I didn't see the j += i in the inner loop; that changes it. Now I don't know :-) Commented Jan 9, 2020 at 15:48
  • @AnttiHaapala The inner loop runs n times, then n/2, then n/3, etc. Is that still n^2? Commented Jan 9, 2020 at 15:54
  • No, it is about O(n^1.06). Commented Jan 9, 2020 at 15:57

2 Answers 2

5

The printf in the inner loop is called exactly ceil(n) + ceil(n/2) + ceil(n/3) + ... ceil(n/n) times. To get rid of ceil, we know that ceil(y/n) is bounded above by y/n + 1, so we know that the number of executions is >= n + n/2 + n/3 ... n/n but is < n + 1 + n/2 + 1 + n/3 + 1 + n/4 + 1... + n/n + 1. The former can be factored to n(1 + 1/2 + 1/3 + 1/4 ... 1/n) and the latter can be refactored into to n(1 + 1/2 + 1/3 + 1/4 ... 1/n) + n.

The latter factor is of the first addend to infinity is the the harmonic series, which diverges. The sum of the first k terms from the Wikipedia page is known to be bounded:

https://wikimedia.org/api/rest_v1/media/math/render/svg/c92abcc9592300b3eca1aac9748346649ba86af9

which means that 1 + 1/2 + 1/3 + 1/4 ... 1/n is Ө(ln n) = Ө(log n); we can give strict bounds for the number of times that printf is called (c(n): n log n <= c(n) < n log n + 2n. Since n log n grows faster than 2n, we can keep only the former and notice that both bounds belong to Ө(n log n) and hence c(n) belongs to Ө(n log n) as well (Ө(F) means that the function is both Ω(F) and O(F)).

Sign up to request clarification or add additional context in comments.

Comments

1

Another way to analyze the complexity is to investigate how many more iterations are added if you double n.

for (i = 1; i <= 2*n; i++){
    for(j = 1; j <= 2*n; j += i) {   // note: not j++
        printf("Iteration %d : %d\n", i, j);   
    }
}

This can be broken up into two loops:

for (i = 1; i <= n; i++){
    for(j = 1; j <= 2*n; j += i) {   // note: not j++
        printf("Iteration %d : %d\n", i, j);   
    }
}

for (i = n+1; i <= 2*n; i++){
    for(j = 1; j <= 2*n; j += i) {   // note: not j++
        printf("Iteration %d : %d\n", i, j);   
    }
}

In the first loop, it looks like the original loop, but the inner loop size has doubled. So, the first loop runs twice as long as the original algorithm.

For the second loop, the runtime is O(n), since the inner loop does 2 iterations for each value of i (excluding the last value of i, for which there is 1 iteration).

So, if T(n) is the runtime of your original algorithm, then

T(2n) = 2×T(n) + C×n

Which is equivalent to

T(n) = 2×T(n/2) + C×n/2

Which is recognizable as the typical binary divide and conquer complexity of O(n lg n).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.