Overview

The goal of this assignment is to allow you to practice utilizing iteration in the Python programming language. To record your solutions and answers, create a new Jupyter Notebook titled Notebook 03 - Iteration.ipynb and use this notebook to complete the following activities and answer the corresponding questions.

Make sure you label your activities appropriately. That is for Activity 1, have a header cell that is titled Activity 1. Likewise, use the Markdown cells to answer the questions (include the questions above the answer).

This Notebook assignment is due Midnight Thursday, September 24, 2015 and is to be done individually.

Activity 1: Estimating Pi via Monte Carlo

For this activity, you are to implement the Monte Carlo simulation of Pi.

The Monte Carlo method is a way of finding approximate solutions to problems that cannot be precisely solved. A common example of such a problem is determining the value of pi.

To compute the value of pi, we can do the following:

Let's consider the problem of estimating Pi by utilizing the Monte Carlo method. Suppose you have a circle inscribed in a square (as in the figure). The experiment simply consists of throwing darts on this figure completely at random (meaning that every point on the dartboard has an equal chance of being hit by the dart). How can we use this experiment to estimate Pi?

The answer lies discovering the relationship between the geometry of the figure and the statistical outcome of throwing the darts.

Let's first look at the geometry of the figure. Let's assume the radius of the circle is R, then the Area of the circle = Pi*R2 and the Area of the square = 4*R2.

Now if we divide the area of the circle by the area of the square we get Pi/4.

But, how do we estimate Pi by simulation? In the simulation, you keep throwing darts at random onto the dartboard. All of the darts fall within the square, but not all of them fall within the circle. Here's the key. If you throw darts completely at random, this experiment estimate the ratio of the area of the circle to the area of the square, by counting the number of darts in each.

Our study of the geometry tells us this ratio is Pi/4. So, now we can estimate Pi as

Pi = 4 x (Number of Darts in Circle) / (Number of Darts in Square)

The common approach to implementing this simulation involves computing a pair of x and y coordinates which range between [-1, 1] in each iteration and checking to see if that point is within a circle with a center at the origin and a radius of 1. If the point is within a circle, increment the counter that tracks the number of darts in a circle. This approach is shown in the image below.

Alternatively, you can generate x and y coordinates with a range of [0, 1] and check if the hypotenuse of this point with respect to the origin is less than 1. The advantage of this method is that we can simply use random.random() to compute two numbers between [0, 1] and then math.hypot(x, y) to compute the hypotenuse of this coordinate with respect to the origin and x-axis. This approach is shown in the image below.

For your experiments, throw at least 100 darts. After your simulation, display the computed value of pi using the formula above.

Questions

After completing the activity above, answer the following questions:

  1. Describe in your own words how the simulation works. What are the steps you are computing? What are you keeping track of in each iteration?

  2. How does the number of darts thrown affect the accuracy of the estimate? Provide evidence for your answer.

Activity 2: Calculating Mortgage Payments

For this activity, you are to implement a mortgage calculator that given the principal, interest rate, and desired monthly payment, the program generates an amortization table and displays how long it took to pay off the mortgage (in years and months) and the total amount of payments over that time.

In the recent housing crisis, many unsuspecting homebuyers got themselves into complicated mortgages that they couldn't afford, and perhaps didn't understand. Most mortgage companies go out of their way to hide from the buyer what the actual cost of purchasing a house is, so you should be able to calculate it for yourself.

A mortgage is quite simple. The bank loans you a certain amount of money (the principal) to purchase a house at a certain interest rate. Every month, you must make a payment to reduce the balance. In addition, the bank changes interest by computing one twelfth of the interest rate times the remaining balance. (Unfortunately, the interest gets charged before your payment is applied.)

For example, suppose that you borrow $100,000 to purchase a home at 5% yearly interest. You agree to pay $500 per month until the mortgage is paid off. In the first month, the interest increases the balance by $416.67, then your payment reduces it by $500, for a remaining balance of $99,916.67. The first payment only reduced the principal by $83.33! (This is going to take a while.)

In the second month, the interest charge is $416.31, and the remaining balance is $99832.99. And so on. If you keep computing like this, you get what is known as an amortization table that shows every payment until the mortgage is paid off:

Month     Payment    Interest      Balance
1         $500.00     $416.67   $ 99916.67
2         $500.00     $416.32   $ 99832.99
...
430       $500.00     $  3.97   $   456.94
431       $458.84     $  1.90   $     0.00

In computing this table, you should be able to report how long it would take to pay off the mortgage and what the total amount paid was:

You paid a total of $215458.84 over 35 years and 11 months.

Here are some things to consider while implementing the calculator:

  1. Start with some simple examples that complete with a few payments, then verify your results by hand.

  2. For dollar values, only display two digits of precision after the decimal point.

  3. The final payment will almost certainly be smaller than all the others, so be careful to check for that case so you don't end up with a negative balance.

  4. If the desired payment is too small, balance will go up every month! If this happens, the program should stop and display an appropriate error message.

  5. If you accidentally create an infinite loop, try to interrupt or restart the kernel.

  6. Use the modulus operator to separate out the years and months.

Fundamentals of Computing I

This activity comes from Lab 2: Control Flow of the Fundamentals of Computing I course at Notre Dame.

Questions

