Lab 02

The Basics
Due by 10pm on Tuesday, September 13th

In this lab you will create programs which put to use some of the week's topics. You'll aso be introduced to some useful new constructs. By the end of the lab, you will have:

As with last week's lab you should create a lab02 directory in which to store all your work for this assignment:
    % cd                 # takes you to your home directory 
    % cd cs150           # enters your 'cs150' folder
    % mkdir lab02        # creates lab02 folder cwd
    % cd lab02           # goes into that folder     
    % pwd                # confirms you are in the appropriate directory

For future labs, the above steps won't be stated explicitly, but in general you should create the appropriate folder at the start of each new lab.

Also, don't forget that you've already done a lot of non-programming work on your prelab.

On this lab, one of the parts (Part 2 - Monte Carlo) allows you to work with a partner. This is optional -- you're free to work on your own if you'd prefer. If you choose to work with a partner, you must work together on all of the components of that section - you cannot split up the tasks and each do half. If you opt to work with a partner, only one partner should submit the files with both of your names on top. Both partners should indicate (in their lab02 README files) both the name of their partner, and which partner submitted the solution.

Part 1 - Loopin'

beer.py: 2 points
sequence.py: 2 points
fibonacci.py: 4 points
interest.py: 4 points

In this portion of the lab you'll be building a bunch of short programs that utilize for-loops.


Beer!

We imagine you are all familiar with the song 'Ninety-Nine Bottles of Beer on the Wall', but just in case, the first verse is

99 bottles of beer on the wall
99 bottles of beer!
Take one down, pass it around
98 bottles of beer on the wall!

Define the Problem:

Write a program called beer.py that prints out all 99 verses of the song.

Input: None.

Output: All verses of the beer song to the terminal.

Understand the Problem:

You should thinkg about the first few verses of the song. The first verse is given above; what changes between the first and the second verse? Between the second and the third? What does the last verse look like?

Design the Algorithm:

Now it is time to think precisely about how you will solve this problem. Your solution should not copy and paste hundreds of lines of print statements. Instead, create a much shorter program by taking advantage of the power of the for-loop. Again, don't worry about grammar.

Implement a Design:

Once you think you have an algorithm written down in pseudocode, start writing your actual program in Python.

Use a constant NUM_BOTTLES to represent the number of bottles and use this in your for-loop. A constant is just a variable whose value never changes throughout the course of the program. Although this is not enforced in Python (it won't stop you from changing a contant's value), other languages do, and it is a useful practice regardless. By convention, we use ALL CAPS for the names of constants, so that you can recognize them easliy. In general, using constants makes your programs easier to maintain, and helps to prevent bugs by reminding you not to change values you don't intend to change. As an added benefit, they typically make your code more readable.

Test the Program:

Once your program works with 99 bottles, try changing your constant to other values (for example: 10 bottles, 0 bottles, 1 bottle, etc.) and make sure that it works.

Handin:

It is a good habit to handin your files every time you finish a program, to make sure that we have up-to-date copies (in case something happens to your files, or you forget to handin at the end). So take a minute now to run the handin command (instructions are at the bottom of every lab), then continue with the next portion of the lab. You can hand in as many times as you want; each submission is distinct and time-stamped, and the graders will grade the latest one available.


Sequence

Define the Problem:

Write a program called sequence.py that lists the squares of all integers from some start number down to one.

Input: A number start from the user.

Output: A list of squares starting from start2 down to 12.

Understand the Problem:


For example, if start is 6 then the output of the program would be
   Squares from 36 down to 1:
   36, 25, 16, 9, 4, 1

Be sure to use the formatting given above, with a comma and a space after each number except for the final 1. (If you need a refresher in how to format your print statements nicely, go back to lab 01.)

Design an Algorithm:

Think about the main steps of the program. Presumably step 1 is to get the input from the user. Then you have to figure out how to print the squares in the right order to the console, with the correct formatting. Write out the pseudocode before you touch the keyboard!

Implement a Design:

Now that you have a promising algorithm, it is time to put it into Python code. Go ahead and write up that program.

Test the Program:

