In Class: Week 11 Tuesday and Thursday:
Recursion


Create a week11 subdirectory in your cs21/class directory by running update21:

$ update21
$ cd
$ cd cs21/class/week11

Topics


In-class work

    Recursion

  1. Open recursion.py. We are going to write some iterative and recursive versions of the same function. Iterative versions use loops, recursive versions do not use loops, and instead contain calls to themselves but on a smaller problem. The idea of recursive functions is based on recursive definitions. For example, n! can be defined as n(n-1)!.

    1. Lets try writing sum of ints together
    2. Next, try writing an iterative version (using a loop) of factorial that takes a positive int value, n, and returns the product of the first n integers.
    3. Next, try writing a recursive version of the factorial function.

  2. For any recursive problem, think of three questions:
    1. What is the base case? That is, in what instances do I know what recursion to occur?
    2. What is the recursive call? Make one or calls in the function to itself. It is very important to use different arguments in the recursive call otherwise you obtain infinite recursion.
    3. Do all possible chains of recursion lead to a base case?
  3. Next, we will work on recursive algorithms for strings and lists. Open up reverseWord.py and write a recursive function that returns the letters of a word in reverse. For example, "hello should yield "olleh".

  4. Recursive functions can have more than one different recursive call within the function. For example, let us work out how we can recursively define binary search, which recursively calls binary search on one-half of the problem. We will code our solution in recBinary.py

  5. Recursion plays in important role in mathematics and graphical representations of seemingly complex objects. We will revisit our bulls eye graphics example from early in the course to define a recursive solution to the problem in recursiveCircle.py. A more advanced solution can then be looked at in recursiveCircle2.py.

    Dynamic Programming

  6. The Fibonacci sequence is a very popular recurrence relation from mathematics. First, let us code up a Python implementation of a function that can calculate the value of any position in the Fibonacci sequence using a recursive function in fibonacci.py.

  7. Adding control flow statements, we see that the definition works on small numbers, such as f(5). But when we try f(100), the program seems to run forever. And looking at the flow, it seems to be repeating itself. This is an example were the recursive definition contains overlapping subproblems. A common solution to this is to use dynamic programming, where we keep track of all previously solved Fibonacci values. Then, we create an additional base case that checks to see if our list of solutions contains the answer already. If it does, we return it, else with proceed with the normal recurrence steps.

    Merge Sort

  8. Last week, we looked at several sorting algorithms, all of which had O(N^2) runtime. We will take a look at merge sort, a divide-and-conquer algorithm with a similar pattern to binary search in that we divide our problem in half successively into we reach small lists that are easy to sort.

  9. First, in partners, come up with an algorithm to combine two already sorted lists into one. This is similar to the merge step in merge sort.

  10. Next, let's code up our solution for merge and our recursive definition for merge sort in mergesort.py.

  11. When we analyze the run time of merge sort, we notice that on any level of the recursion, the total amount of work being down by all of the merges is O(N). That is, to combine two lists of size N/2, it takes O(N) time. Similarly, combining two N/4 lists and combining another two N/4 lists takes O(N) time in total. The next question is, how many levels are there in merge sort? Since divide-and-conquer divides the problem in half each time, we see that it takes O(lgN) recursions to reach the base case, meaning that we do O(N) work a total of O(lg N) times resulting in an O(N lg N) algorithm.