After completing the activity above, answer the following questions:

  1. Describe in your own words how the mortgage calculator works. What are the steps you are computing? What are you keeping track of in each iteration?

  2. If you had a mortgage with a principal of $250,000, an interest rate of 4%, and a monthly payment of $1000, how long will it take for you to pay it off? How much will you have paid in total?

Activity 3: Generating Music

For this activity, you are write code that allows you to compose electronic music. To do this, you will:

  1. Generate waveforms that correspond to music notes.

  2. Compose a series of notes that represent a song or tune.

  3. Listen to your music!

Generating Waveforms

Before composing songs, we first need to generate waveforms which are basically a series of numbers that represent the sound wave of every note in our electronic music generator. As can be seen in this table of Note Frequencies, the traditional musical pitches all have a corresponding frequency associated with them. To generate the sound wave at that pitch, we need to produce a sine wave with the appropriate frequency using the following equation:

waveform(x) = sin(2 * pi * frequency * x / framerate)

Because the sine wave is a continuous function, we sample it 44100 times per second (this is called the framerate) and store the result of the equation into a list. Therefore, each note in our machine has a corresponding waveform which consists of a list of numbers generated using a sine wave and the appropriate frequency associated with the note.

The recipe below outlines how you should generate the waveforms for this activity:

  1. First, we import the math library so we can use the sin function and the pi value.

  2. Next, we define the Framerate and the Frequencies. For the Framerate, we will use 44110 frames per second. The set of Frequenies comes from the table of Note Frequencies referenced earlier.

  3. Third, you will need to define a variable for each note in our machine. As can be seen below, C0 is set 0 because C0 is our first note and corresponds to the first frequency in our Frequencies list. The last note is B2, which is the twentieth entry in our Frequencies list.

    Notice, that each note variable follows this pattern: {Note}{Octave}, that is C0 refers to the C note in the first octave, while B2 refers to the B note in the third octave.

    For now, we are not concerned with flats and sharps, so we only need to enumerate three octaves of the notes C, D, E, F, G, A, B.

  4. Finally, we need to generate the waveforms for each note we enumerated. To do this, we will create a list called Waveforms such that Waveforms[C0] will give us the waveform that corresponds to the note C0. To produce every waveform, you will need to follow the process described below.

Once you have completed this recipe and executed it, you should have the waveforms necessary to compose electronic music.

import math                             # Import libraries

Framerate   = 44100                     # Waveform Framerate
Frequencies = (                         # Note Frequencies
    130.8, 146.8, 164.8, 174.6, 196.0, 220.0, 246.9,    # First Octave
    261.6, 293.7, 329.6, 349.2, 392.0, 440.0, 493.9,    # Second Octave
    523.3, 587.3, 659.3, 698.5, 784.0, 880.0, 987.8,    # Third Octave
)
# TODO: Define each note in three octaves as a constant
C0 = 0
...
B2 = 20

# TODO: Generate waveforms for each note
Waveforms = []

FOR-EACH NOTE:
    INITIALIZE EMPTY WAVEFORM LIST
    FOR I IN 0 TO Framerate/2:
        WAVEFORM VALUE = SIN(2 * PI * Frequencies[NOTE] * I / Framerate)
        ADD WAVEFORM VALUE TO WAVEFORM LIST
    ADD WAVEFORM LIST to Waveforms

Composing Songs

Once you have your waveforms, you can compose a song by using the following recipe:

from IPython.display import Audio       # Import Audio Generator

music = []                              # Initialize music list
notes = (                               # Sequence series of notes
  C1, D1, E1, F1, G1, A1, B1, C2
)

# TODO: Build music list with waveform data
FOR-EACH NOTE:
    ADD CORRESPONDING WAVEFORM TO MUSIC LIST

Audio(music, rate=framerate)            # Generate audio player with composed music data

The recipe above, once completed, will generate an audio player that can play a simple ascending scale.

Some hints about composing the songs:

  1. You want to extend the music list to include the corresponding waveform data rather than appending it.

  2. To simulate releasing a note, you can add a short pause between notes by inserting a short list of 0's (i.e. [0]*10).

Listening to Music

Here are some examples of music generated using this process:

  1. Scale

  2. Mary Had a Little Lamb

  3. Twinkle, Twinkle Little Star

  4. Alouette

  5. Bad Blood

  6. Seven Nation Army

Questions

After completing the activity above, answer the following questions:

  1. Using the song composition recipe above, replicate at least two of the example songs listed and then compose a song of your choosing.

    How accurate do the songs sound? Play around with the values of Framerate and the rate argument to Audio. How does modifying either of those values effect your songs?

  2. Write a program that reads a sequence of notes from the user, composes a song, and then generates an audio player for the user to listen to. Write a program that allows the user to pick one of the songs you created by displaying a menu of options, asking the user for a selection, and then generating the specified song.

    Explain how this program works, what sort of obstacles you encountered, and how you overcame them.

Clear Notebook Output

Because the Audio generated by Jupyter is huge, we ask that when you submit your Notebooks, that you clear all Notebook output by going to the top of the Notebook and selecting Cell > All Output > Clear.

This will remove any output your cells generated, which is fine because the graders can simply re-execute the cells to produce the output.

Extra Credit

For extra credit, modify the waveform generation program to include sharp and flat notes, and then compose a song that takes advantage of these additional notes.

Submission

To submit your notebook, follow the same directions for Notebook00, except store this notebook in the notebook03 folder.