Complexity Analysis
1. Formal Definitions
Asymptotic Notation
Let be functions.
Big-O (Upper Bound)
Intuition: is eventually bounded above by a constant multiple of .
Big-Omega (Lower Bound)
Intuition: is eventually bounded below by a constant multiple of .
Big-Theta (Tight Bound)
Intuition: grows at the same rate as , up to constant factors.
Little-o (Strict Upper Bound)
Little-omega (Strict Lower Bound)
2. The Complexity Hierarchy
Theorem. For polynomial-exponential functions, the following hierarchy holds:
Proof (selected). We prove :
Similarly, :
And for any constant :
Common Complexity Classes
| Class | Name | Example |
|---|---|---|
| Constant | Array access | |
| Logarithmic | Binary search | |
| Linear | Linear search | |
| Linearithmic | Merge sort, heap sort | |
| Quadratic | Bubble sort, insertion sort | |
| Cubic | Floyd-Warshall, matrix multiply | |
| Exponential | Subset enumeration, brute force | |
| Factorial | Permutation enumeration (TSP) |
Board-specific AQA requires Big-O notation for standard algorithms (searching, sorting); focuses on time complexity. CIE (9618) covers Big-O, Big-Theta, and Big-Omega notation; requires space complexity analysis. OCR (A) requires Big-O notation; may require comparison of algorithm performance. Edexcel covers time and space complexity with Big-O notation.
3. Best, Average, and Worst Case
Definitions
For an algorithm with input size , let be the set of all possible inputs of size , and let be the running time of algorithm on input .
- Best case:
- Worst case:
- Average case:
where is the probability of input .
Example: Quick Sort
| Case | Complexity | When |
|---|---|---|
| Best | Pivot always splits in half | |
| Average | Random inputs (expected) | |
| Worst | Already sorted, min/max pivot |
Pitfall Average case assumes a uniform distribution of inputs. Real-world data may not be uniformly distributed. Always state the distribution assumption when discussing average case.
4. Analyzing Recursive Algorithms
The Master Theorem
For recurrences of the form where , :
Let . Compare with :
| Case | Condition | Solution |
|---|---|---|
| 1 | ||
| 2 | ||
| 3 | and |
Worked Examples
Example 1: Merge sort:
. . Case 2 with : .
Example 2: Binary search:
. . Case 2 with : .
Example 3:
. . Case 1: .
Example 4:
. . Check regularity: for . Case 3: .
5. Amortized Analysis
Motivation
Some operations are expensive in isolation but cheap on average. Amortized analysis gives the average cost per operation over a worst-case sequence of operations (not average over random inputs).
Methods
Aggregate Method
Compute the total cost of operations and divide by .
Example: Dynamic array. Total cost of insertions: (proved in Arrays and Records). Amortised cost per insertion: .
Accounting Method
Assign an amortised cost to each operation. Some operations are charged more than their actual cost (creating a "credit"); others are charged less (consuming credit). The credit must always be non-negative.
Example: Dynamic array. Charge \3$1$2kO(k)2kk$ insertions.
Potential Method
Define a potential function mapping data structure states to non-negative real numbers. The amortised cost of operation is:
Total amortised cost:
Since and : .
Example: Dynamic array. Let when size capacity/2, else 0.
- Insert without resize: Actual cost = 1. increases by 2. Amortised cost = .
- Insert with resize from to : Actual cost = (copy) + 1 (insert). Before: size = , capacity = , . After: size = , capacity = , . Change: . Amortised = .
Amortised cost per operation: .
6. Practical Analysis
Space-Time Tradeoffs
Sometimes we can reduce time complexity by using more memory, or reduce space by accepting more time.
| Tradeoff | Example |
|---|---|
| Hash table | search vs space |
| Memoization | repeated lookups vs space |
| Precomputed tables | trig functions vs large ROM |
Logarithmic Factors
grows very slowly. For all practical input sizes, is often acceptable even when is achievable with more complex algorithms.
| 10 | ||
| 20 | ||
| 30 |
Problem Set
Problem 1. Prove that using the formal definition.
Answer
We need to find and such that for all .
For : .
Choose and . ✓
(More tightly: also works.)
Problem 2. Prove that .
Answer
Proof by contradiction. Assume . Then such that for all .
This implies for all . But grows without bound, so this is impossible for any fixed . Contradiction.
Equivalently: , so .
Problem 3. Use the Master Theorem to solve .
Answer
, , .
.
This is Case 2 with : .
Problem 4. Determine the time complexity of the following function:
def mystery(n):
count = 0
i = 1
while i < n:
j = 1
while j < n:
count += 1
j *= 2
i *= 3
return count
Answer
Outer loop: takes values . Number of iterations: .
Inner loop: takes values . Number of iterations: .
Total: .
Problem 5. Show that .
Answer
Upper bound: .
Lower bound: .
Therefore: .
Problem 6. A student claims that an algorithm with time complexity is always slower than one with complexity . Is this correct? Explain.
Answer
No. Big-O is an asymptotic upper bound — it describes behaviour as . For small , the algorithm might be faster due to smaller constant factors.
Example: Algorithm A takes operations, Algorithm B takes operations. For : A ≈ ; B = . B is much faster.
The crossover point is where , i.e., , which is at . Below this, B is faster.
Problem 7. Perform amortized analysis of a stack that supports push (), pop (), and multipop(k) which pops elements (, or ).
Answer
Aggregate method. In a sequence of operations, each push adds one element. Each pop removes one element. Each multipop removes elements. Total elements removed total elements pushed . Total cost of all multipop and pop operations . Total push cost: . Total: . Amortised per operation: .
Accounting method. Charge \2$1$1$1$0O(1)$.
Potential method. Let (number of elements on the stack).
- Push: actual = 1, increases by 1. Amortised = .
- Pop: actual = 1, decreases by 1. Amortised = .
- Multipop(k): actual = , decreases by . Amortised = .
All amortised costs: .
Problem 8. Determine the time complexity of the following recursive function:
def f(n):
if n <= 1:
return 1
return f(n - 1) + f(n - 1)
Answer
, .
This is not in Master Theorem form (requires , not ).
Expanding: .
.
This is the Fibonacci-like recursion without memoization, leading to exponential time.
Problem 9. Rank the following functions in order of increasing growth rate: , , , , , , .
Answer
First simplify: (assuming is base 2).
Growth rates (slowest to fastest):
Verification of selected orderings:
- : ✓
- : ✓
- : ✓
- : ✓
Problem 10. Prove that .
Answer
Proof. Let and . Then such that and for all .
Then: .
Let . Then , so .
Corollary. If , then .
For revision on specific algorithm complexities, see Sorting Algorithms and Searching Algorithms.
Problems
Problem 1. Determine the Big-O time complexity of the following code fragment:
for i in range(n):
for j in range(i):
print(i, j)
Hint
Count the total number of times the inner loop body executes. When i = 0, the inner loop runs 0 times. When i = 1, it runs 1 time. When i = 2, it runs 2 times. Sum this series.
Answer
The inner loop runs i times for each value of i from 0 to n−1.
Total iterations =
The dominant term is , so the time complexity is .
The constant and the term are dropped because Big-O ignores constant factors and lower-order terms.
Problem 2. Determine the Big-O time complexity of the following code fragment:
i = 1
while i < n:
for j in range(n):
print(j)
i *= 2
Hint
The outer loop variable i doubles each iteration (1, 2, 4, 8, ...). How many times does the outer
loop run? How many times does the inner loop run per outer iteration?
Answer
Outer loop: i takes values 1, 2, 4, 8, ..., . The number of iterations is
.
Inner loop: Runs n times per outer iteration.
Total: .
Problem 3. Algorithm A has time complexity with a constant factor of 10, and Algorithm B has time complexity with a constant factor of 1. For approximately what values of is Algorithm A faster than Algorithm B?
Hint
Algorithm A performs approximately operations and Algorithm B performs approximately operations. Solve , which simplifies to .
Answer
We need , which simplifies to (for ).
Testing values:
| A faster? | |||
|---|---|---|---|
| 10 | 33.2 | 10 | No |
| 20 | 43.2 | 20 | No |
| 30 | 49.1 | 30 | No |
| 40 | 53.2 | 40 | No |
| 50 | 56.4 | 50 | No |
| 60 | 59.1 | 60 | Yes |
Algorithm A becomes faster at approximately .
This demonstrates that Big-O notation only describes asymptotic behaviour. For small inputs, the algorithm with the better Big-O complexity can be slower due to larger constant factors.
Problem 4. Compare the time complexities of the following pairs of algorithms and state which is more efficient asymptotically. Justify each answer: (a) vs , (b) vs , (c) vs .
Hint
Use the limit test: if , then , meaning grows strictly slower than .
Answer
(a) is more efficient than . . So — it grows strictly slower.
(b) is more efficient than . (exponential always dominates polynomial). So .
(c) is more efficient than . (by L'Hôpital's rule or the known hierarchy). So .
Summary (most to least efficient): .
Problem 5. Analyze the space complexity of the following function:
def create_matrix(n):
matrix = []
for i in range(n):
row = [0] * (2 * n)
matrix.append(row)
return matrix
Hint
Count the total number of elements stored. The matrix has n rows, each containing 2 * n
elements.
Answer
The function creates a matrix with:
nrows- Each row contains
2nelements (integers) - Total elements:
Each integer occupies space, and the list overhead is also per element.
Space complexity: .
The dominant factor is the total number of stored values (). The variable i, the row
reference, and the loop overhead all use additional space and are negligible compared to the
matrix itself.
Problem 6. Analyze the space complexity of the following recursive function:
def sum_recursive(arr, n):
if n <= 0:
return 0
return arr[n-1] + sum_recursive(arr, n-1)
Hint
Recursive functions use stack space proportional to their recursion depth. How deep does the recursion go, and how much space does each stack frame use?
Answer
Recursion depth: The function calls itself with n-1 until n <= 0. This means the maximum
depth is n (from sum_recursive(arr, n) down to sum_recursive(arr, 0)).
Stack frame size: Each frame stores the parameters arr (a reference, ) and n (an
integer, ), plus the return address and local variables — all per frame.
Total space: .
Space complexity: due to the call stack. The input array arr itself is not counted in
the auxiliary space analysis since it is the input, not allocated by the function.
Note: An iterative version of this function would use only auxiliary space.
Problem 7. State the best-case, average-case, and worst-case time complexity of quicksort. For each case, describe the type of input that produces that complexity and explain why it occurs.
Hint
Quicksort's performance depends on how the pivot partitions the array. Consider what happens when the pivot is the median element, a random element, and the minimum or maximum element.
Answer
| Case | Complexity | Input condition | Explanation |
|---|---|---|---|
| Best | Pivot always divides array in half | Each level processes all elements, and there are levels. Total: . | |
| Average | Random inputs | With a random pivot, the expected split ratio is balanced. The recurrence holds on average. | |
| Worst | Already sorted (with first/last pivot) | If pivot is always the minimum or maximum, one partition has elements and the other has 0. Recurrence: . |
Mitigation: Randomised quicksort (choosing a random pivot) or median-of-three pivot selection makes the worst case extremely unlikely in practice, giving expected regardless of input.
Problem 8. Explain why binary search has time complexity. In your answer, show how the search range halves at each step and derive the maximum number of iterations mathematically.
Hint
Start with a range of size . After each comparison, the range is reduced to at most half. After comparisons, the range size is at most . When does this become less than 1?
Answer
Intuitive argument: Binary search halves the search range at each step:
- After 1 comparison: range ≤
- After 2 comparisons: range ≤
- After 3 comparisons: range ≤
- After comparisons: range ≤
The algorithm terminates when the range has fewer than 1 element:
So the maximum number of iterations is .
Formal derivation using the Master Theorem:
Here , , . Since , this is Master Theorem Case 2 with :
Why log n is efficient: grows extremely slowly. For (one billion), . This means binary search finds any element in a sorted billion-element array with at most 30 comparisons.
Problem 9. Determine the Big-O time complexity of the following recursive function:
def recursive_func(n):
if n <= 0:
return 0
x = 0
for i in range(n):
x += i
return x + recursive_func(n // 2)
Hint
Set up the recurrence relation. The function does work (the for loop) and then calls itself with . This gives . Apply the Master Theorem.
Answer
Recurrence relation:
The term comes from the for loop that iterates n times. The recursive call passes .
Applying the Master Theorem: , , .
where .
Check regularity condition: for . ✓
This is Case 3 of the Master Theorem: .
Verification by expansion:
Time complexity: .
Problem 10. (Exam-style) Two algorithms solve the same problem — counting inversions in an array (the number of pairs (i, j) where i < j but A[i] > A[j]).
Algorithm P uses a brute-force approach:
def algorithm_p(arr):
result = 0
for i in range(len(arr)):
for j in range(i + 1, len(arr)):
if arr[i] > arr[j]:
result += 1
return result
Algorithm Q uses a modified merge sort that counts inversions during the merge step.
(a) Determine the time complexity of each algorithm. (b) For , estimate the number of operations for each algorithm. (c) Which algorithm is more efficient and by what factor? Explain your reasoning.
Hint
For Algorithm P, count the number of iterations of the nested loops. For Algorithm Q, recall that merge sort is and the modification to count inversions doesn't change the asymptotic complexity.
Answer
(a) Time complexity:
Algorithm P: The outer loop runs times (i = 0 to n−1). For each i, the inner loop runs from i+1 to n−1, which is iterations.
Total iterations:
Time complexity of P: .
Algorithm Q: Modified merge sort follows the same divide-and-conquer structure as standard merge sort. Each level processes all elements during merging, and there are levels. The inversion count is accumulated during the merge step without changing the merge complexity.
Time complexity of Q: .
(b) Estimated operations for :
| Algorithm | Formula | Operations |
|---|---|---|
| P | ||
| Q |
(c) Comparison:
Algorithm Q is significantly more efficient. The ratio is:
Algorithm Q is approximately 376 times faster than Algorithm P for .
Reasoning: The difference grows with . For , Algorithm P would perform operations while Algorithm Q performs — a factor of 25,000×. This demonstrates the critical importance of choosing algorithms with better asymptotic complexity for large inputs.
:::