Skip to main content

Assignment 2: Algorithm Basics

Due: Tuesday, January 31 by 10:30pm

Summary: For this assignment, you will identify the parts of an algorithm, find some problems with that algorithm, and write an improved version. You will then design some of your own algorithms in Scheme.

Purposes: The purpose of this assignment is for you to practice identifying the parts of algorithms, to get some experience thinking carefully about the edge cases where an algorithm could go wrong, and to get you started thinking in Scheme.

Collaboration: You must work with your assigned partner(s) on this assignment. You may discuss this assignment with anyone, provided you credit such discussions when you submit the assignment.

Submitting: Email your answer to csc151-01-grader@grinnell.edu. The subject of your email should be [CSC 151.01] Assignment 2 and should contain your answers to all parts of the assignment. Scheme code should be in the body of the message, not in an attachment.

Warning: So that this assignment is a learning experience for everyone, we may spend class time publicly critiquing your work.

Problem 1: A Birthday Problem

Topics: algorithms, parts of algorithms, testing

In any sufficiently large group of people, the likehood that at least two people were born on the same day of the year is surprisingly high (over 50% for a group of 23 people, source: Wikipedia). While this is an interesting theoretical result, it would be nice to test it in class. The following algorithm is supposed to tell us how many students share a birthday with at least one other student in class, but it has some problems:

  1. All the students in the class line up in an arbitrary order.
  2. The instructor erases the whiteboard.
  3. We will call the student at the front of the line student A.
  4. We will call the student next in line student B.
  5. Student B announces their birthday while student A listens.
  6. Student A compares their own birthday to student B’s birthday.
  7. If both birthdays are on the same day of the year, skip to step 11. If not, continue to step 8.
  8. If student B is at the end of the line, go to step 14. Otherwise, continue to step 9.
  9. The name “student B” will now refer to the next student in line behind our current student B.
  10. Go to step 5.
  11. The instructor writes one tally on the whiteboard.
  12. Both students A and B exit the classroom.
  13. If there is at least one student left in line, go to step 3. Otherwise, continue to step 14.
  14. the number of tallies on the whiteboard is the number of students who share a birthday with at least one other student. Exit the algorithm

Part A: Understanding the birthday algorithm

Explain, in your own words, the high-level idea behind this algorithm. Ambiguity is okay: the goal of this part is to guess the intent of the algorithm designer, not the exact steps. If you believe the intent does not match the actual steps, try to summarize what you believe the algorithm designer meant to do.

Part B: Parts of the birthday algorithm

The algorithm above uses all six of the parts of an algorithm covered in the Algorithms reading. Find an example of each piece of an algorithm and list the step(s) that demonstrate that algorithmic building block. Briefly explain your choice.

i. Identify an example of sequencing in the algorithm above. Identify the step(s) that demonstrate this algorithmic building block, and explain your choice.

ii. Identify an example of repetition in the algorithm above. Identify the step(s) that demonstrate this algorithmic building block, and explain your choice.

iii. Identify an example of a conditional in the algorithm above. Identify the step(s) that demonstrate this algorithmic building block, and explain your choice.

iv Identify an example of a variable in the algorithm above. Identify the step(s) that demonstrate this algorithmic building block, and explain your choice.

v. Identify an example of a parameter in the algorithm above. Identify the step(s) that demonstrate this algorithmic building block, and explain your choice.

vi. Identify an example of a subroutine in the algorithm above. Identify the step(s) that demonstrate this algorithmic building block, and explain your choice.

Part C: Problems with the birthday algorithm

The birthday algorithm provided with this assignment has some problems. Find two problems with the algorithm and explain them below. At least one of the problems you identify should relate to the correctness of the algorithm rather than issues with clarity, efficiency, or precision. When you describe a problem with the algorithm that will cause it to produce an incorrect result, provide an example situation where you would get the wrong answer.

Part D: Fixing the birthday algorithm

Write a new birthday algorithm that will produce the right answer. Make sure to address the issues you identified in part C. You should use an approach roughly equivalent to what you summarized in part A.

Problem 2: Scoring divers, gymnasts, and similar athletes

Topics: Scheme basics, Numeric computation

As you may know, in many sports, such as diving and gymnastics, a group of judges award scores to each athlete. To improve the accuracy of the scoring, they normally drop the top and bottom score and then compute the average. (Yes, there are many variants thereof.) We’ll call this a robust average.

