CS150 - Fall 2012 - Class 19

  • quiz!

  • admin
       - CS talk Thursday at 12:30 in MBH 104
          - pizza at 12:20
          - extra credit for attending
       - CS lunch on Friday in Ross, LaForce 121
       - The rest of the semester
          - Two labs/assignments left
          - No class next Wednesday (though labs will still be due at the "beginning" of class)
          - Second test project out next week. Due at 6pm on the last day of classes.


  • the four steps to writing a recursive function:
       1. define what the function header is
       2. define the recursive case
       3. define the base case
       4. put it all together

  • write a function called power that takes a base and an exponent and returns base^exponent (i.e. the same thing as ** without using **)
       - can assume that the exponent is >= 0

       1. define what the function header is
          def power(base, exponent)

       2. define the recursive case
          b^e = b * b^(e-1)

          - we can define the power function as the power function of the exponent - 1 times the base

       3. define the base case
          - each time the exponent is getting smaller
          - eventually, the exponent will be 0
             - b^0 = 1

       4. look at the power function in recursion.py code
          - check the base case when the exponent == 0
             - in this case just return 1
          - otherwise, do the recursive case
             - base * power(base, exponent-1)

       - how many times is power called?
          - exponent+1 times
             - power(base, exponent)
             - power(base, exponent-1)
             - power(base, exponent-2)
             - ...
             - power(base, 1)
             - power(base, 0)

       - can we do any better efficiency-wise (that is, less calls to exponent)?
       - look at fast_power function in recursion.py code
          - two different recursive cases
             - if the exponent is even
                - b^e = b^(e/2) * b^(e/2)
             - if the exponent is odd
                - our normal recursive case
                - b^e = b * b^(e-1)
          - why is this faster?
             - when it's even, instead of just decreasing by 1, we decrease by a half!

  • look at the spiral function in turtle_recursion.py code
       - for example, what would the picture look like if I called:
          >>> spiral(80, 50)
       - what does this function do?
          - recursive function
          - draws a spiral on the screen
             - forward 80
             - left 30
             - spiral( 76, 49 )
                - forward 76
                - left 30
                - spiral(72.2, 48)
                   - forward 72.2
                   - left 30
                   - ...
          - when does it stop?
             - when levels == 0
          - repeat 50 times:
             - forward length
             - left 30
             - reduce length by 5%

       - what if we wanted to end up back at the starting point, but we couldn't pick the pen up?
          - one way would be to trace our steps backward
          - Assume that the recursive call returns back to its starting point. What would we need to do to make sure that our call returned back to the starting point?
             - Add the following after the recursive call:

                right(30)
                backward(length)

          - if we run it now, we draw the spiral all the way down, and then we retrace backwards
       - why does this work?
          - each call to spiral retraces its own part after the recursive call
          - the stack keeps track of each of the recursive calls

  • run broccoli_demo function in turtle_recursion.py code
       1. define what the function header is
          - broccoli(x, y, length, angle)

       2. define the recursive case
          - broccoli is a line with three other broccolis at the end
             - one directly straight out
             - one 20 degrees to the left
             - one 20 degrees to the right
          - the three other broccolis should be smaller/shorter than the current

       3. define the base case
          - eventually the length of the broccoli to be drawn gets too short
          - at that point instead of recursing, we draw a yellow dot at the end and stop recursing

       4. put it all together
          - look at broccoli function in turtle_recursion.py code
             - draw a line in the direction specified
             - check the base case
                - if the line length is less than 10, just put a yellow dot at the end
             - otherwise, it's the recursive case
                - draw three smaller broccolis at different angles

             - what are new_x and new_y?
                - the ending coordinates of the line being drawn
             - why do we need to save them?
                - after the first recursive call to broccoli the turtle won't be in the same place

       - if we turn tracing back on, what will we see, that is, what order will it draw in?
          - going to go right (angle -20) over and over again until it gets too short
             - it will end the recursion then draw a short center
             - this also will be a base case and draw the left
          - then it will start to work it's way back up
          - eventually, it will make it back up to the top-level and start drawing the center stalk
          - after drawing the center stalk, it will draw the left stalk

       - how many lines does the program draw?
          - a lot! :)
          - how could we figure out how many?
             - change broccoli so that it returns the number of lines that it draws
          - base case:
             - the base case only consists of a single line (followed by a dot)
             - add "return 1" after drawing the dot
          - recursive case:
             - consists of 1 line plus all of the lines in the three recursive broccolis
             - save the return values from each of the recursive calls
             - return the sum of these values plus 1

                c1 = broccoli(new_x, new_y, length * 0.75, angle - 20)
              c2 = broccoli(new_x, new_y, length * 0.75, angle)
              c3 = broccoli(new_x, new_y, length * 0.75, angle + 20)
             return c1 + c2 + c3 + 1
          - 3280 lines!

  • recursion limit
       - python has a default recursion limit set
          - if the "stack" gets more entries on it than this limit, you'll get an exception:

             >>> factorial(1000)
             ...
             RuntimeError: maximum recursion depth exceeded
       - There are situations where the default recursion limit is not sufficient
          - the default is 1000
             - this is the maximum stack size and other functions may also end up on the stack so you may not actually be able to recurse this many times
          - you can increase the limit from the sys module:

             import sys

             sys.setrecursionlimit(10000)
       
          - now we can call our factorial function with larger numbers:

             >>> factorial(1000)
             4023872600770937...