CS 110 Fall 2010
Lab 10: Recursion
and Performance
Due: Wednesday, November 17th by class time
Today, we'll practice recursive problem solving and compare to iterative solutions. Then we’ll start thinking about our final projects.
Part 1 - Timing of Recursive versus Iterative Solutions
You
may, if you wish, work in the "pairs programming" style for Part 1 of
this assignment. If you do, then make sure that both of you get
approximately equal time in the driving versus over-the-shoulder
coaching type roles.
The goal of this part is to
write functions for solving each of the following problems and then to
test these functions on problems of increasing size. In order to test
the functions, we need to have functions for getting the time just
before the test and then getting the time just after the test. We can
then subtract and see how long the test takes.
We first need a way to create a list of random
numbers, where the size of the list is given by one of the
parameters. We can use an external library/module called random and use the following:
import random
def genlist(numelements, maxvalue):
alist = []
while numelements > 0:
val = random.randint(0, maxvalue)
alist.append(val)
numelements -= 1
return alist
We can also use an external module called time to retrieve the current time. When we invoke the time()
function in this module, we are given a float value whose units are the
number of seconds elapsed since some reference point in the past.
So if we call this function both before and after we perform some
computation, we can take the difference of the 'after' time from the
'before' time to get the number of seconds the computation took.
Since computers execute at very fast speeds, these numbers will be very
small, and well well be displayed in scientific notation, but their
values are significant.
Suppose, for example, that I have a function called empty(n)
that simply executes a for loop with a useless assignment statement in
its body for n iterations:
def empty(n):
for i in range(n):
x = 10 * i
I could then use the following code to determine and print the time it takes to execute empty for different values of n:
def main():
start1 = time.time()
empty(10000)
end1 = time.time()
delta1 = end1 - start1
print ('Execution of empty(10000) took', delta1, 'sec.')
start2 = time.time()
empty(100000)
end2 = time.time()
delta2 = end2 - start2
print ('Execution of empty(100000) took', delta2, 'sec.')
main()
This lab will investigate the following four algorithmic objectives and the performance of different
algorithms to obtain the same result as we change the "size" of the
problem. Part of the deliverables will be comparison graphs in which the
size of the problem is plotted on the x-axis and the execution time is
plotted on the y-axis for different algorithms used to compute the same objective result. You will need one or more additional functions that allow you to retrieve requested input sizes and user selectable function to execute in order to carry out the necessary experimentation. (Think of these as generalization to the functionality I am carrying out in the example main() given above.) Be sure and include good documentation of all of your functions.
A. Find the minimum value in a list.
Given randomly generated lists of integers, the goal is to to find the
minimum value in the given list. We will define three different
functions to find the min:
def min1(alist): This function should use a
loop to find the smallest element in the list. The function returns the found minimum value from the list.
def min2(alist): This function should use
recursion and list slicing to find the smallest element in the list. The function returns the found minimum value from the list.
def min3(alist, start): This function should use
recursion to find the smallest element in the list. However, recursive calls should always pass the
original list, and the
start
parameter should be used to determine the starting position in the
sublist being considered by the recursive call. The function returns
the found minimum value from the list.
For this objective, the "size" of the problem is really the length of
the list. Additional questions to answer (in Analysis.rtf):
- How large a problem can min2() solve?
- How large a problem can min3() solve?
- Can we determine a limit on the size problem solved by min1()?
- Why do you suppose the answers to questions 1 to 3 are what they are?
- Compare the performance (execution time) of min1() versus
min2() in a graph as the size of the problem grows. Explain any
differences you see.
- Compare the performance (execution time) of min2() versus
min3() in a graph as the size of the problem grows. Explain any
differences you see.
B. Search for a specific value in a list.
Given randomly generated lists of integers, the goal is to to find a
specific given value in the given list. Analagous to min(), we
will define three different functions to find a value:
def find1(alist, key): This function should use a
loop to attempt to find
key in
alist. The function should return the index of the list where
key is found, or return -1 if
key is not found in the list.
def find2(alist, key): This function should use r
ecursion and list slicing to search for key in alist. The function returns
key if
key is found and returns
None if
key is not found.
def find3(alist, key, start): This function should use
recursion to find
key in
alist, and as above, recursive calls should pass the original list, and only change the
start position defining the sublist under consideration by the recursive call. The function should return
the index in the original
alist where
key is found, or return -1 if
key is not found in the list.
Again for this objective, the "size" of the problem is the length of
the list. Before you start the experimentation phase, hypothesize
whether any of the answers to questions 1-6 from above will change for
this objective. Then experiment and answer the same questions.
C. Compute the factorial function.
Given an input integer,
n, compute
n factorial, returning the computed value. We will define two versions of this function:
def fact1(n): This function should use a
loop to compute n!, returning the computed value.
def fact2(n): This function should use
recursion to compute n!, returning the computed value.
Answer the following questions through experimentation:
- How large a problem can the recursive versus iterative algorithms for factorial support?
- Compare the performance of fact1() and fact2() as we increase n.
- Explain any differences in performance seen.
D. Compute the Fibonacci function.
Given an input integer, n, compute Fibonacci(n), returning the
computed value. We will define two versions of this function:
def fib1(n): This function should use a
loop to compute Fib(n), returning the computed value.
def fib2(n): This function should use
recursion to compute Fib(n), returning the computed value.
Answer the following questions through experimentation:
- How large a problem can the recursive versus iterative
algorithms for Fibonacci support? ... be careful here, as the
results may be different than what you have seen before.
- Compare the performance of fib1() and fib2().
- Explain both the differences between the two ways of
computing Fibonacci as well as any differences between your answers
here and the answers in previous problems.
Part 2 - Final Project Proposal
The final project for this course is a program of your own design. This
project should demonstrate a wide range of programming constructs and
concepts from the course, including good commenting and design (through
structure and appropriate function definitions). In addition to these features, your project will be graded for its complexity. However, be sure to build your programs incrementally, since a program must work to receive a good grade. The creativity and artistry of your final product will also be evaluated in the determination of your final grade.
Please submit a detailed proposal for your final project. This should be at least a few paragraphs. You
may want to use some high level pseudocode for how your main method
will work, and specify other methods you intend to create and what they
do. This proposal should be saved in a file called Proposal.rtf.
Projects may be worked on individually or in groups of two. Proposals
for pairs must be substantially more ambitious than individual
projects, and should clearly specify how the workload will be divided
among both students.
Make sure you include your name(s) on the project proposal, and give your project a name.
Possible projects:
Composite Photo Generator
This
program would take a base image and create a new image that is composed
of many smaller images, arranged so as to give the appearance of the
original base image. One good example of this is at the end of the other hall in Olin, the Mona-Lisa generated from dice. You
may have seen other images that are similar to this: a picture of earth
created with hundreds of small pictures of animals, a picture of
Einstein created with numerous small black and white photos of
scientists, etc. Here, you would make a
program that would take in a base photo and a set of smaller images,
and use those to create a composite representation of the base photo
using the smaller images as building blocks.
Animation
As
some of you have discovered, you can make a simple animation by
repeatedly displaying many Image/GraphWin objects (or with a single
such object that gets modified between displays). Use techniques from class to create a detailed image as in the first project, with animation. For
example, you might create a cityscape, in which the sun moves through
an arc in the sky, the sky brightens and then darkens as the sun rises
and sets, lights turn on in windows at various times in the evening,
and clouds sail across the sky, billowing, expanding, and contracting
smoothly. Alternatively you might animate a plant that grows organically and eventually flowers. Or an elaborate dog-fight between monkey pirates in space ships.
Games
A
number of games where players take turns and are not timed can be
created using the tools and techniques we have been working with. For example, you might create a battleship game. Or Connect-Four. Or Mastermind. Or a game you made up. For any of these games, you should display the board graphically after each move.
You can also make games that are not turn based. If you choose to do so, you’ll need to use the system clock (not too hard, I can help with that).
Regardless, any game you create should use methods that recognize keyboard or mouse input. The documentation should help you here, and I can also give you pointers on this if you’re stuck.
Kaleidoscope
A
kaleidoscope program would take one or more images, and automatically
use them to create a geometric collage of the sort often seen when
looking through a kaleidoscope. That is,
random fragments of each image would be thrown together in a highly
symmetric way to form a composite image which would be itself highly
symmetric, and yet be composed of many small components, each of which
would come from one of the original images.
Paint Program
Create a program that allows the user to draw on a canvas with the mouse. You
might want to allow the user to select colors from a palette, draw
lines and shapes, fill regions with color, and apply filters like the
ones you’ve created already. As with games,
if you choose to do this you should use methods from the Picture class
to recognize mouse input so that the user can, for example, simply
click on a color, and drag a circle on the screen.
Others
You
are welcome to also propose other ideas; fractal generators, a more
complex biological simulator, physical simulations, or anything else
you can think of. However, the complexity and depth of your project must be comparable those described above.
Note: While we haven’t done so in class, it is not difficult to have the computer choose random values in a given range. This
gives you additional flexibility in terms of what your programs can do,
so feel free to include randomness in your proposals.
Part 3 - Uploading your lab
Lab10 should contain:
-
Analysis.rtf
- Performance.py
- Proposal.rtf