Download Lab 07: Recursion

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Lab 07: Recursion
Homework due Wed. Oct. 29
Project due Mon. Nov. 3
Williams CS 136, Fall 2014, Prof. McGuire
1
Educational Goals
• Master complex applications of recursion
• Design elegant solutions to mathematically-motivated problems
• Learn the enumeration design pattern
From now on in this course, “Python list” and “array” both refer to Python’s built-in list class. That class is actually
a dynamic array with amortized O(1) insert and removal at either end, O(1) access to each element, and O(n) insertion
and removal within the list in the length n of the list.
2
Homework
Although many language standard libraries do not do so, it is possible to implement a String data structure so that
substrings can be computed in constant time, independent of the length of the substring or the original string. Assume
that Strings are immutable. Describe briefly how you would implement a String with this fast substring. You may use
code to illustrate your design, although it is not necessary.
3
Specification
You may spend no more than five hours on this project after your scheduled lab session∗ . The time need not be
contiguous and you are responsible for monitoring it yourself in accordance with the Honor Code.
1. Create a file named recursion.py that contains the following first line:
# username, Your Full Name, [email protected]
where username, Your Full Name, and email are the appropriate fields for you.
2. Add the following functions to recursion.py, being careful with whitespace (Python uses indenting level
to mark blocks of statements)
3. Implement the following functions.You may define additional helper functions with names prefixed by a single
underscore.
# Returns the number of cannon balls in a square pyramid stack of the given integer height,
# in which there is one cannonball at the top, sitting on top of a square of four
# cannonballs, sitting on top of a square composed of nine cannonballs, and so forth.
def countSquareCannonballs(height):
# Returns true if string s reads the same forwards and backwards and false otherwise.
def isPalindrome(s):
# Returns the sum of the digits in integer n when it is represented in decimal form.
# Example: digitSum(1234) == 1 + 2 + 3 + 4 == 10
def decimalDigitSum(n):
∗ If you’re not finished by that point, don’t worry. You’ve probably achieved most of the educational value and grade after five hours. I know
that given enough time, you could find and fix that last bug. But I don’t want you spending inordinate amount of time in lab on something like
that. If this was an English paper or Math problem set, you’d happily submit it once it was “done,” even if not perfect. Just because there’s a
computer telling you that your code is imperfect shouldn’t change your behavior. I don’t expect anyone in this class to submit perfect work for any
assignment. Just include a README.TXT file or comments explaining what you’re thinking and any bugs that you know about. Work on bonus
topics does not count against the time total, nor does time spent before your scheduled lab session.
1
# Returns a string that is the binary representation of the integer n.
# The result must have no leading zeros (unless it is zero itself)
def toBinaryString(n):
# Returns true if some subset of the Python list of integers in
# numbers sums to exactly targetSum (this is the famous ‘‘subset-sum problem’’).
def canMakeSum(numbers, targetSum):
# Appends all unique subsets of string s to the Python list result in any order.
# subsetStrings("ABC", r) -> r = ["", "A", "B", "C", "AB", "AC", "BC", "ABC"].
def subsetStrings(s, result):
# Appends all permutations of string s to the Python list result in any order.
# permute("ABC", r) -> r = ["ABC", "ACB", "BAC", "BCA", "CAB", "CBA"]
def permutationStrings(s, result):
For example:
For example:
4. Your solutions must be recursive, even if you can come up with an iterative alternative. You may not use for,
while, itertools, list comprehensions, or other iteration constructs anywhere in your solution. You may not
mutate arguments in your solution, e.g., by writing to the fields of a class or elements of an array, except where
specified.
5. Label each function with its space and time bounds in terms of the argument parameters. You can assume that
arithmetic is constant time, but beware that string concatenation and searching are linear in the length of the
strings. Assume that substring is linear in the length of the substring.
6. Your script must not print any output or perform any computation when it is imported into another script (this
means: put any test code inside an if statement as with last week).
7. Submit your solution as described at http://cs.williams.edu/˜morgan/cs136/docs.html
4
Advice
Spend a lot of time thinking and little time coding. You can solve many of these problems using a single if statement
with a single expression in each branch (and maybe a helper method). You should seek elegant solutions that reveal
the structure of the algorithm, rather than writing a lot of code that might obscure it.
A non-recursive solution for toBinaryString is:
def toBinaryString(n):
if n == 0:
return "0"
else:
s = ""
while n > 0:
s = str(number & 1) + s
n = n >> 1;
return s
The & operator performs a binary AND operation on the bits and the bit shift operator >> moves all of the bits of a
number the specified number of binary digits to the right. You can’t use the while loop in your solution, but you can
use these bitwise operators.
Note that a & 1 == a % 2 (where % is the modulo, or integer division remainder, operator in Python) and
a >> 1 == int(a / 2). You can apply the decimal equivalent trick for reading off decimal digits: dividing by 10
shifts all digits to the right and taking the floor of a number modulo 10 reads off the right-most digit.
There are two basic strategies for the more complex functions. The first is to use a helper function that accumulates some partial result with each recursive call. For example, for subsetStrings, use a helper method:
def substringHelper(s, soFar, result): that is initially called as substringHelper(s, "", result). The variable soFar keeps track of the characters currently in the substring you are building. To process s, recursively build
2
substrings containing the first character of s by adding that character to soFar and then recursively processing the
remainder of s. Also, build all substrings not containing the first character by recursing on the remainder of s without
adding the first character to soFar. In other words, you will perform two recursive calls inside substringHelper. The
variable soFar will contain one possible substring to be appended to result when s is empty.
The second basic strategy is to enumerate all possible solutions by mapping them to the set of integers and then
using bitwise operations “read off” each solution. This first arises in the assignment for the canMakeSum problem.
Think of each number in the set as mapping to a bit in an integer (it helps to write the integer under the set in binary).
For example, the set [3, 7, 1, 8, -25] contains 5 elements. For a subset, each of those elements may be in one of
2 states: either in (“1”) or out (“0”), so there are 25 = 32 possible subsets. What is the 20th subset? 20 in binary is
10100, so the subset is [3, 1]:
Set:
Integer bits:
[
1
3,
0
7,
1
1,
0
8,
0
-25
]
By simply counting from 0 to 31 and reading off the bits of each of those integers, we can read off the subsets. For
each subset, it is a simple matter to consider what its sum is.
Beware that most programming languages provide integers with at least 31 bits, and many today default to 64 bits.
Python provides integers with unlimited bits, so no matter how many elements are in your set, you can directly apply
this process.
5
Bonus
1. Implement the following by repeatedly creating new strings that are copies of other strings with pairs of characters removed (this isn’t too hard):
# Given a string containing only bracketing characters from [](){}, returns
# true if those characters appear in properly nested and balanced from
# and false otherwise. Examples: isBalanced("{ ( [ ] ) ( [ ( ) ] ) }") == True,
# isBalanced("([)]") == False
def isBalanced(s):
2. Now, implement that function without performing string allocation. You may use iteration constructs here if you
wish, although it is not necessary. This is a little tricky.
3