
CSC 385 - Data Structures and Algorithms
University of Illinois Springfield
College of Health, Science, and Technology
rot13-encryption(message, key) {
encrypted = Empty String
for each character in message {
location = (character + key) % alphabet length
encrypted_character = alphabet[location]
encrypted appends encrypted_character
}
return encrypted
}
sum = 0
for i = 1 to n
sum = sum + i
sum = 0
for i = 1 to n
{
for j = 1 to i
sum = sum + 1
}
sum = n * (n + 1) / 2
Three algorithms for computing the sum \(1 + 2 + \dots + n\) for an integer \(n > 0\).
The graph of input size (n) vs time (t)
Provided by Frank M. Carrano and Timothy M. Henry
Growth rate functions from most efficient to least efficient. In other words, from slow growth to fast growth
Generated using Mathematica
\(f(n) = 10n + 5\)
\(C\) is the constant 20 and \(g(n) = n\)
Generated using Mathematica
\(f(n) = 10n + 5\)
\(C=20\) and \(g(n) = n^2\)
A lot of notes but if you understand BigO then Big\(\Omega\) and \(\Theta\) are easy.
Generated Using Mathematica
Notice that \(f(n)\) overtook \(C * g(n)\) somewher in the negative and stays that way as \(n \rightarrow \infty\)
Generated with Mathematica
It is clear to see that \(C * g(n)\) overtakes \(f(n)\)
Generated with Matematica
For \(O(n)\), the constant \(C = 20\) was used
For \(\Omega(n)\), the constant \(C = 1\) was used
You can see that \(f(n)\) will stay between \(O(n)\) and \(\Omega(n)\) as \(n \rightarrow \infty\)
Given two algorithms that perform the same task and their BigO notation you can easily dtermine which algorithm should be faster for a given input size by simply substituting the input size in place of \(n\).
We are concerned not with the actual running time but with the rate at which the running time or space consumption increases as the value for \(n\) increases.



