Consider the following Python function.
def blast(n):
if n > 0:
print n
blast(n-1)
else:
print "Blast off!"
What is the the output from the call?
blast(5)
The following mechanism helps us understand what is happening:
What is the output of the following?
def rp1( L, i ):
if i < len(L):
print L[i],
rp1( L, i+1 )
else:
print
def rp2( L, i ):
if i < len(L):
rp2( L, i+1 )
print L[i],
else:
print
L = [ 2, 3, 5, 7, 11 ]
rp1(L,0)
rp2(L,0)
Note that the entirety of list L is not copied to the top of the stack. Instead, a reference (an alias) to L is placed on the stack.
The factorial function is
and
This is an imprecise definition because the :math:` cdots ` is not formally defined.
Writing this recursively helps to clear it up:
and
The factorial is now defined in terms of itself, but on a smaller number!
Note how this definition now has a recursive part and a non-recursive part:
We’ll add output code to the implementation to help visualize the recursive calls in a different way.
The Fibonacci sequence starts with the values 0 and 1.
Each new value in the sequence is obtained by adding the two previous values, producing
Recursively, the value, , of the sequence is defined as
This leads naturally to a recursive function, which we will complete in lecture.
Fractals are often defined using recursion. How do we draw a Sierpinski triangle like the one shown below?
Define the basic principle
Define the recursive step
def draw_sierpinski( ):
Remember the lego homework? We wanted to find a solution based on mix and match. While a non-recursive solution exists, the recursive solution is easier to formulate.
Given a list of legos and a lego we would like match:
Define the basis step(s): when should we stop?
Define the recursive step
def match_lego(legolist, lego):
Consider the following recursive version of binary search:
def binary_search_rec( L, value, low, high ):
if low == high:
return L[low] == value
mid = (low + high) / 2
if value < L[mid]:
return binary_search_rec( L, value, low, mid-1 )
else:
return binary_search_rec( L, value, mid, high )
def binary_search(L, value):
return binary_search_rec(L, value, 0, len(L)-1)
Here is an example of how this is called:
print binary_search( [ 5, 13, 15, 24, 30, 38, 40, 45], 13 )
Note that we have two functions, with binary_search acting as a “driver function” of binary_search_rec which does the real work.
Is the code right?
The fundamental idea of merge sort is recursive:
We repeat our use of the merge function from Lecture 20:
def merge(L1,L2):
i1 = 0
i2 = 0
L = []
while i1<len(L1) and i2<len(L2):
if L1[i1] < L2[i2]:
L.append(L1[i1])
i1 += 1
else:
L.append(L2[i2])
i2 += 1
L.extend(L1[i1:])
L.extend(L2[i2:])
return L
Using this, we will write the main merge_sort function in class.
def merge_sort(L):
Comparing what we write to our earlier non-recursive version of merge sort shows that the primary job of the recursion is to organize the merging process!
Attempts to define the formal foundations of mathematics at the start of the 20th century depended heavily on recursion. While ultimately completing this formalization was shown to be impossible, the work led to much of the formal basis of computer science. Here is an example of the definition of adding two non-negative integers based on just +1 and -1 operations (formally known as successor and predecessor operations) and equality tests:
def add(m,n):
if n == 0:
return m
else:
return add(m,n-1) + 1
Show the sequence of calls made by
print add(5,3)
and then show the return values resulting from each recursive call.
Following the ideas from the previous problem, write a recursive function to multiply two non-negative integers using only the add function we’ve just defined, together with +1, -1 and equality tests.
Now, define the integer power function, , in terms of the mult function you just wrote, together with +1, -1, and equality.
Euclid’s algorithm for finding the greatest common divisor is one of the oldest known algorithms. If and are positive integers, with , then let be the remainder of dividing by . If , then is the GCD of the two integers. Otherwise, the GCD of and equals the GCD of and . Here is the Python code:
def gcd(a,b):
if a < b:
a,b = b,a
r = a % b
if r==0:
return b
else:
return gcd(b,r)
What is the output of
print gcd(36,24)
print gcd(84,65)
print gcd(84,66)
Why do we know that gcd is proceeding toward the base case (as required by our “rules” of writing recursive functions)?
Specify the recursive calls and return values from our merge_sort implementation for the list
L = [ 15, 81, 32, 16, 8, 91, 12 ]