CODEBHAVYA • ADS LEVEL 8

📊 Performance Analysis

Learn to predict time and memory requirements, compare alternative solutions and justify complexity using operation counts.

🎯 Learning Objectives

After completing this level, you should be able to:

  • Identify input size and the basic operation of an algorithm.
  • Count operations and express time complexity as a function of input size.
  • Distinguish best, average and worst-case performance.
  • Use Big-O, Big-Omega and Big-Theta notation correctly.
  • Calculate auxiliary space and recursion-stack space.
  • Explain the complexity of common loop and recursive patterns.

🧭 1. What Is Performance Analysis?

Performance analysis studies how the resources required by an algorithm grow when the input becomes larger. The main resources are execution time and memory space.

Core idea: Instead of asking only “How many seconds did this program take?”, we ask “How does the work grow when the input changes from n to 2n or 10n?”

⏱️ Empirical Measurement

Run the program and record actual time or memory.

  • Machine and compiler dependent
  • Requires test data
  • Useful after implementation

📐 Theoretical Analysis

Count operations as a function of input size.

  • Machine independent
  • Works before implementation
  • Explains scalability

🔬 2. Input Size and Basic Operation

First decide what n represents. Then identify the operation that dominates the running time.

ProblemInput SizeBasic Operation
Search an arrayNumber of elements nKey comparison
Sort recordsNumber of records nComparison/movement
Multiply matricesDimension nMultiplication/addition
Traverse a graphVertices V, edges EVisit vertex/edge
Match a stringText n, pattern mCharacter comparison
RAM model: Assignment, arithmetic, comparison and array access are treated as constant-time operations. This abstraction supports machine-independent comparison.

⏱️ 3. Time Complexity

Time complexity is a function T(n) describing how the number of basic operations grows with input size n.

Constant Work

int first = a[0];
printf("%d", first);

Reason: Both statements execute once, independent of n.

Time Complexity: O(1)

Single Loop

for (int i = 0; i < n; i++) {
    sum += a[i];
}

Reason: The loop body executes n times.

T(n) = an + b ⇒ O(n)

Nested Loops

for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
        count++;
    }
}

Reason: n outer iterations × n inner iterations = n².

Time Complexity: O(n²)

Repeated Halving

for (int value = n; value > 1; value /= 2) {
    count++;
}

Reason: After k iterations, n/2ᵏ ≤ 1, so k ≈ log₂n.

Time Complexity: O(log n)

🎭 4. Best, Average and Worst Cases

Best Case

Minimum work for an input of size n.

Linear search: first position → Θ(1)

Average Case

Expected work under an input distribution.

Linear search: about n/2 → Θ(n)

Worst Case

Maximum work for an input of size n.

Linear search: absent/last → Θ(n)
Important: Big-O does not automatically mean “worst case.” Big-O is an upper-bound notation; the case and notation describe different ideas.

📈 5. Asymptotic Notations

Asymptotic analysis focuses on the dominant term as n becomes large. Constants and lower-order terms do not change the growth class.

O(g(n))

Upper Bound

The algorithm grows no faster than a constant multiple of g(n), for sufficiently large n.

Ω(g(n))

Lower Bound

The algorithm grows at least as fast as a constant multiple of g(n).

Θ(g(n))

Tight Bound

The function is bounded above and below by constant multiples of g(n).

Example: T(n) = 3n² + 5n + 7. The dominant term is n², so the tight bound is Θ(n²).

🚀 6. Common Growth Rates

ComplexityNameExampleScalability
O(1)ConstantArray index accessExcellent
O(log n)LogarithmicBinary searchExcellent
O(n)LinearArray traversalGood
O(n log n)LinearithmicMerge SortGood
O(n²)QuadraticTwo full nested loopsLimited
O(n³)CubicBasic matrix multiplicationLimited
O(2ⁿ)ExponentialNaive subset searchPoor
O(n!)FactorialAll permutationsVery Poor
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ) < O(n!)

🧩 7. Rules for Deriving Complexity

1

Sequential blocks: add costs

O(n) + O(n²) = O(n²).

2

Nested loops: multiply iterations

n iterations containing m iterations produce O(nm).

3

Conditional branches

For worst-case analysis, use the more expensive possible branch.

4

Drop constants

O(5n) becomes O(n).

5

Drop lower-order terms

O(n² + n + 1) becomes O(n²).

6

Track loop updates

i++ is often linear; i *= 2 or i /= 2 is often logarithmic.

💾 8. Space Complexity

Space complexity measures memory used as a function of input size. Auxiliary space is additional memory apart from the input.

