# Lecture 7 --- Order Notation & Basic Recursion - Algorithm Analysis, Formal Definition of Order Notation - Simple recursion, Visualization of recursion, Iteration vs. Recursion - “Rules” for writing recursive functions, Lots of examples ## 7.1 Algorithm Analysis Why should we bother? - We want to do better than just implementing and testing every idea we have. - We want to know why one algorithm is better than another. - We want to know the best we can do. (This is often quite hard.) How do we do it? There are several options, including: - Don’t do any analysis; just use the first algorithm you can think of that works. - Implement and time algorithms to choose the best. - Analyze algorithms by counting operations while assigning different weights to different types of operations based on how long each takes. - Analyze algorithms by assuming each operation requires the same amount of time. Count the total number of operations, and then multiply this count by the average cost of an operation. ## 7.2 Exercise: Counting Example - Suppose arr is an array of n doubles. Here is a simple fragment of code to sum of the values in the array: ```cpp double sum = 0; for (int i=0; i2), O(n3), O(nk), a.k.a. POLYNOMIAL. e.g., find closest pair of points. - O(2n), O(kn), a.k.a. EXPONENTIAL. e.g., Fibonacci, playing chess. ## 7.6 Exercise: A Slightly Harder Example Here’s an algorithm to determine if the value stored in variable x is also in an array called foo. Can you analyze it? What did you do about the if statement? What did you assume about where the value stored in x occurs in the array (if at all)? ```cpp int loc=0; bool found = false; while (!found && loc < n) { if (x == foo[loc]) found = true; else loc++; } if (found) cout << "It is there!\n"; ``` ## 7.7 Best-Case, Average-Case and Worst-Case Analysis - For a given fixed size array, we might want to know: – The fewest number of operations (best case) that might occur. – The average number of operations (average case) that will occur. – The maximum number of operations (worst case) that can occur. - The last is the most common. The first is rarely used. - On the previous algorithm, the best case is O(1), but the average case and worst case are both O(n). ## 7.8 Approaching An Analysis Problem - Decide the important variable (or variables) that determine the “size” of the problem. For arrays and other “container classes” this will generally be the number of values stored. - Decide what to count. The order notation helps us here. - If each loop iteration does a fixed (or bounded) amount of work, then we only need to count the number of loop iterations. - We might also count specific operations. For example, in the previous exercise, we could count the number of comparisons. - Do the count and use order notation to describe the result. ## 7.9 Exercise: Order Notation For each version below, give an order notation estimate of the number of operations as a function of n: 1 ```cpp int count=0; for (int i=0; i& v, unsigned int i) { if (i < v.size()) { cout << i << ": " << v[i] << endl; print_vec(v, i+1); } } ``` ```cpp void print_vec(std::vector& v) { print_vec(v, 0); } ``` What will this print when called in the following code? ```cpp int main() { std::vector a; a.push_back(3); a.push_back(5); a.push_back(11); a.push_back(17); print_vec(a); } ``` How can you change the second print vec function as little as possible so that this code prints the contents of the vector in reverse order?