If your syntax is correct, it is time to run your program with a variety of inputs. Try n=6 (because you know what the answer should look like), but also try other values of n, such as n=0, 1, 10, etc.

Handin:

It is a good habit to handin your files every time you finish a program, to make sure that we have up-to-date copies (in case something happens to your files, or you forget to handin at the end). So take a minute now to run the handin command (instructions are at the bottom of every lab), then continue with the next portion of the lab. You can hand in as many times as you want; each submission is distinct and time-stamped, and the graders will grade the latest one available.


Fibonacci

The Fibonacci numbers are a sequence of integers defined by the following recurrence relation

Describe the Problem:

Write a program called fibonacci.py that computes the nth Fibonacci number.

Input: A positive integer n > 2 from the user.

Output: The n-th Fibonacci number.

Understand the Problem:

For example, the first six Fibonacci numbers are 1, 1, 2, 3, 5, 8. A run of the program might appear as follows

   My incredible Fibonacci number generator!
   
   Please enter an integer: 8
   The 8th number in the Fibonacci sequence is 21.

Design an Algorithm:

Write down pseudocode describing how you would solve this problem. Try to use enough detail so that it ressembles code, but not so much jargon that it is Python-specific and unintelligible to non-coders.

A few suggestions:

Implement a Design:

Translate your pseudocode into a Python program named fibonacci.py. Although your final program should only print the requested Fibonacci number, you may want to temporarily include print statements within your loop so you can make sure the program is doing what you intended.

Test the Program:

Try running your program with a variety of inputs. Try n=8 (because you know the answer) and n=14 (you did this on your prelab). Also try n=0,1,2, etc. and check that what it returns makes sense.

Handin:

Just another reminder to run the handin command to hand in your files.


Interesting

In light of the recent economic meltdown, you've decided to take a more conservative approach with your financial portolio; as of today, you're getting rid of all your sub-prime morgage securities, all your toxic assets (what were you thinking?), and your considerable stockpile of pork bellies.

To play it safe, you're going to put your money into a savings account. This account earns a fixed percentage of interest every month. Assuming you make reguar, monthly deposits as well, you'd like to know how much you'll have after a given number of month, assuming your monthly contribution occurs after interest is accrued.

Describe the Problem:

Write a program called interest.py with the following functionality.

Input: Get an initial deposit, a monthly interest rate, a monthly deposit, and a number of months n from the user.

Output: The total money in your possession at the end of n months.

Understand the Problem:

For example, suppose your initial savings is $100, the monthly interest rate is 1%, and you plan on contributing $20 a month. Then during the first month, you'd get $1 of interest (100 * 0.01) followed by another $20 from your regular contribution. So you'd end the first month with $121. On the second month, your interest would be $1.21 (i.e., 121 * 0.01), which, together with you fixed $20, leaves you with $142.21.

After the third month, you'd have $163.63. Technically, the amount you'd have on month 3 would be $163.6321, but most accounts don't keep track of fractions of pennies (as fans of the movie Office Space may know), so you'll want to cut off anything below 2 decimal places.

A run of the program might appear as follows.

Sample Output

   Welcome to the Interest Calculator!
   
   Enter your initial savings: 100
   Enter the monthly interest rate: 0.01
   Enter your monthly contribution: 20
   How many months would you like computed: 3
   
   Initially you put in $100
   After month 1 you would have $121
   After month 2 you would have $142.21
   After month 3 you would have $163.63

Design an Algorithm:

Write pseudocode that solves this problem. As with the previous problem, give enough detail to be using loops and variables, use Python-like indentation, but try to avoid Python-specific syntax.

Implement a Design:

Translate your pseudocode into a Python program named interest.py.

As pointed out above, you'll need to truncate your output to 2 decimal places. One clever way to do this: multiply your value by 100 (now your number represents cents), cast it as an int (now you've tossed out any fraction of a cent that might have been there), and then divide by 100 (to get back to dollars again).

Test the Program:

Run your program on a variety of inputs. You should certainly try the example given above, up to 5 months (you calculated this on your prelab).

Note: If your answers are off by a cent here or there, don't worry, it is most likely just a rounding error.