Fixed Space

Counters and a fixed number of variables.

Iterative maximum → O(1)

Variable Space

Arrays, dynamic nodes, recursion frames or tables.

Copied array → O(n)

Recursion Stack

long long factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

Time: O(n), because there are n calls.

Auxiliary Space: O(n), because n call frames may remain active.

Amortized analysis: A dynamic-array resize may cost O(n), but across n appends the total copying is O(n). Therefore, append has amortized O(1) time.
INTERACTIVE ALGORITHM VISUALIZATION

🎬 9. Premium Complexity Growth Visualizer

CodeBhavya

Choose an input size and loop pattern. Results appear only after you click Analyze Growth.

Select values and click Analyze Growth.

💻 10. Compare Linear and Binary Search

This program counts comparisons for the same key. Binary search requires a sorted array.

#include <stdio.h>

int linearSearch(int a[], int n, int key, int *count) {
    for (int i = 0; i < n; i++) {
        (*count)++;
        if (a[i] == key) return i;
    }
    return -1;
}

int binarySearch(int a[], int n, int key, int *count) {
    int low = 0, high = n - 1;
    while (low <= high) {
        int mid = low + (high - low) / 2;
        (*count)++;
        if (a[mid] == key) return mid;
        if (a[mid] < key) low = mid + 1;
        else high = mid - 1;
    }
    return -1;
}

int main(void) {
    int n, key;
    scanf("%d", &n);
    int a[n];
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    scanf("%d", &key);

    int linearCount = 0, binaryCount = 0;
    int linearIndex = linearSearch(a, n, key, &linearCount);
    int binaryIndex = binarySearch(a, n, key, &binaryCount);

    printf("Linear Search: index = %d, comparisons = %d\n", linearIndex, linearCount);
    printf("Binary Search: index = %d, comparisons = %d\n", binaryIndex, binaryCount);
    return 0;
}

Sample Input

8
4 9 13 18 25 31 42 57
42

Sample Output

Linear Search: index = 6, comparisons = 7
Binary Search: index = 6, comparisons = 3
Linear SearchBest O(1), Worst O(n)

It may inspect every item.

Binary SearchBest O(1), Worst O(log n)

It halves the interval.

Auxiliary SpaceO(1) for both

Both versions are iterative.

🔍 11. Program Tracing — Linear vs Binary Search

Trace the sample program one statement at a time and compare how both searches locate key 42.

💼 12. Interview Points

  • Explain the reason: state how many times the dominant operation executes.
  • Mention assumptions: sorted input, balanced trees and uniform hashing can change complexity.
  • Separate time and space: an in-place algorithm may use little memory but still run slowly.
  • Discuss trade-offs: extra memory or preprocessing may reduce repeated-query time.
  • Use tight bounds: state Θ when upper and lower growth match.
01

Worst-case versus amortized complexity?

02

Why does recursive binary search use O(log n) space?

03

Can O(n²) also be O(n³)? Which is tighter?

04

When is extra space worth using?

✍️ 13. Practice Problems

Solve each problem first. Use Hint only when needed and Show Answer to verify your reasoning.

1. Complexity of accessing a[k]?

2. Complexity of one loop from 0 to n−1?

3. Complexity of two full nested loops?

4. Complexity when i doubles from 1 until n?

5. Complexity of a triangular loop with j from i to n−1?

6. Combine sequential Θ(n) and Θ(n²) blocks.

7. Best and worst time of iterative binary search?

8. Best and worst time of insertion sort?

9. Auxiliary space of recursive factorial?

10. Auxiliary space of iterative factorial?

11. Tight bound for 3n² + 5n + 7?

12. Order log n, n, n log n, n² and 2ⁿ.

13. Maximum binary-search comparisons for n = 1024?

14. Estimate n log₂n for n = 8.

15. At n = 10, compare n² and 2ⁿ.

16. Why is dynamic-array append amortized O(1)?

17. Space for an additional n × n matrix?

18. Solve T(n) = T(n/2) + O(1).

19. Solve T(n) = 2T(n/2) + O(n).

20. Why preserve both sizes in O(n + m)?

📝 14. Quick Revision

  • Performance analysis studies resource growth as input size increases.
  • Time counts dominant operations; space counts growing memory.
  • Big-O is an upper bound, Ω is a lower bound and Θ is a tight bound.
  • Sequential costs add; nested iteration counts multiply.
  • Repeated doubling or halving usually gives logarithmic complexity.
  • Recursive algorithms may consume call-stack space.
  • Amortized analysis distributes occasional expensive operations across a sequence.