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.
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*R
2 and the Area of the square =4*R
2.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.
After completing the activity above, answer the following questions:
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?
How does the number of darts thrown affect the accuracy of the estimate? Provide evidence for your answer.
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:
Start with some simple examples that complete with a few payments, then verify your results by hand.
For dollar values, only display two digits of precision after the decimal point.
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.
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.
If you accidentally create an infinite loop, try to interrupt
or
restart
the kernel.
Use the modulus operator to separate out the years and months.
This activity comes from Lab 2: Control Flow of the Fundamentals of Computing I course at Notre Dame.
After completing the activity above, answer the following questions:
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?
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?
For this activity, you are write code that allows you to compose electronic music. To do this, you will:
Generate waveforms that correspond to music notes.
Compose a series of notes that represent a song or tune.
Listen to your music!
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:
First, we import the math
library so we can use the sin
function and
the pi
value.
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.
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
.
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
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:
You want to extend the music
list to include the corresponding
waveform data rather than appending it.
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
).
Here are some examples of music generated using this process:
After completing the activity above, answer the following questions:
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?
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.
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.
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.
To submit your notebook, follow the same directions for Notebook00, except store this notebook in the notebook03 folder.