Handin:

Just another reminder to run the handin command to hand in your files.


Part 2 - Monte Carlo!

monte.py: 10 points, partners allowed

You probably remember bumping into that peculiar number π = 3.14159265..., right? It comes up when you're talking about circles and trigonometry, but also appears in a bunch unexpected places that seem to have little to do with either. As a refresher, Pi can be defined as the ratio of a circle's circumference (Π d) to its diameter (d). One interesting feature of Pi is that it's an irrational number, meaning it cannot be expressed as a fraction m/n where both m and n are integers; consequently, its decimal representation never ends or even repeats. In fact, we can make a stronger statement about Pi: it is a transcendental number, which means that there is no polynomial with rational coefficients of which Pi is a root. But, I digress.

Since ancient times, mathematicians have been fascinated with the study of Pi and it's various properties. Early approximations of Pi, such as 22/7 and 355/113 were accurate to 3 and 7 digits repsectively (the latter approximation was the best known for nearly a millenium). Currently, more than the first trillion (a million million) digits are known. There are many ways to estimate Pi -- for example, you could draw as precise a circle as you can manage, measure its circumference C and diameter d, and then divide C/d; this should give you Pi. Alternatively, there is a geometry-based approach due to Archimedes. We'll investigate a third approach using what is called a Monte Carlo method.

Monte Carlo Method

When we say we're using a Monte Carlo method, we usually mean we're going to do a bunch of random trials and observe the fraction of those trials that have a certain property. In our case, we're going to be throwing darts into a square region, and computing the fraction of those darts that land within a circle inscribed inside that square. Each throw is a trial, and the property we are concerned with is whether or not the dart landed inside the circle or not.

Describe the Problem:

Write a program called monte.py that computes an approximate value of Pi.

Input: A number of trials n from the user.

Ouput: An approximation to Pi using a Monte Carlo method with n trials.

Understand the Problem:

More precisely, we'll begin by (theoretically) constructing a target circle inscribed in a square. This is our dart board, and the target circle reaches all the way to the edge of the square. It might look something like the following:

Pi Target

Next, we'll simulate repeatedly throwing darts at random against the board above (we'll assume that our random throws alway hits the square, and are equally likely to land at any given point inside the square). We then look at the fraction of the darts that land within the circle out of all those that were thrown. I claim that if we then multiply this fraction by 4, we should have a good approximation to Pi. What sort of dark sorcery is this? Let's take a closer look.

The area of a square is the length of a side squared. Thus our square, with sides of length 2, has an area of 4. The area of a circle is Pi times the radius squared, so for a unit circle (with radius 1), the area is Pi. Therefore, the ratio of the area of our circle to the area of our square is precisely Pi/4. (That is, the circle takes up a Pi/4 portion of our dart board.)

Since each dart lands at a random location in the square, the probability that any one dart lands within the circle should be exactly the fraction of the square taken up by the circle: Pi/4. Thus, if we repeat this experiment over and over again, we'd expect roughly a Pi/4 fraction of the darts to land in the circle. By counting up the fraction that actually land in the circle in our experiments, we've gotten a probabilistic estimate for Pi/4, or a quarter of Pi. If we just multiply that estimate by 4, we've got our approximation for Pi itself. Restating our discussion as a formula, we have

That is, if you throw lots of darts and keep track of the fraction that land inside the circle, multiplying this fraction by 4 should give you an approximate value for Pi. Of course, it is only an approximation: suppose (by some cosmic fluke) that all the darts thrown land near the upper left corner, outside of the circle. Then your approximation for Pi would be 0 (a rather weak estimate). However, as the number of trials increases, the likelihood that your estimate is good increases as well.

A run of the program might appear as follows.

Sample Output


    % python3.2 monte.pi 

    This program calculates the value of Pi by
    simulating the throwing of darts onto a round
    target on a square background.

        How many darts to throw? 1
    The value of Pi after 1 iterations is 4.0
    which is off by 27.32%
    % python3.2 monte.pi 

    This program calculates the value of Pi by
    simulating the throwing of darts onto a round
    target on a square background.

        How many darts to throw? 100
    The value of Pi after 100 iterations is 2.96
    which is off by 5.78%
    % python3.2 monte.pi 

    This program calculates the value of Pi by
    simulating the throwing of darts onto a round
    target on a square background.

        How many darts to throw? 1000
    The value of Pi after 1000 iterations is 3.084
    which is off by 1.83%