In this problem, we will work incrementally to build some portions of a program that calculate an overall score from several judges for a diver’s performance. In addition, we want the program to work for the scores provided for any diver, so we will generalize the operations with a form of subroutine.

Furthermore, we want the output to be easily readable and meaningful, so we will do some numeric processing to convert the precise scores into something more humanly intelligible.

In the reading on the parts of algorithms, we learned that we can write named subroutines take named parameters. While we have not yet learned how to write our own subroutines (“procedures”, in Scheme), we do have an easy way to import named values from different programs.

Say we define the file contestantA.rkt with the following contents representing each judge’s score for the first contestant.

#lang racket
(provide (all-defined-out))
(define judge1 7)
(define judge2 10)
(define judge3 5)
(define judge4 8)
(define judge5 6)
(define judge6 9)
(define judge7 8)
(define judge8 6)

If the file assignment2.rkt is saved in the same folder as contestantA.rkt, we can load the data and, say, calculate the average of the first two judges’ scores as follows.

#lang racket
(require (file "contestantA.rkt"))
(/ (+ judge1 judge2) 2)

Running this program would likely produce the following result.

17/2

Of course, that’s incomplete as a score. To find the complete score in a way that is robust to outliers, we will calculate the average of six judges’ scores after dropping the lowest and highest score, and call it robust-average. The following steps will help you solve the problem incrementally.

Part A: Finding the robust average

i. Write a definition that assigns the name total-score to a computed total of all the scores. (That is, total-score should remain correct, even if we change the values associated with judge1 through judge8.)

(define total-score ...)

ii. Write a definition that assigns the name average-score to a computed average of the scores by the usual means.

(define average-score)

iii. Write a definition that assigns the name highest-score to a computed highest score.

iv. Write a definition that assigns the name lowest-score to a computed lowest score.

v. Write a definition that assigns the name robust-average to the robust average score (that is, the score that results from dropping the lowest and highest scores and then averaging the result).

vi. To complete the notion that assignment2.rkt operates like a subroutine, create a few more contestant files, change the require statement, and verify that the results you generate are different and correct.

Part B: Cleaner averages

The averaged scores you may have seen so far may not be all that pretty. Instead of the 22/3 you might get for contestant A, we’d probably prefer 7.3 (which is an approximation of 7.3333333333333…, the decimal representation of 22/3).

You may recall that we have a number of mechanisms for rounding real numbers to integers, such as ceiling and floor. But what if we want to round not to an integer, but to only one digit after the decimal point? Scheme does not include a built-in operation for doing that kind of rounding. Nonetheless, it is fairly straightforward.

i. Add instructions to your program that calculate a version of robust-average rounded to the nearest tenth.

ii. Now, let’s generalize your instructions to round to an arbitrary number of digits after the decimal point.

Suppose precision is a non-negative integer and robust-average is the value you computed above. Write another set of instructions for rounding robust-average to use exactly precision digits after the decimal point.

> (*your-instructions* ... robust-average ... precision ...)

As you write your instructions, you may find the expt function useful. (expt b p) computes bp.

Problem 3: Adding swim times

Topics: Scheme basics, Numeric computation

The swim team needs a program to tally the times for members’ events. Suppose the values time-1-min, time-1-sec, time-2-min, and time-2-sec represent the times of two swimmers - swimmer 1 took time-1-min minutes and time-1-sec seconds and swimmer 2 took time-2-min minutes and time-2-sec. You can assume that all of these times are sensible values (i.e., positive integers, with seconds in the range 0 to 59, inclusive).

Write a definition that assigns the names time-total-min and time-total-sec to the computed sum of the two times. For example, if swimmer one took 2 minutes and 15 seconds and swimmer two took 2 minutes and 11 seconds, then the total time is 4 minutes and 26 seconds. Similarly, if swimmer one took 2 minutes and 35 seconds and swimmer two took 2 minutes and 36 seconds, then the total time is 5 minutes and 11 seconds. As the second example suggests, you will need to keep the sum of seconds sum in a sensible range.

Evaluation

We will primarily evaluate your work on correctness (does your code compute what it’s supposed to and are your procedure descriptions accurate); clarity (is it easy to tell what your code does and how it acheives its results; is your writing clear and free of jargon); and concision (have you kept your work short and clean, rather than long and rambly).