2 ComplexityAnalysis Version2
2 ComplexityAnalysis Version2
CS211
Complexity
Analysis
Lectures 2 & 3
Samar Alsaleh
Spring 2022
04
03 Finishing Thoughts
Summary & resources
02 Code Complexity
Finding complexity of code structures
Theoretician: seeks to
understand
4
Running Time
7
Algorithm Analysis
Why analyze algorithms?
- The same problem can be solved with different algorithms which differ in efficiency
- We analyze the algorithms to:
- Evaluate algorithm performance
- Compare different algorithms
- Provide guarantees
- Understand theoreKcal basis
- We focus on analyzing :
- Running Kme
- Memory usage
- Worst-case and typical case
➔ Analysis of algorithms compares algorithms NOT programs
8
Algorithm Analysis (cont.)
- The analysis of algorithms involves evaluaKng an algorithm in terms of Kme or space
efficiency
- An algorithm that solves a problem but requires a year is hardly of any use
- Likewise, an algorithm that requires thousands of gigabytes of main memory is not
(currently) useful on most machines
- The efficiency of an algorithm is usually expressed in terms of CPU Kme
- To evaluate an algorithm’s efficiency, real-Kme units such as microseconds and
nanoseconds should not be used
- Rather, logical units that express a relaKonship between the input size n (of a file or
an array) and the amount of Kme t required to process the data should be used
9
Algorithm Analysis (cont.)
12
Machine Independence
- The evaluaKon of efficiency should be machine independent
- It is not useful to measure how fast the algorithm runs as this depends on which
computer, OS, programming language, compiler, and kind of inputs are used in tesKng
- Instead,
- we count the number of basic operaKons the algorithm performs
- A basic operaKon is an operaKon which takes a constant amount of Kme to execute
- we calculate how this number depends on the size of the input
- The efficiency of an algorithm is the number of basic operaKons it performs
- This number is a funcKon of the input size n
13
Basic OperaKons
Common Basic Operations
Operation Example
Arithmetic operations * , /, % , + , ×
Assignment statements of simple data types x = 12
Reading of primitive types [Link]
Writing of a primitive type [Link]
Simple conditional tests if(x < 3) …
Method call addXY(x, y);
A method's return statement return x;
Memory Access fetch/store
Notes:
- Assignment and increment/decrement operaKons (such as ++ , + = ) are considered as consisKng of two
basic operaKons
- The execuKon Kme of the method itself may depend on the value of parameter and it may not be constant
- To simplify complexity analysis we will not consider memory access (fetch or store) operaKons
14
Algorithm Complexity: Growth FuncKons
- ComputaKonal complexity is a measure of the degree of difficulty of an algorithm
- It indicates how much effort is needed to apply an algorithm or how costly it is
- This cost can be measured in a variety of ways
- We must decide what we are trying to efficiently opKmize
- %me complexity – CPU Kme
- space complexity – memory space
- CPU Kme is generally the focus
- The rates of growth are expressed as funcKons, which are generally in terms of the
number of inputs n
- A growth funcKon shows the relaKonship between the size of the problem (n) and the
value we hope to opKmize (!me or space)
- This funcKon represents the Kme or space complexity of the algorithm 15
Algorithm Complexity: Growth FuncKons (cont.)
Important Growth FuncKons:
- There are seven funcKons that oSen appear in
algorithm analysis:
- Constant funcKon: f(n) = c
- Logarithmic funcKon: f(n) = log n
- Linear funcKon: f(n) = n
- N-Log-N funcKon: f(n) = n log n
- QuadraKc funcKon: f(n) = n 2
- Cubic funcKon: f(n) = n 3
- ExponenKal funcKon: f(n) = 2n
- In a log-log plot, the slope of the line corresponds to
the growth rate of the funcKon
16
Algorithm Complexity: The Cases
- Worst Case Complexity:
- The funcKon defined by the maximum
number of steps taken on any instance
of size n 500
Wort Case
Problem/Input Size, n
➔ average over inputs of size n
17
Algorithm Complexity: The Cases (cont.)
Example: Linear Search Complexity
- Best Case: Item found at the beginning
- One comparison
- Worst Case: Item found at the end
- n comparisons
- Average Case: Item may be found at index 0, or 1, or 2, . . . or n - 1
- Average number of comparisons is: (1 + 2 + . . . + n)/n = (n + 1)/2
18
Algorithm Complexity: The Strategy
- We are usually interested in determining
the largest number of operaKons that might n0
be performed for a given problem size
- Best case depends on the input
Complexity ( f (n) ) →
- Average case is difficult to compute actual running
time
- So, we usually focus on worst case analysis
- Easier to compute
- Usually close to the actual running Kme
- Crucial to real-Kme systems (e.g. air-
traffic control)
Strategy: Input size ( n ) →
- Try to find upper and lower bounds of the Upper and Lower Bounds for n > n0
worst-case funcKon
19
02
Algorithm 1
n Data Items
10010101000100100 ➔ ➔ Returns in time T2(n)
10010010101000101
0000100100100
22
AsymptoKc Complexity (cont.)
Growth Rates vs. n Size
* Assuming: 1 GHz CPU and that it can execute on average one instrucKon in 1 nanosecond
23
AsymptoKc Complexity (cont.)
- AsymptoKc complexity is based on the dominant term of the growth funcKon
- The term that increases most quickly as n increases
- We approximate f(n) by a funcKon g(n) in a way that does not substanKally change the
magnitude of f(n)
- The funcKon g(n) is sufficiently close to f(n) for large values of the input size n
- This "approximate" measure of efficiency is called asymptoKc complexity
- It does not give the exact number of operaKons of an algorithm, but it shows how that
number grows with the size of the input
- This gives us a measure that will work for different operaKng systems, compilers and CPUs
- AsymptoKc bounds are used to esKmate the efficiency of algorithms by assessing the
amount of Kme and memory needed to accomplish the task for which the algorithms
were designed
24
AsymptoKc NotaKons
- AsymptoKc notaKons are mathemaKcal notaKons used to describe the running Kme of an
algorithm when the input tends towards a parKcular value or a limiKng value
- The most commonly used asymptoKc notaKons are:
- O (oh) NotaKon
- O(expression) gives an upper bound on the growth rate of a funcKon
- It describes the worst-case scenario or the longest amount of Kme an algorithm can possibly
take to complete
- × (omega) NotaKon
- ×(expression) gives a lower bound on the growth rate of a funcKon
- It measures the best-case scenario or the shortest amount of Kme an algorithm can possibly
take to complete
- − (theta) NotaKon
- −(expression) consist of all the funcKons that lie in both O(expression) and ×(expression)
- It indicates that the upper and lower bounds are the same within a constant factor
25
Big-O NotaKon
Big O, DefiniKon:
- f(n) = O(g(n)) if and only if there are posiKve constants c and n0 such that f(n) × c g(n)
when n − n0
- This says that funcKon f(n) grows, asymptoKcally, at a rate no faster than g(n) and up to
a constant factor
- Thus g(n) is an upper bound on f(n)
Another way:
f(n) is O(g(n)) ↔ there exist numbers c, n0 > 0
such that for each n ≥ n0
f(n) ≤ c.g(n)
26
Big-O NotaKon: IllustraKon
c . g(n)
f(n)
n0 n
f(n) = O(g(n))
Growth f(n) is × growth of g(n)
27
Big-× NotaKon
Big ×, DefiniKon:
- f(n) = ×(g(n)) if and only if there are posiKve constants c and n0 such that
f(n) − c g(n) when n − n0
- This says that funcKon f(n) grows, asymptoKcally, at a rate no slower than g(n)
- Thus g(n) is a lower bound on f(n)
Another way:
f(n) is Ω(g(n)) ↔ there exist numbers c, n0 > 0
such that for each n ≥ n0
f(n) ≥ c.g(n)
28
Big-× NotaKon: IllustraKon
f(n)
c . g(n)
n0 n
f(n) = ×(g(n))
Growth f(n) is − growth of g(n)
29
Big-× NotaKon
Big ×, DefiniKon:
- f(n) = ×(g(n)) if and only if f(n) = O(g(n)) and f(n) = −(g(n))
- This says that funcKon f(n) grows, asymptoKcally, at the same rate as g(n)
Another way:
f(n) is θ(g(n)) ↔ there exist numbers c1, c2, n0 > 0
such that for each n ≥ n0
c1.g(n) ≤ f(n) ≤ c2.g(n)
30
Big-× NotaKon: IllustraKon
cc2 .⋅ g(n)
g(n)
2
f(n)
f(n)
cc1⋅. g(n)
g(n)
1
nN0 nn
f(n) = ×(g(n))
Growth f(n) is = growth of g(n)
31
AsymptoKc NotaKons Summary
Asymptotic
Mathematical Expression Relative Rates of Growth
Notation
32
Big-Oh Rules
- Remember that Big-Oh is simplified analysis of an algorithm’s efficiency
- When using big-oh notaKon, the following simplificaKon rules can be applied:
Rule 1: Ignore constant factors
- O( c . f(n) ) = O( f(n) )
- If f(n) is a product of several factors, any constants can be omiVed
- Examples:
- O(5n) = O(n)
- O(300n 2) = O(n 2)
33
Big-Oh Rules (cont.)
Rule 2: Simplify polynomial funcKons
- If a funcKon f(n) is a polynomial of degree d, then f(n) is O(n d)
- Examples:
- O(n 3 + 3n 2 + 4n + 6) = O(n 3)
- O(21n 8 + 33n 5 + 4n 2 + 6) = O(n 8)
34
Big-Oh Rules (cont.)
Rule 3: Ignore smaller terms
- If f(n) is a sum of several terms, only keep the one with the largest growth rate
- Examples:
- O(3n log n + 5 log n + 10n + 6) = O(n log n)
- O(n 3 + 2n 2 + 3n log n + 7) = O(n 3)
- O(2000n 3 + 2n! + n 800 + 10n + 27n log n + 5) = O(n!)
35
Big-Oh Rules (cont.)
Rule 4: Ignore logarithms base
- loga n = O(logb n)
- For any a and b that are greater than one
- Example:
- O(3 log3 n + 2 log4 n) = O(log n) NOT O(log3 n)
36
Big-Oh Rules (cont.)
Rule 5: AddiKon Rule
- O( f(n)) + O(g(n)) = O( max( f(n), g(n)) )
- If an algorithm takes O( f(n) + g(n)) steps and funcKon f(n) is bigger than g(n), then
algorithm’s performance can be simplified to O( f(n))
- Example1:
- f(n) = n 2, g(n) = n 3
- O( f(n) + g(n)) = O(max(n 2, n 3)) = O(n 3)
- Example2:
- f(n) = n 3 + n 2 log n × O(n 3)
- g(n) = n 3 + 2n! + 10n × O(n!)
- O( f(n) + g(n)) = O(max(n 3, n!)) = O(n!)
37
Big-Oh Rules (cont.)
Rule 6: MulKplicaKon Rule
- O( f(n)) × O(g(n)) = O( f(n) × g(n))
- If an algorithm performs an operaKon that takes f(n) steps, and for every step
performs another operaKon that takes g(n) steps, algorithm’s total performance is
O( f(n) × g(n))
- Example:
- f(n) = n 3 + 2n 2 + 3n log n + 7 − O(n 3)
- g(n) = 8n 2 + 5n + 2 − O(n 2)
- O( f(n) × g(n)) = O((n 3) × (n 2)) = O(n 5)
38
Big-Oh Rules (cont.)
Rule 7: Tight bounding
- Always use the smallest possible class of funcKons to bound an algorithm
- f(n) = 2n × O(n) instead of O(n 2)
- f(n) = n + (log n − log n) × O( n) instead of O(n 2)
39
Comparing Growth FuncKons
- You might think that faster processors would make efficient algorithms less important
- A faster CPU helps, but not relaKve to the dominant term
Affect on an algorithm that runs for a few seconds
Growth Rate
Time after 100x more data Problem size after 100x speedup
n A few minutes 100x
n2 Several hours 10x
n3 Several weeks 4-5x
2n Forever 1x
c < log4 n < log3 n < log2 n < log2 n < logk n < n < n < n log2 n < n 2 < n 3 < 2n < 3n < n! < n n
41
Comparing Growth FuncKons (cont.)
f(n) = n 2 g(n) = n 3
1 n →1 < n
g(n) grows faster than f(n) × f(n) = O(g(n)) OR g(n) = −( f(n))
- AlternaKvely, you can subsKtute with a large value of n in both funcKons and compare
the numeric results
43
Comparing Growth FuncKons (cont.)
- Example: Which of the following funcKons grows faster?
- Note that log 3 < log 2 numerically, but asymptoKcly they are the same
46
Big-Oh Categories & ImplicaKons
Class Name Practical Implication
Common Big-Oh categories and pracKcal
02 Code Complexity
Finding complexity of code structures
50
Analyzing Loop ExecuKon
Loops: for, while, and do-while
- Loops complexity is determined by the number of iteraKons in the loop mulKplied by
the complexity of the body of the loop
- Loop complexity = Number of iteration × Body complexity
- Examples:
Number of loop executions = n
for (int count = 0; count < n; count++)
//some sequence of O(1) steps ➡ Body complexity = O(1)
Loop complexity = n × O(1) = O(n)
}
n = n / 2;
//some sequence of O(1) steps ➡ O(log n)
56
57
56
Analyzing Loop ExecuKon (cont.)
Netsted loops:
- When loops are nested, we mulKply the complexity of the outer loop by the
complexity of the inner loop
- Nested loops complexity = complexity of inner loop × complexity of outer loop
- Examples:
for (int count=0; count<n; count++) Both the inner and outer loops
n
n
for (int count2=0; count2<n; count2++)
// some sequence of O(1) steps ➡ have complexity of O(n)
The overall efficiency is O(n 2)
sum = 0
n 2
for(int i = 0; i < n*n; i++)
n for(int j = 0; j < n; j++)
sum += i * j ;
➡ O(n ) 3
58
57
Analyzing Loop ExecuKon (cont.)
- More examples:
i = 1;
while(i <= n) {
j = 1;
while(j <= n){
n
log n
}
//statements of constant complexity
j = j*2; ➡ O(n log n)
i = i+1;
}
60
59
Analyzing Sequence of Statements (cont.)
- More example:
n for (i = 1; i <= n; i++)
sum = sum + i; Complexity: O(n) + O(n 2)
n2
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++)
sum = sum + i + j;
➡ = O(max(n, n 2))
= O(n 2)
}
3
for (i = 1; i <= n*n; i++)
Complexity: O(n 3) + O(1) + O(n 2) + O(n)
n for (j = 1; j <= n; j++)
sum = sum + i + j; = O(max(n 3, 1, n 2, n))
1 sum = sum / n; = O(n 3)
n2
for (i = 1; i <= n*n; i++)
sum = sum + i;
➡ OR: O(n 3) + O(1) + O(n 2) + O(n)
for (j = 1; j <= n; j++)
= O(n 3 + n 2 + n + 1)
n = O(n 3)
sum = sum + j * j;
61
60
Analyzing CondiKonal Statements
CondiKons: if-else, switch
- Take the complexity of the most expensive case
- if(test) s1 else s2
- The running Kme is never more than the running Kme of the test plus the larger of
the running Kmes of s1 and s2
- Example:
1 if (test == 1)
for (i = 1; i <= n; i++)
n
sum = sum + i;
Running time = 1 + max(n, n 2)
n2
else
for (i = 1; i <= n; i++)
➡ = O(n 2)
for (j = 1; j <= n; j++)
sum = sum + i + j;
61
62
Analyzing CondiKonal Statements (cont.)
- if-else Example:
char key;
int[][] A = new int[n][n];
int[][] B = new int[n][n];
int[][] C = new int[n][n];
: : :
2
n if(key == ‘+') {
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
C[i][j] = A[i][j] + B[i][j];
➡ Overall Complexity: O(n 3)
} // End of if block
3 else if(key == 'x')
n
C = matrixMult(A, B);
1 else
[Link]("Error! Enter '+' or 'x'!");
63
62
64
+ (max O(n2,n))
65
66
Analyzing Method Calls
- The body of a loop may contain a call to a method
- To determine the order of the loop body, the order of the method must be taken into
account
- The overhead of the method call itself is generally ignored
- Loop example:
static int myMethod(int n){
int sum = 0;
log n for(int i = 1; i < n; i = i * 2)
sum = sum + i + helper(i);
return sum; n
}
static int helper(int n){
int sum = 0;
➡ Complexity of myMethod(): O(n log n)
65
69
Example Algorithms’ ComplexiKes
- Examples of Algorithms and their big-O complexity
Big-O Notation Example Algorithms
Push, Pop, Enqueue (if there is a tail reference),
O(1)
Dequeue, Accessing an array element
O(log(n)) Binary search
O(n) Linear search
O(n log(n)) Heap sort, Quick sort (average), Merge sort
O(n2) Selection sort, Insertion sort, Bubble sort
O(n3) Matrix multiplication
O(2n) Towers of Hanoi
66
70
04
03 Finishing Thoughts
Summary & resources
02 Code Complexity
Finding complexity of code structures
Textbooks Chapters:
- Data Structures and Algorithms in Java, Ch2
- Data Structures and Algorithm Analysis in Java, Ch2
Short Videos:
- What Is Big O? (Comparing Algorithms)
- Big-O notation in 5 minutes
- #2.4- ﺷﺮح ﻣﻔﻬﻮم اﻟـBig O - Notation
Credits:
- Some slides illustrative content is courtesy of Prof.
Robert Sedgewick, Princeton University
73
69
Resources & More…
Other Resources:
- Java Software Structures, - Designing and Using Data
Structures,4th edition, Lewis and Chase
- Data Structures and Problem Solving Using Java, 4th edition, Weiss
- Data Structures & Algorithms in Java, 3rd edition, Drozdek
- Data Structures and Algorithm Analysis in Java, 3rd edition, Weiss
- Algorithms, 4th edition, Sedgewick and Wayne
- A Practical Introduction to Data Structures and Algorithm Analysis,
3rd edition, Shaffer
- Data Structures and Algorithms in Java, 6th edition, Goodrich,
Tamassia and Goldwasser
- Slides at: [Link]
74
70
Resources & More…
Good Reads:
- How to find time complexity of an algorithm?
- 8 time complexities that every programmer should know
- Understanding Time Complexity with Simple Examples
- Analysis of Algorithms | Set 3 (Asymptotic Notations)
- Analysis of Algorithms | Set 4 (Analysis of Loops)
- Practice Questions on Time Complexity Analysis
75
71
Thank You & Stay Safe
:)