Design an Algorithm:

Write pseudocode to approximate Pi via this dart-throwing method--you'll throw random darts at a 2-by-2 board, and calculate what fraction end up within the unit circle. Try this on your own, and then confirm that your pseudocode looks something like the following:

Implement a Design:

Translate your pseudocode into a Python program named monte.py.

In order to implement your pseudocode, you'll need to "throw a dart." Each dart's position is specified by an (x,y) coordinate, so to "throw" a dart, you just need to randomly generate two values for each dart throw (one for x and one for y).

Generating random numbers / Throwing random darts

Python has a module that will let you generate random numbers called random. To use it you need to:

  1. At the top of your program, add the line import random
  2. You can get a random integer between 0 and 99 (inclusive) with the following call
        rInt = random.randrange(100)    # returns an integer in [0,99]
    
    Another way to get the same result is with the following call:
        rInt = random.randint(0,99)
    
    If you'd like a random float between 0.0 and 1.0 (exclusive) you can do:
        rFloat = random.random()   # returns a float in [0.0, 1.0)
  3. If you'd like that random float to be between 0.0 and 1.0 (INclusive) you can do:
        rFloat = random.uniform(0.0, 1.0)   # returns a float in [0.0, 1.0]
  4. In order to generate a random double between -1 and 1 (which you'll do for each of the x- and y-coordinates of each dart throw), do the following.
        randX = random.uniform(-1.0, 1.0)  # randX is between -1.0 and 1.0  
    

Now that you have your dart throw, your next question should be: how do I know if I've hit the target? You'll do this by calculating the distance from the center of the circle to the dart and determining if it is within 1 unit and therefore in the circle. If you think of the circle as being centered at the origin (0,0) and generate x and y values ranging from -1 to 1, you can just use the distance formula to calculate the distance from (x,y) to the origin:

distance formula

So to find out how far a dart lands from the origin, we need a square root, which requires the math module.

Math module

You can gain access to the math module by including the statement

import math

at the beginning of your program. This will give you access to more advanced mathematical functions as well as a decent value of Pi for checking the correctness of your program later on. In particular, math.sqrt(exp) will return the square root of the expression exp, and math.pi is a constant representing the value of Pi. See the online documentation for more details, if you need them.

Now in your for loop, you can check whether the dart lands within the unit circle with the following block of code (called an if-statement, which we will be learning in class in more detail in the weeks to come):

   if (math.sqrt(x*x+y*y) <= 1) : 
      # the body of this if-statement will only be run
      # if the distance between (x,y) and the origin is at most 1
      # (that is, if your point (x,y) is inside the circle.)
      # So. What do you want to do when you hit the target?

You do not have to calculate the percentage difference between your approximation and Python's math.pi, but it might be interesting to look at.

Test the Program:

Try running your program with an increasing number of trials. Ideally, the more trials you have, the closer to the real value of Pi you get. You should expect that each run produces a slightly different value due to the fact that it is using a random number generator.

Handin:

Just another reminder to run the handin command to hand in your files.


Part 3 - Patterns, Patterns Everywhere

patternA.py: 2 points
patternB.py: 2 points
patternC.py: 2 points
patternD.py: 4 points
patternE.py: 4 points
patternN.py: 4 points

In the next batch of problems, we're going to be looking at sequences of patterns. For each problem, a few patterns are given, each of which made out of ASCII characters and is associated with an index (a number). Your task is to first determine the rules that determine how a particular pattern in the sequence relates to the corresponding number. Then you'll write a program that generates an arbitrary pattern from the given sequence.

Describe the Problem:

For each sequence of patterns X, write a program called patternX.py that generates a specified pattern in sequence X.

Input: A positive integer n that represents an index in the sequence.

Output: The n-th pattern in sequence X.

Understand the Problem:

For example, below are some indices (the numbers on the left) and their associated figures for Pattern A.

Pattern A

For your first program (patternA.py), consider the pattern shown below

  1.     1 2 3
        1 2 3
        1 2 3
        
  2.     1 2 3 4
        1 2 3 4
        1 2 3 4
        1 2 3 4
        
  3.     1 2 3 4 5
        1 2 3 4 5
        1 2 3 4 5
        1 2 3 4 5
        1 2 3 4 5
        

Design an Algorithm:

You can probably see what's happening; figure n consists of n rows, each of which includes all numbers from 1 to n. Therefore your algorithm will need to first get an integer n from the user. Then you'll loop over rows from 1 to n. And for each row, you'll use a nested loop to print the digits from 1 to n, separated by spaces. In particular, the psuedocode may be:

Similarly, your algorithm for patterns B, C, D, E and N should generate arbitrary figures from the patterns below.

Implement a Design:

Translate your pseudocode into Python programs named patternX.py (where X ranges over A, B, C, D, E, and N.)

Pattern B

  1.     1 1 1
        2 2 2
        3 3 3
        
  2.     1 1 1 1
        2 2 2 2
        3 3 3 3
        4 4 4 4
        
  3.     1 1 1 1 1
        2 2 2 2 2
        3 3 3 3 3
        4 4 4 4 4
        5 5 5 5 5
        

Pattern C

  1.     1 2 3
        2 3
        3 
        
  2.     1 2 3 4
        2 3 4
        3 4
        4
        
  3.     1 2 3 4 5
        2 3 4 5
        3 4 5 
        4 5
        5
        

Pattern D

  1.     1 
        1 2 2 
        1 2 2 3 3 3
        
  2.     1 
        1 2 2 
        1 2 2 3 3 3
        1 2 2 3 3 3 4 4 4 4
        
  3.     1 
        1 2 2 
        1 2 2 3 3 3
        1 2 2 3 3 3 4 4 4 4
        1 2 2 3 3 3 4 4 4 4 5 5 5 5 5
        

Pattern E

  1.     ***
        *
        **
        *
        ***
        
  2.     ****
        *
        *
        ***
        *
        *
        ****
        
  3.     *****
        *
        *
        *
        ****
        *
        *
        *
        *****
        

Pattern N

  1.     *  *
        ** *
        * **
        *  *
        
  2.     *   *
        **  *
        * * *
        *  **
        *   *
        
  3.     *    *
        **   *
        * *  *
        *  * *
        *   **
        *    *
        
Test the Programs:

Don't forget to test your programs on a variety of inputs!


Part 4 - Wrap Up

As with every lab, your last job prior to submission is to complete a brief write-up in a README file. Create a new README file in your lab02 folder.

In this file, write a sentence or two about what you learned in this lab. Also give an estimate of the amount of time you spent on the lab. If you have further thoughts about the lab (e.g. parts that were confusing, helpful, annoying, fun, or challenging), please let us know.

Finally, if you worked with a partner on Part 2, your README file should indicate the name of the partner, and specify which of you submitted the solution to that problem.

Handin

If you followed the Honor Code in this assignment, insert a paragraph attesting to the fact within one of your README file.

I affirm that I have adhered to the Honor Code in this assignment.

You now just need to electronically handin all your files. As a reminder

 
     % cd             # changes to your home directory
     % cd cs150       # goes to your cs150 folder
     % handin         # starts the handin program
                      # class is 150
                      # assignment is 2
                      # file/directory is lab02
     % lshand         # should show that you've handed in something

You can also specify the options to handin from the command line

 
     % cd ~/cs150     # goes to your cs150 folder
     % handin -c 150 -a 2 lab02

File Checklist


You should have submitted the following files:
   beer.py
   sequence.py
   fibonacci.py
   interest.py
   monte.py     (unless you worked with a partner, and your partner submitted)
   patternA.py
   patternB.py
   patternC.py
   patternD.py
   patternE.py
   patternN.py
   README


Last Modified: August 2016 - T. Wexler, B. Geitz, C. Taylor, A. Sharp, S. Semsioglu.