1

Need help on how to calculate this time complexity

int func(int n) {
    int a = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= n - i; ++j) {
            for (int k = 0; k <= n - i; ++k) {
                for (int o = 1; o <= i; ++o) {
                    a++;
                }
            }
        }
    }
    return a;
}

enter image description here

Here is where I got it from, but I am not sure if this is correct and dont know how to continue.

Edit: I think I made some progress but I am not sure if this is correct. enter image description here

5
  • @teapot418 this isnt a part of a real code, its a function i simplified for ease of calculation. Commented Jun 20, 2024 at 8:03
  • 3
    wolframalpha.com/… Commented Jun 20, 2024 at 8:05
  • 2
    if you don't know how to compute things like sum(i^a for i=b..c), you can easily bound this below with integral(x^a from b-1 to c) and above with integral(x^a from b to c+1). For example sum(i^2, i=1..n) is bounded below by n^3/3 and above by ((n+1)^3 - 1)/3, which gives that it's Theta(n^3). If you're trying to prove complexities from first principles (rather than apply the "rules of thumb" about nested loops), this trick is very helpful since it helps you calculate tight explicit lower and upper bounds. Commented Jun 20, 2024 at 8:15
  • 1
    @PaulHankin I think you should make that into an answer Commented Jun 20, 2024 at 8:17
  • 1
    Your last step definitely doesn't work. You can't cancel O(n⁴) – O(n⁴) out to zero, because the constants and lower-order terms might be different. For example, 3n⁴ – 2n⁴ is not zero. Commented Jun 20, 2024 at 9:00

2 Answers 2

3

TLDR: O(N⁴)

The outer i loop runs N times.

For each iteration of the i loop, the j loop will run approximately N/2 times. (When i==0 it runs N-1 times. When i==n-1, it runs 0 times. So technically it runs (N-1)/2 times...)

For each iteration of the j loop, the k loop runs approximately N/2 times as well.

With each iteration of the k loop, the inner o loop runs approximately N/2 times as well.

N*(N/2)*(N/2)*(N/2) == N⁴ / 8 => O(N⁴)

The fact that the inner loops run N/2 vs (N-1)/2 is negligible for purposes of computing "big-O" notation complexity.

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

Comments

2

We can get a closed formula for this.

The work done is reflected by the value of 𝑎 that is returned.

The inner three loops respectively iterate 𝑛 − 𝑖 + 1, 𝑛 − 𝑖 + 1 and 𝑖 times on their own, so in total, a++ executes 𝑖(𝑛 − 𝑖 + 1)² times, which is equivalent to 𝑖³ − 2(𝑛+1)𝑖² + (𝑛+1)²𝑖

We can analyse what the outer loop accumulates to for each of these three terms:

  • The sum of the first term is a sum of a sequence of cubes, which is the square of the nth triangle number: (𝑛(𝑛+1)/2)²

  • The sum of the second term is the value −2(𝑛+1) multiplied by the sum of a sequence of squares, and that sum is a square pyramidal number, so we get -2(𝑛+1)𝑛(𝑛+1)(2𝑛+1)/6

  • The sum of the third term is the value (𝑛+1)² multiplied by a triangular number, and so we get (𝑛+1)²𝑛(𝑛+1)/2

Add those three sums together and we get:

𝑛²(𝑛+1)²/4 − 2(𝑛+1)²𝑛(2𝑛+1)/6 + (𝑛+1)³𝑛/2

which simplifies to:

𝑛(𝑛+1)²(𝑛+2)/12 = O(𝑛4)

We can make a quick test by running the function with that formula (using JavaScript here):

function func(n) { // Original function
    let a = 0;
    for (let  i = 1; i <= n; ++i) {
        for (let j = 0; j <= n - i; ++j) {
            for (let k = 0; k <= n - i; ++k) {
                for (let o = 1; o <= i; ++o) {
                    a++;
                }
            }
        }
    }
    return a;
}

function closedForm(n) {
    return n*(n+1)*(n+1)*(n+2)/12;
}

// Run a few tests 
for (let n = 1; n < 20; n++) {
    console.log(func(n), closedForm(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.