You are on page 1of 8

Computer Science 1 CSci 1100

Test 3 Version 2
November 21, 2013
SOLUTIONS
1. (6 points) Given a dictionary, d, whose keys are strings and values are sets of strings. Write one line
of code to create a set, s, that contains all strings that appear in the dictionary both as a key and as
a member of the value sets. No loops are allowed. For example, if the dictionary is
d = { 'rpi' : set(['union', 'wpi']), 'wpi' : set(['union', 'cornell'])}
Then s should be
set(['wpi'])
Solution:
This is problem is ill-formed. It was originally intended to have all keys and values be strings, but it
was mistakenly changed just before the test. If that were the case then the following solution would
have been correct:
s = set(d.keys()) & set(d.values())
It is the solution that most students wrote, although some students did realize that d.values() is a
list of sets (as the problem was written) and therefore creating a set is not a valid operation. There is
a one-line solution to the question as written using functions we have not discussed:
s = set(d.keys()) & reduce(lambda x,y: x.union(y), d.values())
If you are curious, take a look at reduce and lambda to see if you can gure out what these do.
2. (10 points) Consider the following code for binary search, including an added print statement
def binary_search( x, L):
low = 0
high = len(L)
while low != high:
mid = (low+high)/2
print low, mid, high
if x > L[mid]:
low = mid+1
print "above"
else:
high = mid
print "below"
return low
Show the output of the statement
print binary_search( 10, [ 1, 4, 6, 6, 10, 10, 10, 12 ])
Solution:
0 4 8
below
0 2 4
above
3 3 4
above
4
3. (4 points) When running the merge function discussed in lecture, how must the two input lists be
organized in order for the function to work properly?
Solution:
The values in each list must be in increasing order (or at least non-decreasing).
2
4. (24 points, total) Consider the Bird class
class Bird(object):
def __init__( self, n, x0, y0, r0, dx0, dy0 ):
self.name = n
self.x = x0
self.y = y0
self.radius = r0
self.dx = dx0
self.dy = dy0
(a) (9 points out of the 24) Add two methods to the Bird class. The rst one, called move should
change the x and y positions of a bird by the dx and dy values. The second one, called in_bounds,
should return True if all of the birds circle is inside the game rectangle (with corners (0,0) and
(100,100)), and False otherwise.
b = Bird( 'Tweety', 21.5, 12.5, 8, -6.2, -3.2)
b.move()
print b.x, b.y # Should output 15.3 9.3
print b.in_bounds() # Should output True
b.move()
print b.in_bounds() # Should output False
Solution:
def move(self):
self.x += self.dx
self.y += self.dy
def in_bounds(self):
return self.x-self.radius > 0 and self.x+self.radius < 100 and \
self.y-self.radius > 0 and self.y+self.radius < 100
(b) (15 points out of the 24) When you wrote your code for HW 7 we allowed you to assume
(among other things) that the birds were inside the game rectangle at the start. Our solution,
however, included safety checks to make sure that we gave you valid input. Write a function
check_birds that takes a list of birds as its only argument. The function should change the
list of birds to remove any bird having any part of its circle on or outside the boundaries of the
rectangle. The function should return the name of the birds that were removed. For example,
after the following code
birds = []
birds.append( Bird('Tweety', 7, 95, 12, 2, 3) )
birds.append( Bird('Sylvestor', 50, 50, 10, -3, 6) )
birds.append( Bird('Daisy', 80, 20, 9, -1, 5) )
print check_birds(birds), len(birds)
The output should be
[ 'Tweety' ], 2
because Tweety has been removed from birds for being outside the rectangle.
Solution:
def check_birds( birds ):
removed = []
i = 0
while i < len(birds):
if not birds[i].in_bounds():
removed.append( birds[i].name )
3
birds.pop(i)
else:
i += 1
return removed
Solution where the indices of the birds to remove are kept:
def check_birds( birds ):
remove_indices = []
remove_names = []
for i in range(len(birds)):
if not birds[i].in_bounds():
remove_names.append( birds[i].name )
remove_indices.append( i )
remove_indices.reverse() # so we can pop back-to-fron
for i in remove_indices:
birds.pop(i)
return remove_names
Solution where the birds to keep are in the second list that is properly copied back to birds
def check_birds( birds ):
remove_names = []
keep_birds = []
for i in range(len(birds)):
if birds[i].in_bounds():
keep_birds.append(birds[i])
else:
remove_names.append( birds[i].name )
birds[:] = remove_names # The [:] is crucial
return remove_names
4
5. (24 points, total) Suppose you have a dictionary to represent your phone contacts. The keys are the
names of people and the value associated with each key is the set of all that persons phone numbers,
represented as strings (and only three digits for simplicitys sake). Here is an example:
contacts = {}
contacts['Bob'] = set(['100', '909'])
contacts['Alice'] = set(['505', '101'])
contacts['Chad'] = set(['999'])
contacts['Danielle'] = set(['123' ,'234', '345'])
(a) (7 points of the total) Write a function called add_contact that adds a phone number to a
contact. If the contact does not exist, the function should create the contact. For example, the
function call
add_contacts( contacts, 'Bob', '108')
would add a third number to the contact set for 'Bob'
Solution:
def add_contacts( contacts, name, number ):
if name in contacts:
contacts[name].add(number)
else:
contacts[name] = set([number])
(b) (7 points of the total) Write a function find_name that takes the contact dictionary and a
phone number and returns the name of the individual having that number. If the number is not
in the dictionary, the function should return the string "Unknown". For example, the call
find_name( contacts, '234' )
should return 'Danielle'. You may assume each phone number is associated with at most one
contact name.
Solution:
def find_name( contacts, number):
for name in contacts.keys():
if number in contacts[name]:
return name
return 'Unknown'
(c) (10 points of the total) Write a function reverse_contacts that creates and returns a new
dictionary where the keys are phone numbers and the values are the names of the people having
the phone number. You may assume only one person has a given phone number. Using this
new dictionary it will be much faster to search for the name associated with a number. For the
contacts dictionary from the start of the problem the call
reverse_contacts( contacts )
Should produce the dictionary
{'101': 'Alice', '909': 'Bob', '999': 'Chad', '345': 'Danielle', \
'123': 'Danielle', '234': 'Danielle', '100': 'Bob', '505': 'Alice'}
Solution:
5
def reverse_contacts( contacts ):
rc = {}
for name in contacts.keys():
for number in contacts[name]:
rc[number] = name
return rc
6
6. (10 points) Show the output of the following code:
places = { 'OR':{'Portland' : set(['Pearl District', 'Alameda']), 'Eugene' : set()},
'NY':{'Albany' : set(), 'NY' : set(['Chelsea', 'Harlem'])} }
print places['OR']['Eugene'] # <---
a = []
for place in places:
a += places[place].keys()
print a # <---
for x in a:
if len(x) < 7:
print x
for place in places:
if x in places[place]:
print places[place][x] # <---
Solution:
set([])
['Albany', 'NY', 'Portland', 'Eugene']
Albany
set([])
NY
set(['Harlem', 'Chelsea'])
Eugene
set([])
7. (12 points) Write a function called smallest_in_common that takes two dierent lists, both in in-
creasing order, and returns the smallest value that appears in both lists. Importantly, you may not
use a set, and you may not use a dictionary (these lead to simple code, but does more work than
necessary). The most credit will be given for solutions that scan through each list no more than once.
For example, if
l1 = [ 1, 3, 8, 12, 12, 15, 20 ]
l2 = [ 7, 9, 10, 11, 15, 30, 35 ]
l3 = [ 2, 4, 5, 13, 16, 17, 23, 25 ]
then
print smallest_in_common(l1,l2) # should output 15
print smallest_in_common(l1,l3) # should output None
Solution:
def smallest_in_common(l1, l2):
i = 0
j = 0
while i < len(l1) and j < len(l2):
if l1[i] < l2[j]:
i += 1
elif l1[i] > l2[j]:
j += 1
else:
return l1[i]
return None
7
8. (10 points) In order to sort a list of 2d point coordinates, [x,y], you need to write a function, which
we will call compare_points, that will be passed to the list sort function. compare_points should
take two points (lists of two values) p and q as arguments, and it should return -1 if p should be before
q, 1 if p should be after q, and 0 if p and q are equal. Write compare_points so that the ordering is
based on the rst value unless these values are the same. As examples,
print compare_points( [1,3], [5,6] ) # outputs -1
print compare_points( [1,3], [1,6] ) # outputs -1
print compare_points( [1,3], [1,3] ) # outputs 0
print compare_points( [1,3], [0,3] ) # outputs 1
Using this, the code
L = [ [5,8], [5,2], [12,3], [1,3], [10,2], [12,1], [12,3] ]
L.sort( cmp=compare_points )
print L
outputs
[[1, 3], [5, 2], [5, 8], [10, 2], [12, 1], [12, 3], [12, 3]]
Solution:
def compare_points( p, q ):
if p[0] < q[0]:
return -1
elif p[0] > q[0]:
return 1
elif p[1] < q[1]:
return -1
elif p[1] > q[1]:
return 1
else:
return 0
or
def compare_points( p, q ):
if p[0] < q[0] or (p[0] == q[0] and p[1] < q[1]):
return -1
elif p[0] > q[0] or (p[0] == q[0] and p[1] > q[1]):
return 1
else:
return 0
or
def compare_points( p, q ):
if p[0] < q[0] or (p[0] == q[0] and p[1] < q[1]):
return -1
elif p[0] == q[0] and p[1] == q[1]:
return 0
else:
return 1
8