[adapted from Cortina/von Ronne]
[email zipped folder with all homework contents to Avia at aweinste@andrew.cmu.edu]
Overview
For this assignment, you will create a source file (that is, a text file) for each of the problems below. You should store all of these files in a folder named pa2.
Note: You are responsible for protecting your solutions to these problems from being seen by other students either physically (e.g., by looking over your shoulder) or electronically.
In particular, since the lab machines use the Andrew File
System to share files worldwide machines, you need to be careful that you do not put files in a place that is publicly accessible.
If you are doing the assignment on the Gates Hall Cluster machines we use in lab, our recommendation is that you should place your pa2 directory under ~/private/15110. that is, the new directory pa2 is inside a directory called 15110, which is inside the directory called private, which is inside your home directory. (Every new andrew account is set up with a private subdirectory within the account's home directory that only that account can access.) Please refer to Setting up Directories for instructions on creating and managing your directories.
Exercises
1.
Prime Factorization.
Every integer greater than 1 can be expressed as the product of one or more prime numbers (which may be repeated). For example, 60=2×2×3×5 , and two, three, and five are all prime. This is called the number's prime factorization, and it is unique for each integer number of at least 2.
A number n 's prime factorization can be calculated using the following algorithm.
I.
Set dividend equal to n .
II.
Set possible_factor equal to 2.
III.
Set factors to be an empty array.
IV.
While dividend is not 1, do the following: a.
If possible_factor is a divisor of dividend :
1.
Append possible_factor onto factors .
2.
Set dividend to dividend / possible_factor
.
Otherwise, (if you did not execute the substeps 1 and 2, above):
3.
Add 1 to possible_factor .
V.
Return factors .
Hint: To determine if a number is a divisor of another number, think about using the modulo operator.
Implement this algorithm as a Ruby function called factor(n) (stored in factor.rb
). Your function should be able to be used as follows:
>> factor(60)
=> [2, 2, 3, 5]
>> factor(45)
=> [3, 3, 5]
>> factor(35)
=> [5, 7]
>> factor(13)
=> [13]
2.
Printing an ASCII-art Square.
By printing out different characters at different locations, it is possible create images. This is sometimes called ASCII art, and works best in a terminal that uses a fixed-width font. Regular shapes, such as the square shown below, are easy to create—even at different sizes—algorithmically.
XXXXX
X X
X X
X X
XXXXX
The sides of this square can be created using the following algorithm, which requires the square's size (that is, the number of columns of characters wide and lines of text heigh the square is). It assumes that size is at least three.
First, print out size copies of the character "X" on one line. Then, for the next size
−2 lines, print out one "X", print out size
−2 spaces, and then one
"X". Finally, for the last line print out size copies of the character "X" again.
Hint: You will need loops here! In fact, you will probably need a loop inside of a loop for part of your solution. If you use a loop inside of another loop, please, choose a different variable for each loop (e.g., if the outer loop uses "i", the inner loop can use "j").
Create a Ruby function make_square(size) (in make_square.rb
) that implements this algorithm. Your function should be able to be used as follow: irb(main):002:0> make_square(5)
XXXXX
X X
X X
X X
XXXXX
=> nil irb(main):003:0> make_square(8)
XXXXXXXX
X X
X X
X X
X X
X X
X X
XXXXXXXX
=> nil
3.
The Babylonian Method of Finding Square Roots.
The square root of a number can be approximated to an arbitrary level of precision by an algorithm known as the Babylonian Method. It is thought that the Babylonians may have used this algorithm to calculate square roots, but its first explicit description is found in Hero of Alexandria's work from the 1st century A.D. a.
To calculate an approximation of the square root of a positive integer n , the following version of the Babylonian method can be used. The result will be accurate to a precision of within
1 / 1000
.
I.
Set guess equal to n.
II.
Set precision equal to
1 / 1000
.
III.
Set error equal to guess
− ( n / guess )
IV.
While error > precision , do the following: a.
Set guess equal to the average of guess and n / guess
. b.
Set error equal to guess
– ( n / guess ).
V.
Return guess as the approximation of the square root.
Define a Ruby function square_root(n) in square_root.rb
that uses this algorithm to compute an approximation of √n . (Note: you must use this algorithm, and not, for example, Math.sqrt
.)
Your function should be able to be used as follows: irb(main):002:0> for i in 1..16 do irb(main):003:1* print square_root(i) irb(main):004:1> print "\n" irb(main):005:1> end
1
1.41421568627451
1.73214285714286
2.00000009292229
2.23606889564336
2.44949437160697
2.64576704419029
2.82846857188015
3.00009155413138
3.16245562280389
3.31693893473046
3.4641016533503
3.6055513629176
3.74165756905871
3.87298369800872
4.00000063669294
=> 1..16 b.
Define a Ruby function square_root2(n,precision) in square_root2.rb
that uses the Babylonian method to compute an approximation of √n that is within precision of the true √n . You may assume that n ×10
−13
≤ precision <1 , and thus, the limitations of Ruby's floating point numbers will not prevent the Babylonian method from from achieving the desired accuracy.
4.
Textual Calendar.
One often needs to consult a calendar to know what day of the week a certain date will fall on (or conversely, what date will be on a particular day of the week). On unix.andrew.cmu.edu, there is a handy program called cal that will display a textual calendar for the current month. (Open a terminal or ssh in, and run cal to see what it looks like.)
In February 2011, for example, the output of cal looked something like this:
February 2011
Su Mo Tu We Th Fr Sa
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28
The output consists of a header line, followed by a table that shows the dates in
February arranged into "columns" that represent the different days of the week.
Note that each "column" consists of three fixed-width characters (including spaces). Note also that the table of dates for a month is completely determined by the number of days in that month (in this case, 28) and the day of the week of the first day of the month (in this case, Tuesday).
The following algorithm can be used to construct a table showing the days of the month. It requires two integer parameters, the number of days in the month n and the day of the week of the first day of the month d . The day of the week d is encoded using 0 for Sunday, 1 for Monday, 2 for Tuesday, 3 for
Wednesday, etc.
I.
Set week_days to an array containing the strings "Su", "Mo", "Tu",
"We", "Th", "Fr", and "Sa".
II.
Iterate over week_days , and print each out in its own "column." Then print a newline character.
III.
Skip d "columns".
IV.
For each i in 1 to n do the following: a) Print i (with extra spaces to fill the "column"). b) If you just printed out the 7th "column", print a newline to move to the next line.
V.
Print a newline.
Hint for step IVb: Notice that for any calendar, when adding its d to any value in the 7th "column," the sum is always divisible by 7. For example, in the
February calendar above, where d = 2 (for Tuesday), 2+5, 2+12, 2+19, and
2+26 are all divisible by 7.
Define a Ruby function calendar(n, d) in calendar.rb
that uses this algorithm to print a table showing the days of a month with n days and whose first day starts on the day of the week encoded by the integer d .
Your function should be able to be used as follows: irb(main):004:0> calendar(31,6)
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
=> nil irb(main):005:0> calendar(29,3)
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29
=> nil
5.
Suppose you want to find out how many scores in a list are above the average score of that list. This should be done in two parts. First, develop a function to compute the average, and then develop a second function that that counts the number of scores above the average using the average value computed in the first part.
a.
Write a Ruby function called score_avg(x) (in above_avg.rb
), which takes a list (array) of numerical scores ( x
0
… x n
−1
) as its parameter and returns their arithmetic mean,
1 𝑥̅ = 𝑛 𝑛−1
∑ 𝑥 𝑖 𝑖=0
This should be done in two steps. First, iterate over the elements of the list, computing the sum of those elements. Then divide that sum by the length of the list, and return the quotient of that division operation. b.
Write a Ruby function called num_above_avg(scores) (in above_avg.rb
).
It should take a list of numerical scores as its parameter and return a count of the number of scores that are larger than the arithmetic mean.
This should be done as follows: Call the function score_avg that you wrote for part (a) to compute the average of the scores in scores . Then, initialize a variable count to zero. Then, iterate over the scores in scores , and increment count for each score that is greater than the average.
Finally, return the final value of count .
6.
Write a Ruby function called std_dev(x) (in std_dev.rb
), which takes a list of numerical scores as its parameter and returns their standard deviation,
𝜎 = √
1 𝑛 𝑛−1
∑(𝑥 𝑖 𝑖=0
− 𝑥̅) 2
You should use the following algorithm for accomplishing this calculation.
I.
Set avg to the average of array x .
II.
Set sum_of_squares to zero.
III.
Iterate over the scores in array x , for each score xi , do the following: i.
Add the square of xi - avg to sum_of_squares .
IV.
Calculate the quotient of sum_of_squares divided by the length of the array.
V.
Return the square root of the quotient computed in the previous step.
7.
Sometimes, in addition to knowing the average (mean) of a list, it is also useful to know the "middle" value (median) of that list. Define a Ruby function median(list) (in median.rb
) that takes a parameter containing a list of values and returns the median value of that list.
This should be done using the following algorithm:
I.
Set sorted_list to the list containing the elements of list rearranged in sorted order.
II.
Set n to the length of list .
III.
If n is odd, then return the element at position ( n
−1) / 2 in sorted_list .
Otherwise (that is, when n is even), return the average of the elements at positions
( n / 2)
−1 and n / 2
in sorted_list .
You may use Ruby's built-in array sort method to sort the list in step I.
8. Pascal's Triangle a.
Factorial.
The factorial of n , written n !
is 1×2×…× n . Its computation is very similar to that of the sum of the first n non-negative integers, ∑ 𝑛 𝑖=0 𝑖 .
Recall, that this sum can be computed using the following Ruby function: def sum(n)
x = 0
for i in 1..n do
x = x + i
end
return x end
In factorial.rb
, define a function factorial(n) that computes n !
for any nonnegative integer n . It should follow the pattern of the sum function above. (Hint: think about how factorial(0) and sum(0) differ and how the additional computation being performed for each larger n differs.)
Example:
>> factorial(4)
=> 24
>> factorial(0)
=> 1 b.
The Triangle.
The first n rows of a slightly rotated version of Pascal's Triangle can be computed using the following algorithm:
I.
For each integer i from 0 through n
−1 , inclusive, do the following:
A.
For each integer j from 0 through i , inclusive, do the following: 𝑖!
1.
Set val to be 𝑗!∗(𝑖−𝑗)!
.
2.
Print enough spaces to align the value stored in val .
3.
Print val .
B.
Print a new line character ("\n").
II.
Return nil .
In pascal.rb
, define a function pascal(n) that implements this algorithm to produce n rows of Pascal's Triangle in the format shown in the example below.
You will need to figure out how to accomplish step I.A.2 so that it right aligns each column; you can assume that the largest value in the triangle is less than
1000. You should make calls to your factorial function from part (a) to get the values of i !
, j !
, and ( i
− j )!
.
Example:
>> load "factorial.rb"
=> true
>> load "pascal.rb"
=> true
>> pascal(10)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
=> nil
9. Number of Occurrences.
In the file num_occurrences.rb
, define a Ruby function num_occurrences(list, key) that takes an array list and an object key as parameters. It should return the number of elements in list that are equal to key .
I.
Set count to 0.
II.For each element x in list , do the following:
A.If x is equal to key , do the following:
1.Add 1 to count .
III.Return count .
Example:
>> load "num_occurrences.rb"
=> true
>> num_occurrences(["a", "b", "a", "b", "c", "b", "d", "e"], "b")
=> 3
10. Index of Maximum Element.
In max_index.rb
, define a Ruby function max_index(list) that returns the index of the largest element in list . If the largest element occurs more than once in list , max_index should return the index of the first occurrence.
Use the following algorithm:
I.
Set i to 0.
II.
Set max_so_far to the element at index i in list .
III.
Set max_so_far_index to i .
IV.
While i is less than the length of list , do the following:
A.
Set x to the element at index i in list .
B.
If x is greater than max_so_far , then do the following:
1.
Set max_so_far to x
2.
Set max_so_far_index to i
C.
Add 1 to i .
V.
Return max_so_far_index .
Example:
>> load "max_index.rb"
=> true
>> max_index([3, 5, -10, 7, 6])
=> 3
>> max_index(["z", "x", "y", "z", "x"])
=> 0
Submisions
You should now have a pa2 directory that contains the files factor.rb, make_square.rb, square_root.rb, square_root2.rb, calender .
rb, above_avg.rb, std_dev.rb, median.rb, factorial.rb, pascal.rb, num_occurrences.rb, max_index.rb
each containing the corresponding function
.
Zip up your directory and name it pa2. Email to aweinste@andrew.cmu.edu
and in the heading write your name and pa2
.