Examples on finding time complexity of Iterative programs-1

Brief primer on Time Complexity

Whenever we try to write the solution for any problem statement, it is very important to analyse how much time the code takes to execute on the computer system and how much amount of memory of the system it is consuming. One thing to note here is that we are not always interested in finding the exact amount of time or memory but the approximation. This is the reason we normally talk about asymptotic analysis and the math behind them whenever we discuss performance of some code or algorithm.

Asymptotic Notations

Note 1: If small $\mathcal{o}$ or small $\omega$ is possible then $\Theta$ is not possible.

Note 2: We generally discuss about worst case complexity, therefore Big $\mathcal{O}$ is mostly used to report time and space complexities.

Major time complexity classes for Iterative programs

1. $\mathcal{O}(1)$ time complexity

public static void main(String[] args){
    System.out.println("Mandy8055");
}
public static void main(String[] args){
    for(int i = 1; i < 1000000000; i++)
        System.out.println("Mandy8055");
}

2. $\mathcal{O}(n)$ time complexity

public static void main(String[] args){
    int x, y, z;
    x = y + z; // 1
    for(int i = 1; i < n; i++)
        System.out.println("Mandy8055"); // n
    // Resultant n + 1 or O(n)
}
public static void main(String[] args){
    int j = 0;
    for(int i = 1; i < n; i++)
        System.out.println("Mandy8055"); // n times
    while(j < n){
       System.out.println("Again");  // n times
       j++; 
    }
    // Resultant 2n times or O(n)
}
public static void main(String[] args){
    for(int i = 1; i < n; i = i + 23)
        System.out.println("Mandy8055");  // n / 23 times or O(n)
}
public static void main(String[] args){
    for(int i = n; i > 0; i = i - 23)
        System.out.println("Mandy8055");  // n / 23 times or O(n)
}

1. Example 1

public static void main(String[] args){
    int i = 1;
    while(i < n){
        i += 4;
        i += 6;        // Combined O(n / 11) or O(n)
        i++;
        System.out.println("Mandy8055");
        // Same is applicable for subtraction
    }
}

2. Example 2

public static void main(String[] args){
    int i = 1;
    while(i < n){
        i += 2;
        i -= 4;        // Combined O(n / 8) or O(n)
        i += 10;
        System.out.println("Mandy8055");
    }
}

3. Example 3(CAVEAT)

public static void main(String[] args){
    int i = 1;
    while(i < n){
        i += 2;
        i -= 6;        // Combined n - 3
        i += 1;
        System.out.println("Mandy8055");
    }
    // Infinite loop
}

3. $\mathcal{O}(\log n)$ time complexity

public static void main(String[] args){
    for(int i = 1; i < n; i = i * 23)
        System.out.println("Mandy8055");
}
public static void main(String[] args){
    int i = n;
    while(i > 0){
        System.out.println("Mandy8055");
        i /= 23;
    }
}

Some more examples:

Example 1: Loop executes $\log_{60}n$ times:

public static void main(String[] args){
    int i = 0;
    while(i < n){
        System.out.println("Mandy8055");
        i *= 12;
        i *= 5;
    }
}

Example 2: Loop executes $\log_{6}n$ times:

public static void main(String[] args){
    int i = 0;
    while(i < n){
        System.out.println("Mandy8055");
        i *= 12;
        i *= 5;
        i /= 10;
    }
}

Example 3: CAVEAT for infinite loop:

public static void main(String[] args){
    int i = n;
    while(i > 0){
        System.out.println("Mandy8055");
        i *= 12;
        i *= 5;
        i /= 10;
    }
}

4. $\mathcal{O}(\log\log n)$ time complexity

public static void main(String[] args){
    int i = 12;
    while(i < n){
        System.out.println("Mandy8055");
        i = (int)Math.pow(i, 29);
    }
}
public static void main(String[] args){
    int i = n;
    while(i > 12){
        System.out.println("Mandy8055");
        i = (int)Math.sqrt(i);
    }
}

One more example:

Example 1: Loop executes $(\log_{2}\log_{3} n)$ times:

public static void main(String[] args){
    int i = 3;
    while(i < n){
        i = (int)Math.pow(i, 4);
        i = (int)Math.sqrt(i);
        // The above two statements combined make the value of counter variable to increment as i^2.
    }
}

5. $\mathcal{O}(n^c)$ time complexity

The nested independent loops generally incur the time complexity as $\mathcal{O}(n^c)$ where c is some constant. By independent I mean that the counter or loop variables for the outer as well as the inner loop share no relationship with each other. Let us see some examples to get the clear idea.

Example 1: Notice there is no relationship between outer and inner loop counter variables i and j. The complexity of the below code snippet becomes $\mathcal{O}(n^2)$:

public static void main(String[] args){
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++){
            System.out.println("Mandy8055");
        }
}

Example 2: A slight variation. The complexity becomes $\mathcal{O}(n^3)$:

public static void main(String[] args){
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n*n; j++){
            System.out.println("Mandy8055");
        }
}

Example 3: To calculate the time complexity of below code snippet convert the loop condition in terms of n. The time complexity becomes $\mathcal{O}(n^{3/2})$.

public static void main(String[] args){
    for(int i = 0; i < n; i++)
        for(int j = 0; j * j < n; j++){ // j runs root(n) times or n^(1/2) times
            System.out.println("Mandy8055");
        }
}

In the next post; I share some of my insights on dependent nested loops which are much more interesting and fun to calculate.