Up to now, our primary focus has been on writing correct code. Next, we'll analyze our algorithms to determine the amount of time that they take to run, relative to the size of the input. In up coming lectures, it'll apply what you learned in this one, to analyze algorithms and compare them in order to determine which one to use. To analyze our algorithms, we're not going to measure them by timing them. Instead, we're going to read the code and look at the number of steps that they'll take for a particular input size. For example, in this lecture, we're going to write several functions that print integers, and we're going to focus on how many times the print function is called in each of our functions. The first function that we'll analyze is print ints. This function prints the integers from 1 up to n inclusive. Let's call this function. We'll run the module and call print_ints with an argument of ten. In this case, the print function is called ten times. That is, the for loop iterates ten times. What if this function were called with the argument 20? In that case, the for loop would iterate 20 times, and he print function would be called 20 times. And what about 40? There would be 40 iterations of the loop. Let's plot this so that we have a visual representation of the run time. On one axis, we'll have n, the input to the function. And on the other axis, we'll have the number of steps that the function will execute for a given n. For this problem, the steps that we're measuring are the print function calls. Notice that the number of steps is proportional to the size of n. Next, let's consider another function. Print odd ints, prints the odd integers from one to n inclusive. We'll call this function for the argument 10 as well. Print was only count five times. If the argument were 20, it would be called ten times. And if the argument were 40, it would be called 20 times. For the first function, the number of print function calls was equal to n. For this one, it's roughly half of n depending on whether n is odd or even. [UNKNOWN] this as well. We can see that the number of steps still proportional to the size of n, it's roughly half of n. This function and the first are both considered linear functions. The run time grows linearly with respect to the size of the input n. Next up, we'll analyze function print pairs. It prints all combinations of pairs of integers from 1 to n, inclusive. We're going to start by calling this function a couple of times. First, we'll call print pairs with an argument 2. In that case, four pairs are printed. We'll call it again with the argument 3. In this case, we've got 9 pairs being printed. Now, with the argument 4, there are 16 pairs printed. Do you see a pattern? For the argument n, the print function is called n squared times. When analyzing algorithms, we don't always have the luxury of running code. Sometimes, we do analysis before writing code. To determine whether it's even worth writing, we'll analyze this function by reading the code, and the same could be done with pseudo code. The print function call is inside the inner loop of a nested loop. When the inner loop is executed, it will iterate n times. And the inner loop is executed once for each iteration of the outer loop. The outer loop will execute n times as well. So with n iterations of the outer loop times n iterations of the inner loop, print is called n squared times. This time, the number of steps is proportional to the size of the input squared. We'll plot this as well. As n grows, the number of steps is growing more quickly than it did for the other 2 algorithms. If we had 2 algorithms that solved the same problem and one had linear run time while the other had quadratic run time, we'd want to use the one with linear run time if we were trying to pick the most efficient algorithm. Let's consider one more function. This function, print double step, also prints the integers from 1 to n inclusive. But rather than using a step size of one or two, like the previous functions, this step size varies. The step size is the difference between a pair of numbers in the sequence. The initial step size would be 1. The next step would be 2, then 4, 8, 16, 32 and so on. Let's consider how many integers are printed for various values of n. Let's start with n equal to 4. In that case, three integers would be printed. The integers 1, 2 and 4. How about when n refers to 5? In that case, there would still only be three integers printed, the ints 1, 2, and 4. The same goes for when n refers to 6 and 7. It's not until n refers to 8 that that we then have a fourth integer printed. When n refers to the values 8 through 15, there are still only four ints printed. It's only when n refers to 16 that there would be a fifth int printed. And then, when n refers to 32, twice that, six would be printed. When it's twice that, 64, seven would be printed. Every time n is doubled, an additional integer is printed. For the previous function, we said the number of steps is equal to n squared. For this function, the number of steps is proportional to log base 2 of m. This algorithm is logarithmic. As the side of n grows, the number of steps grows more slowly than it did for the linear and quadratic algorithms.