Exam 3: Thinking recursively
Assigned: Wednesday, 4 April 2018
Prologue Due: Friday, 6 April 2018 by 10:30pm
Exam Due: Tuesday, 10 April 2018 by 10:30pm
Cover Sheet Due: Wednesday, 11 April 2018 by the start of class
Epilogue Due: Wednesday, 11 April 2018 by 10:30pm
Please read the exam procedures page for policies, turn-in procedures, and grading details. If you have any questions about the exam, check the Q&A at the bottom of this page. We will generally spend some time in class every day on questions and answers while the exam is in progress.
While the exam is out, please check back periodically to see if we have reported any new errata.
Complete the exam using the
exam03.rkt starter source
code. Please rename this file to 000000.rkt, but replace
000000 with your generated random number.
Prologue
The prologue for this examination will be via email, which you should send to your instructor. Your message should be titled CSC 151.01: Exam 3 Prologue (your name).
-
For each problem, please include a short note about something that will help you solve the problem. Mostly, we want to see some evidence that you’ve thought about the problem. You might note some similar procedures you’ve written or problems you’ve solved in the past (e.g., in a lab or on a homework assignment). You might note procedures that you expect to use. You might sketch an algorithm. You might pose a question to yourself. (We won’t necessarily read this in a timely fashion, so if you have questions for your instructor, you should ask by email or in person.)
If, when looking at a problem, you think you already know the answer, you can feel free to write something short like “solved” or “trivial”.
-
Which of those problems do you expect to be the most difficult for you to solve? Why?
-
Conclude by answering the question What is an approach that you expect will help you be successful on this exam? For example, you might suggest that you will work thirty minutes on the exam each day, or work on the exam at 7pm each day, when your brain is most able to process information.
Epilogue
The epilogue for this examination will also be via email, which you should send to your instructor. Your message should be titled CSC 151.01: Exam 3 Epilogue (your name). Include answers to the following questions.
What was the most difficult part of the exam?
What made that part difficult?
What are two things you can do to be more successful on the next exam?
Problem 1: Splitting lists by sign
Topics: list recursion
We’ve seen a number of ways to filter elements from a list. What if we don’t want to filter elements, but rather split them into multiple categories. For example, what if we want to divide a list of real numbers into the negative, zero, and positive values?
> (partition-numbers (list -3 -2 -1 0 1 2 3))
'((-3 -2 -1) (0) (1 2 3))
> (partition-numbers (list 6 4 0 0 -5 9 -11))
'((-5 -11) (0 0) (6 4 9))
> (partition-numbers null)
'(() () ())
> (partition-numbers (list 5 1 -3 0 2 -4 8 0 3 -5))
'((-3 -4 -5) (0 0) (5 1 2 8 3))
> (partition-numbers (list -3.2 1/2 0 4.2 0.0 8/3 0 -7/2))
'((-3.2 -3 1/2) (0 0.0 0) (1/2 4.2 2 2/3))
It is certainly possible to write partition-numbers with three calls to
filter. But suppose we wanted to recurse over the list only once.
Document and implement a version of partition-numbers which recurses over
the list at most once. You may not use filter. However, since you are
likely to create the lists in reverse order, you may use reverse.
Problem 2: Removing duplicate values
Topics: list recursion
Document (4P’s) and write a recursive procedure, (remove-duplicates
lst), that takes a list as input and returns the same list,
but with the duplicates removed. Elements that remain should be in the
same order as their first appearance in the original list.
> (remove-duplicates '(d b a e b d f a q s f a a a b))
'(d b a e f q s)
> (remove-duplicates '(d b a e b d f a q s f a a a b s a m r i s b o r e d))
'(d b a e f q s m r i o)
Hint: You may find it useful to write a helper procedure, (remove-all
val lst), that removes all copies of val from lst. If
you do choose to write that helper, remember to document it.
> (remove-all 'a (list 'b 'a 'd 'a 'c))
'(b d c)
Note: Although you might be able to solve a related problem with
tally-all, if you use tally-all you cannot guarantee that the elements
stay in the same order.
Problem 3: Palindromes
Topics: numeric recursion, strings and characters
A string is called a palindrome if reversing the characters of the
string results in the same string. A few examples of palindromes are
"radar", "civic", and "aabaa". Document and write a procedure
(palindrome? str) that checks to see if the string str
is a palindrome. You may not use any string comparison procedures
for this problem (e.g. string=?, string<=?, etc.) nor may you build
other strings along the way. You must leave the string as a string.
You may not, for example, turn it into a list of characters. You will
find string-ref, string-length, and char=? useful procedures for
solving this problem.
> (palindrome? "hello")
#f
> (palindrome? "civic")
#t
Hint: Use numeric recursion with the position of the character.
Problem 4: Computing square roots
Topics: numeric recursion
Throughout the semester, you’ve used the sqrt procedure, which computes
the square root of a positive real number. But what if that procedure
did not exist? We’d need to implement it. Fortunately, we can use a
divide-and-conquer algorithm to find square roots.
Start with two guesses: One of which you know is less than the square root of the number (0 is a good start), the other of which is larger than the square root of the number (the number itself is a good start). We repeatedly use the average of those two numbers as a guess and determine whether (a) the guess is close enough to the square root; (b) the guess is smaller than the square root; or (c) the guess is greater than the square root.
How do we know whether it’s close enough? The square root might be
an irrational number, so we can’t expect to compute it exactly. So,
we’ll consider an additional parameter, epsilon. If the the square
of the guess is within epsilon, we allow the computation to stop.
But what if the guess isn’t close enough? If the square of the guess is smaller than the original number, then our guess was too small, so we increase the lower bound. Otherwise, we decrease the upper bound. In both cases, we try again.
For example, let’s find the square root of 2 with an epsilon of 0.001.
Iteration 1
- Our initial lower bound is 0 and upper bound is 2.
- Our guess is the average of these two numbers, or 1.
- 1 squared is 1.
- (2 - 1) is 1, which is much bigger than 0.001, so we keep searching.
- 1 is less than 2, so we increase our lower bound to our guess of 1.
Iteration 2
- The current lower bound is 1 and upper bound is 2.
- Our guess is 1.5, the average of 1 and 2.
- 1.5 squared is 2.25.
- (2.25 - 2) is .25, which is still bigger than 0.001, so we keep searching.
- 2.25 is bigger than 2, so we decrease our upper bound to 1.5.
Iteration 3
- The lower bound is now 1 and the upper bound is now 1.5.
- Our guess is the average of these two numbers, 1.25.
- 1.25 squared is 1.5625.
- (2 - 1.5625) is 0.4375, which is still more than 0.001. (Note that our error does not always decrease. 1.25 is further from the correct answer than 1.5 was.)
- 1.5625 is smaller than 2, so we increase our lower bound to our guess of 1.25.
The computation continues as follows.
- Lower bound: 1.25; Upper bound: 1.5; Guess: 1.375; Square: 1.890625
- Lower bound: 1.375; Upper bound: 1.5; Guess: 1.4375; Square: 2.06640625
- Lower bound: 1.375; Upper bound: 1.4375; Guess: 1.40625; Square: 1.9775390625
- Lower bound: 1.40625; Upper bound: 1.4375; Guess: 1.421875; Square: 2.021728515625
- Lower bound: 1.40625; Upper bound: 1.421875; Guess: 1.4140625; Square: 1.99957275390625
Not a perfect result, but a fairly good estimate. Certainly, the square is within .001 of the expected value.
Now it’s your turn to turn this idea into code.
Document and write a procedure, (square-root n epsilon),
that computes the square root of n using the technique described above.
In writing this procedure, you may assume that n is at least 1.
Note: You will find it useful to have a kernel that keeps track of the lower and upper bound described above.
Problem 5: Exploring random distributions
Topics: randomness and simulation, data visualization
As you may recall, we recently wrote a procedure to simulate the flipping of a
coin, which we called heads?, as well as a procedure that flips n coins
and tells us how many come up heads.
(define heads?
(lambda ()
(zero? (random 2))))
(define count-heads
(lambda (n)
(cond
[(zero? n)
0]
[(heads?)
(+ 1 (count-heads (- n 1)))]
[else
(count-heads (- n 1))])))
But how do we know if the procedure is working correctly? One approach is to do a lot of trials and to plot them. If we’ve written the procedure correctly, we should see a normal distribution.
Write but do not document a procedure, (count-heads-study n trials),
that repeatedly runs (count-heads n) and plots the distribution
of results. trials represents the number of trials.
A call to (count-heads-study 100 20000) might result in a diagram like
the following.
Hint: You will find tally-all useful in this problem.
Problem 6: Whatzitdo?
Topics: recursion over lists, named let, code reading, documentation
Consider the following interesting procedure definition.
(define s
(lambda (l)
(let kernel ([a null]
[b null]
[c l])
(cond
[(null? c)
(list (reverse a) (reverse b))]
[(number? (car c))
(kernel (cons (car c) a) b (cdr c))]
[else
(kernel a (cons (car c) b) (cdr c))]))))
Determine what this procedure does and then do the following.
a. Write 6P-style documentation for the procedure.
b. Rename the procedure and the parameters so that they will make sense to the reader.
c. Explain why there are two calls to reverse in the base case.
Questions and answers
We will post answers to questions of general interest here while the exam is in progress. Please check here before emailing questions!
Initial questions and answers
These questions and answers were provided in the initial release of the examination.
- Have you changed the general questions and answers since the last exam?
- Yes. We’ve added a few. We may have reworded a few.
- What is a general question?
- A question that is about the exam in general, not a particular problem.
- Do all the sections have the same exam?
- Yes.
- Can we still invoke the “There’s more to life” clause if we spend more than five hours on the exam?
- Yes. However, we really do recommend that you stop at five hours unless you are very close to finishing. It’s not worth your time or stress to spend more effort on the exam. It is, however, worth your time to come talk to us, and perhaps to get a mentor or more help (not on this exam, but on the class). There’s likely some concept you’re missing, and we can help figure that out.
- How do we know what our random number is?
- You should have received instructions on how to generate your random number on the day the exam was distributed. If you don’t have a number, ask your professor for one before submitting your exam.
- To show we’ve tested the code informally, would you just like us to just post the inputs we used to test the procedure? If so, how should we list those?
- Copy and paste the interactions pane into the appropriate place in the definitions pane. Select the text. Under the Racket menu, use “Comment out with semicolons.” You should have at least three examples or tests per procedure you write.
- Should we cite our partner from a past lab or assignment if we use code from a past lab or assignment?
- You should cite both yourself and your partner, although you should do so as anonymously as possible. For example “Ideas taken from the solution to problem 7 on assignment 2 written by student 641321 and partner.”
- If we write a broken procedure and replace it later, should we keep the previous one?
- Yes! This will help us give you partial credit if your final procedure isn’t quite right. But make sure to comment out the old one with semicolons and perhaps add a note or two as to what you were trying.
- Do I need to document helper procedures?
- You must document helpers using the 4Ps.
- If I write a helper procedure for one of the problems that does not require me to document the primary procedure, do I need to document the helper?
- Yes. All non-local helpers should be documented with the 4P’s.
- Do I have to document local helper procedures?
- It’s nice if you write a one-line comment that explains what they do, but you certainly don’t need 4P’s.
- What’s a local helper procedure?
- One defined within the procedure, most typically with a
letrecor a named let. - Can I use procedures that we have not covered in class?
- Probably not. Ask about individual procedures if you’re not sure.
- DrRacket crashed and ate my exam. What do I do?
- Send your instructor the exam file and any backups that DrRacket seems to have made. (Those tend to have a similar file name, but with pound signs or tildes added at the front or back.)
- How do I know when you’ve added a new question or answer?
- We try to add a date stamp to the questions and answers.
- Should we cite readings from the CSC 151 Web site? If so, how much information should we give.
- Yes. The title and URL should suffice.
- Can I use some other websites (different from our class websites) to solve problems in the exam?
- As the exam policies page states, “This examination is open book, open notes, open mind, open computer, open Web.” Note that it also states “ If you find ideas in a book or on the Web, be sure to cite them appropriately.”
- But be sure that the other Web sites rely on the procedures you’ve learned and follow the Scheme style we use.
- Do we have to cite the exam itself?
- No.
- How do I generate a random six digit number?
(random 1000000)- How do I submit my exam?
- Name it 012345.rkt (substituting your random number)
- Send it to your instructor, not the grader, as an attachment in an email message entitled “CSC 151 Exam 3” (or something similar)
- How many points will I lose if I don’t do the prologue?
- Somewhere between 2 and 5, depending on the mood of the grader.
- How many points will I lose if I do the prologue late?
- Somewhere between 0 and 5, depending on the mood of the grader.
- How many points will I lose if I don’t do the epilogue?
- Somewhere between 2 and 5, depending on the mood of the grader.
- How many points will I lose if I do the epilogue late?
- Somewhere between 0 and 5, depending on the mood of the grader.
- How many points will I lose if I don’t use the required template?
- Somewhere between 0 and 50, depending on the mood of the grader.
- Will I lose points if I fail to indent my code correctly with ctrl-i?
- Almost certainly.
- How many points will I lose if I fail to indent my code correctly?
- It depends on how hard your code is to read.
- Can you provide a sample time log?
- Sure.
; Time Log: ; Date Start Finish Elapsed Activity ; 2018-04-05 18:00 18:20 20 min Wrote four tests ; 2018-04-06 20:30 21:00 30 min Three non-working versions ; (marked as a, b, and c below). ; Decided to sleep on it. ; 2018-04-07 08:00 08:10 10 min Woke up with an idea. Coded it. ; it seems to work ; 2018-04-07 19:00 19:10 10 min A few more tests just to make ; sure. Done! - Do I have to use YYYY-MM-DD format?
- Certainly. Why would you use anything else?
- Do I get the bonus points for errors if I successfully invoke “There’s more to life”?
- Nope.
- Do I lose points for missing prologue/epilogue and other similar issues if I successfully invoke “There’s more to life”?
- Yes.
- What do you consider the hardest problem or problems on the exam?
- We may answer this question after all of the prologues are submitted.
- Do we have to document local helpers we create with
let? - Certainly. But a one-line comment suffices. For separate helpers, you should use 4P-style documentation (or, preferably, 6P-style documentation).
- In your notes on exam 2, you told us not to repeat code. Will you take off if I write repetitive code on this exam?
- Probably. You should use
letand other similar techniques to find ways to avoid repeated code. Reorganizing your code sometimes helps, too.
New general questions
These questions and answers were added after the initial release of the examination.
Errata
Please check back periodically in case we find any new issues.
- “characters” is misspelled as “chracters” in Problem 3. [AK, 1 point]
epsilonspelled asepislonin Problem 4. [Various, 1 point]- Incorrect code for the stub for Problem 5. [JG, 1 point]
- The “Produces” and “Postconditions” of the
count-heads-studyprocedure in theexam3.rktstarter file were slightly misleading. Since theplotprocedure returns a plot object rather than producing a side effect, this is whatcount-heads-studyshould do as well. [AR, 1 point]
Citations
Some of the problems on this exam are based on (and at times copied from) problems on previous exams for the course. Those exams were written by Charlie Curtsinger, Janet Davis, Rhys Price Jones, Titus Klinge, Samuel A. Rebelsky, John David Stone, Henry Walker, and Jerod Weinman. Many were written collaboratively, or were themselves based upon prior examinations, so precise credit is difficult, if not impossible.
Some problems on this exam were inspired by conversations with our students and by correct and incorrect student solutions on a variety of problems. We thank our students for that inspiration. Usually, a combination of questions or discussions inspired a problem, so it is difficult and inappropriate to credit individual students.