This graph indicates that when \(n = 9\) we are above the 350,000 steps to complete the algorithm. \(9! = 362880\)
\(\Theta(1) \le log(log n) \le log n \le log^2 n \le n \le n log n \le n^2 \le n^3 \le 2^n \le n!\)
Typical growth-rate functions evaluated at increasing values of n
| Precise | Highest order term |
|---|---|
| \(8n^2 + 7\) | \(O(n^2)\) |
| \(8 log n + 5n^3 + 100n^2 + 7\) | \(O(n^3)\) |
| \(5 n log n + n\) | \(O(n log n)\) |
| \(3^n + 6n^4\) | \(O(3^n)\) |
| \(n^{\frac{1}{2}} + 6n^2 log n\) | \(O(n^2 log n)\) |
| \(1000n + \frac{n^2}{log n}\) | \(O(\frac{n^2}{log n})\) |
Based on rule 2, the loop iterates N times so it is of \(O(n)\)
Question: What is the actual precise number of total statement executions in this loop?
Sum all statements to get \(1 + n + n + 1 + n = 3n + 2\) which has magnitude \(O(n)\)
Both loops iterate from 0 to \(N - 1\); i.e., each loop is \(O(N)\). If we apply the rule of multiplying the BigO values of nested loops, we find that overall the nested loops should be \(O(N * N)\), or \(O(N^2)\). That means the statements inside the inner loop are executed \(O(N^2)\) times.
Easy way to see what is going on is to write the values for which \(j\) executes. Suppose N = 5.
| i-value/iteration | j-values/iteration | total executions of j-loop |
|---|---|---|
| 0 | 0, 1, 2, 3, 4 | 5 |
| 1 | 1, 2, 3, 4 | 4 |
| 2 | 2, 3, 4 | 3 |
| 3 | 3, 4 | 2 |
| 4 | 4 | 1 |
To get the total number of statements we have \(5 + 4 + 3 + 2 + 1 = 15\)
Suppose now \(N = 6\). You would see that you would get \(6 + 5 + \dots + 1\) and so on. This is just
\[ \sum_{i=1}^{N} i = \frac{N * (N + 1)}{2} = \frac{N ^ 2 + N}{2} \]
This just becomes \(O(n^2)\) at the end of the day.
for(int i = 0; i < N; i++) {
for(int j = i; j < N; j++) {
for(int k = j; k < N; k++) {
//statements go here
}
}
}You can take the same approach as the two nested dependent loops but identifying the appropriate function is a little more difficult.
The result is \(\frac{n ^ 3 + 3n^2 + 2n}{6}\) which is just \(O(n^3)\)
Computing the exact number of times that an statement is executed inside nested loops with dependent index variables might be difficult. However, computing the BigO is easy, we just apply rule 3.
The outermost loop defines the starting position, i, of the subsequence currently being considered. The starting position can be any integer in the sequence, hence the outermost loop goes from the first integer to the last integer.
The middle loop defines the ending position, j, of the subsequence. The ending position can be any position to the right of the starting position
The innermost loop, k, simply goes from the starting position to the ending position and computes the sum of the numbers in the current subsequence.
public int mcsAlgo1(int nums[]) {
int maxSum = 0; // 1 - time
for(int i = 0; i < nums.length; i++) {
for(int j = i; j < nums.length; j++) {
int currentSum = 0; //N (N + 1) / 2 times
for(int k = i; k <= j; k++) {
currentSum += nums[k]; //(N^3 + 3N^2 + 2N) / 6 times
}
if(currentSum > maxSum) { //N (N + 1) / 2 times
maxSum = currentSum; //N Times
}
}
}
return maxSum; //1 time
}Sum becomes \[ \begin{gather*} 1 + \frac{N (N + 1)}{2} + \frac{N ^ 3 + 3N^2 + 2N}{6} + \frac{N (N + 1)}{2} + N + 1 \\ = \frac{1}{6} (N^3 + 3N^2 + 2N) + N(N + 1) + N + 2 \\ \end{gather*} \]
This reduces to \(O(N^3)\)
public int mcsAlgo2(int nums[]) {
int maxSum = 0; // 1 time
for(int i = 0; i < nums.length; i++) {
int currentSum = 0; // N times
for(int j = i; j < nums.length; j++) {
currentSum += nums[j]; // N (N + 1) / 2 times
if(currentSum > maxSum) { // N (N + 1) / 2 times
maxSum = currentSum; // N times
}
}
}
return maxSum; // 1 time
}Sum becomes \[ \begin{gather*} 1 + N + \frac{N * (N + 1)}{2} + \frac{N * (N + 1)}{2} + N + 1 \\ = N * (N + 1) + 2N + 2 \\ = N^2 + N + 2N + 2 \end{gather*} \]
This reduces to \(O(N^2)\)
public int mcsAlgo3(int nums[]) {
int maxSum = 0; //1 time
int currentSum = 0; // 1 time
for(int i = 0, j = 0; j < nums.length; j++) {
currentSum += nums[j]; //N times
if(currentSum > maxSum) { //N times
maxSum = currentSum; //N times
} else if(currentSum < 0) { //0 times
i = j + 1; // 0 times
currentSum = 0;// 0 times
}
}
return maxSum;//1 time
}Sum
\[ \begin{gather*} 1 + 1 + N + N + N \\ = 3N + 2 \end{gather*} \]
Which reduces to \(O(N)\)
\[ \begin{align*} \frac{1}{6} (N^3 + 3N^2 + 2N) + N(N + 1) + N + 2 &< 3N + 2 \\ \frac{1}{6} (N^3 + 3N^2 + 2N) + N(N + 1) - 2N &< 0 \\ (N^3 + 3N^2 + 2N) + 6N(N + 1) - 12N &< 0 \\ N^3 + 3N^2 + 2N + 6N^2 + 6N - 12N &< 0 \\ N^3 + 9N^2 - 4N &< 0 \\ N (N^2 + 9N + 4) &< 0 \\ \end{align*} \]
This works for values \(N < -9.424\) and \(0 < N < 0.424\) Since we only care about positive integers, and when \(N = 1\), Algorithm 3 is still more efficient, there are no inputs which will make Algorithm 1 more efficient than algorithm 3.
Consider two algorithms A and B. Algorithm A requires \(10N^2\) time and algorithm B requires \(1000N\) time. Answer the following.
\[ \begin{align*} 10N^2 &> 1000N \\ 10N^2 - 1000N &> 0 \\ 10N (N - 100) &> 0 \end{align*} \]
This indicates for all \(N > 100\), algorithm A performs worse than algorithm B
\[ \begin{align*} 10N^2 &< 1000N \\ 10N^2 - 1000N &< 0 \\ 10N (N - 100) &< 0 \end{align*} \]
This indicates for all \(N < 100\), algorithm A performs better than algorithm B
\[ \begin{align*} 10N^2 &= 1000N \\ 10N^2 - 1000N &= 0 \\ 10N (N - 100) &= 0 \end{align*} \]
This indicates for all \(N = 100\), algorithm A performs the same as algorithm B
Now take a break. You deserve it!

CSC 385 - Data Structures and Algorithms