CS150 - Fall 2013 - Class 19
admin
- CS classes next semester
- CS talk Friday at 12:30 in MBH 104
- pizza at 12:20
- extra credit for attending
- The rest of the semester
- Two labs/assignments left
- 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 exponent >= 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...