Week 3. Analysis of Algorithms
Basic Analysis
Most algorithms transform input to output objects, the running time of an algorithm typically grows with the input size. We perform analysis of algorithms to understand how the running time grows with the input size.
Average case run time is often difficult to determine so we focus on the worst case running time: it is easier to analyse and crucial for fields such as games, finance and robotics.
We could determine these values experimentally but:
- it is necessary to implement the algorithm, which can be difficult
- we may have a number of potential algorithms for a given task but we only want to implement one
- results may not be indicative of the running time on other inputs not included in the experiment
- to compare algorithms, same software / hw has to be used
Theoretical Analysis
We can use a high-level description of the algorithm (pseudo-code) instead of an implementation which takes all inputs into account. We can evaluate the speed independent of any environment.
For any given algorithm, we determine a function that characterises the worst-case scenario running time of the algorithm for the input size .
Pseudo-code
Pseudo-code is a high-level description of an algorithm, it is less detailed than a program but more structured than just English. It is preferred for describing algorithms and hides program design issues.
Link to original
Random Access Machine Model
Random Access Machine Model
The Random Access Machine Model consists of:
Link to original
- a CPU
- a memory; potentially unbounded bank of memory cells
- unit time; each CPU operation takes unit time; accessing any cell in memory takes unit time This is an approximation of real computers but mostly sufficient for predicting real running times.
Primitive Operations
Primitive Operations
We can model primitive operations (assigning value to variable, comparing two values, evaluating expression, indexing into an array, following object reference, calling a method, and returning from a method) to take a constant amount of time.
Link to original
We can determine the maximum number of primitive operations by inspecting the pseudo-code of the algorithm:
def arrayMax(A, n):
currentMax = A[0] # 2
for i = 1 to n - 1: # 2n + 1
if A[i] > currentMax: # 2(n - 1)
currentMax = A[i] # 2(n - 1)
{ i += 1 } # 2(n - 1)
return currentMax # 1
# Total = 8n - 2Estimating Running Time
Given the algorithm arrayMax executes primitive operations in worst case.
Let us define as the time taken by the fastest primitive operation, and be the time taken by the slowest primitive operation. Given is the worst-case time:
- is the lower bound (fastest worst-case running time)
- is the upper bound (slowest worst-case running time) Hence we can also define as bounded by two linear functions of :
Consider that , then the running time can be found: .
Growth Rate of Running Time
Changing the hardware / software environment affects by a constant factor but does not alter the growth rate of . The linear growth rate of the running time is an intrinsic property of the algorithm and is independent of hardware, implementation and computing environment.
We primarily consider seven functions:
- Constant:
- Logarithmic:
- Linear:
- n Logarithmic:
- Quadratic:
- Cubic:
- Exponential:
The growth rate is not affected by:
- constant factors:
- lower-order terms:
Big-O Notation
Big-O Notation
The Big-O notation gives an upper bound on the growth rate of a function. Given functions and , we say that is , if there are positive constants and such that for .
In general, if is a polynomial of degree then is .
- Drop lower-order terms.
- Drop constant factor in highest order term.
Examples:
Link to original
- is
- is
- is
- is
The statement means that the growth rate of is not greater than the growth rate of . We can use Big-O notation to rank functions according to their growth rate.
| is | is | |
|---|---|---|
| grows more | Yes | No |
| grows more | No | Yes |
| Same growth | Yes | Yes |
Asymptotic Algorithm Analysis
Asymptotic Algorithm Analysis of an algorithm determines the running time in Big-O notation, to perform analysis:
- we find the worst-case number of primitive operations executed as a function of the input size
- we don’t need this function exactly, we just want to express it using Big-O
If we determine that the algorithm
Link to originalarrayMaxexecutes at most primitive operations then we say that the algorithmarrayMax“runs in time”. Since constant factors and lower-order terms are eventually dropped, we can disregard them when counting primitive operations.
Example: Computing Prefix Averages
We can demonstrate Asymptotic Algorithm Analysis with two algorithms for prefix averages.
The -th prefix average of an array is the average of the first elements of : .

For a sample array , we see the data in to look like:

Computing the array of prefix averages of another array has applications in financial analysis.
Quadratic Implementation
The following algorithm computes prefix averages in quadratic time by directly applying the definition:
def prefixAverages(X, n):
Input array X of n integers
Output array A of prefix averages of X
A = int[n]
for i = 0 to n - 1:
s = X[0]
for j = 1 to i:
s = s + X[j]
A[i] = s / (i + 1)
return AThe running time of prefixAverages is hence we have . The sum of the first integers is , hence we can say that prefixAverages runs in time.
Linear Implementation
The following algorithm computes prefix averages in linear time by keeping a running sum.
def prefixAverages(X, n):
Input array X of n integers
Output array A of prefix averages of X
A = new array of n integers
s = 0
for i = 0 to n - 1:
s = s + X[i]
A[i] = s / (i + 1)
return ABig-O and Relatives
There are three different notations we need to be aware of:
- Big-O where is if asymptotically .
- Big-Omega where is if asymptotically .
Big-Omega
Big-Omega defines if there is a constant and an integer constant such that such that for .
Link to original - Big-Theta where is if asymptotically .
Big-Theta
Big-Theta defines if there is a constant and an integer constant such that such that for .
Link to original
Examples:
