python设计程序辅导、 multiple-choice test辅导
- 首页 >> Python编程(c) Suppose a class takes a multiple-choice test. We're going to experiment with alternative scoring mechanisms. For this problem you'll want to say from random import * (and use the methods randrange and choice, which you can look up in the text or using help(random).)
Let's say you have these three global constants defined (a complete program might determine these values from reading a file; we're just doing it this way for convenience):
NUMBER_OF_STUDENTS = 200
NUMBER_OF_QUESTIONS = 20
NUMBER_OF_CHOICES = 4 # 3 choices is A/B/C, 4 choices is A/B/C/D, 5 is A/B/C/D/E
(c.1) Write a function called generate_answers that generates and returns a string of letters representing the correct answers to the test. (Of course answers to real tests aren't chosen randomly! We're just doing it this way to produce some test data to use when we score students' answers.) The length of the string should be the number of questions; each character in the string should be chosen randomly from the first n letters of the alphabet (where n is the number of choices). [Use the choice() method.]
Call generate_answers to generate the answers we'll use; assign the result to another global constant called ANSWERS.
(c.2) Ideally, we'd read the students and their exam answers from a file. But to save you some time, we'll skip the file-reading part and just generate a random list of students and their answers. To start with, let's say that each student is represented by a string for the student's name or ID and a string representing the student's answers to each question. [Are you thinking of a namedtuple with two fields? You should be.]
Student = namedtuple('Student', 'name answers')
s1 = Student('Jones, Jane', 'ABCCDAABAABCBBCACCAD')
s2 = Student('Smith, Sam', 'BADACCABADCCABDDCBAB')
Write the function random_students that uses the global constants above to generate and return a list of Student namedtuples. The size of the list is the number of students. The names can be randomly generated as you did in an earlier lab or, to save time, you can just generate a random ID number using randrange(). The string representing the student's answers should be generated precisely the same way as you generated the correct answers (so don't duplicate any code!).
(c.3) Modify the Student namedtuple to add two fields, one containing a list of scores on each question (1 if the student's answer matches the correct answer and 0 otherwise) and the other a number representing the sum of the list of question scores:
Student = namedtuple('Student', 'name answers scores total')
s1 = Student('Jones, Jane', 'ABCCDAABAABCBBCACCAD', [1, 0, 1, 1, 1, 0, ...], 10)
s2 = Student('Smith, Sam', 'BADACCABADCCABDDCBAB', [0, 1, 0, 0, 0, 1, ...], 5)
Then modify your random_students function to generate these student records with scores.
Generate your list of random students and then sort it by total, highest total to lowest, and print the top 10 students' names. (You can print them all; we're just trying to save paper and screen space here.) Also print the mean (average) score for all the students.
(c.4) This previous part used a conventional way to score multiple-choice exams. But you should expect the scores on this exam to be lower than on a typical exam: On a typical exam, students on the average are likelier to choose the correct answers than the wrong ones, but we generated our data completely at random. So let's think about how to generate more realistically distributed random data.
We chose each student's answer to each question above by choosing randomly from (let's say) A, B, C, and D. If the correct answer is C, we can bias the selection towards C by choosing randomly from A, B, C, D, and C—adding another copy of the correct answer to the possible choices will increase the likelihood that a random choice from that group will be the correct answer. A group of A, B, C, D, C, and C should produce the correct answer about half the time, since half the choices in the group are correct. So every time we generate a student's answer to a question, we can add to the group of answer choices a few extra copies of the correct answer—let's say between 0 and twice the number of choices, so that with four choices we'd add from 0 to 8 copies of the correct choice—and choose the student's answer randomly from that enhanced group of answer choices.
We can do this by defining a function called generate_weighted_student_answer that takes a string (one character, the correct answer) and returns a string (one character, the student answer chosen randomly from the enhanced group of alternatives as described above). Write a new function called random_students2 that's based on your random_students function but that generates each student's answer to each question by calling generate_weighted_student_answer.
Generate a new list of students using random_students2 and then sort it, highest total to lowest, and print the top 10 students' names. Also print the mean (average) score; it should be higher than in part (c.3).
(c.5) An unconventional way to score this exam would be to assign different weights to different questions. The instructor might assign those weights in advance, based on his or her judgement of which questions are harder or more important. But an even more unconventional way to assign the weights would be to derive them from the students' answers: The questions that were harder (i.e., that fewer people answered correctly) are worth more points than the easier ones.
One way to implement this would be to assign a number of points to each problem, that number being equal to the number of students who missed the problem. Write a function called question_weights that takes a list of Student records and returns a list of numbers, one number for each question on the test, where each number is the number of students who answered that question incorrectly. [Hint: It's helpful to attack complex data structures layer by layer. Try writing code that counts the number of wrong answers to a single question; then apply that in turn to all the questions.] Create another global constant that consists of the results of calling question_weights on your list of students from part (c.4).
Then write a function called Student_weighted_score that takes a Student record and the list of question weights and returns that Student record with its total field changed to reflect that student's score based on his or her correct answers and the corresponding question weights. Then apply Student_weighted_score to each student on your list of students from part (c.4). Finally, sort the result, highest total to lowest, and print the top 10 students' names along with the mean (average) score for all the students.
(d) Do these Python exercises:
(d.1a) Implement the function calculate_GPA that takes as input a list of strings representing letter grades and returns the grade point average (out of 4 with A=4, B=3, C=2, D=1, and F=0) computed from the list. Assume there are no plus or minus grades.
(d.1b) Implement a new function calculate_GPA2 that also computes a GPA from a list of grades. But where you (probably) used a series of if-statements the first time, this time you should use a dictionary (and no if-statements). Your dictionary should map each letter grade to the number of grade points it's worth, including plus and minus grades that are 0.3 points above and below the base letter grade.
assert calculate_GPA(['A', 'C', 'A', 'B', 'A', 'F', 'D']) == 2.5714285714285716
assert calculate_GPA2(['A', 'C', 'A', 'B', 'A', 'F', 'D']) == 2.5714285714285716
(d.2) Implement the function flatten_2D_list that takes as input a two-dimensional table (a list containing lists) and returns the input as a single list containing, in order, all the elements on the original nested list.
assert flatten_2D_list([[1, 3, 2], [3, 5, 1], [7, 5, 1], [3, 2], [9, 4]]) ==
[1, 3, 2, 3, 5, 1, 7, 5, 1, 3, 2, 9, 4]
(d.3a) Implement the function skip_every_third_item that takes as input a list and prints out each item on the list, except that it skips every third item (so it prints L[0] and L[1], skips L[2], prints L[3] and L[4], skips L[5], and so on).
>>> L = ['If', 'you', '432234', 'did', 'the', '9834234', 'exercise', 'correctly', '534523423',
'this', 'should', '1044323', 'be', 'readable']
>>> skip_every_third_item(L)
If
you
did
the
exercise
correctly
this
should
be
readable.
(d.3b) Now write skip_every_nth_item that takes as input a list and an int (call it n) and prints out each item on the list, except that it skips every nth item. Thus a call to skip_every_nth_item(L, 3) would produce the same result as skip_every_third_item(L).
(d.4) We are writing an application to process a company's weekly payroll. Every time an employee "clocks out" (leaves after a day's work), the employee's name is added to a list. Thus, at the end of a week, the name of an employee who worked five days will appear five times in this list.
(d.4a) Implement the function tally_days_worked that takes as input the list described above and returns a dictionary where every key is a name of an employee and the value is the number of days that employee worked in the given week, according to the list. (Do this by processing the list, item by item; don't use the count() method.) You may use the following list for testing your code; remember that the order of items in a dictionary is unpredictable:
work_week = ['Bob', 'Jane', 'Kyle', 'Larry', 'Brenda', 'Samantha', 'Bob',
'Kyle', 'Larry', 'Jane', 'Samantha', 'Jane', 'Jane', 'Kyle',
'Larry', 'Brenda', 'Samantha']
>>> workers = tally_days_worked(work_week)
>>> workers
{'Kyle': 3, 'Larry': 3, 'Bob': 2, 'Brenda': 2, 'Samantha': 3, 'Jane': 4}
(d.4b) We can determine how much each employee earned this week if we start with: (i) the dictionary produced by tally_days_worked(), (ii) an assumption that each employee who works, works an 8-hour day [we could relax this assumption by collecting clock-in and clock-out times for each employee each day, but we'll skip that for now], and (iii) a dictionary giving each employee's hourly rate: hourly_wages = {'Kyle': 13.50, 'Brenda': 8.50, 'Jane': 15.50, 'Bob': 30.00, 'Samantha': 8.50, 'Larry': 8.50, 'Huey': 18.00}
Implement the function pay_employees that takes as input the two dictionaries described above and prints out how much each employee will be paid, in the format shown below.
>>> pay_employees(workers, hourly_wages)
Kyle will be paid $324.00 for 24 hours of work at $13.50 per hour.
Brenda will be paid $136.00 for 16 hours of work at $8.50 per hour.
Larry will be paid $204.00 for 24 hours of work at $8.50 per hour.
Bob will be paid $480.00 for 16 hours of work at $30.00 per hour.
Samantha will be paid $204.00 for 24 hours of work at $8.50 per hour.
Jane will be paid $496.00 for 32 hours of work at $15.50 per hour.
(d.5) Implement the function reverse_dict that takes as a parameter a dictionary and returns a new dictionary with the keys and values of the original dictionary reversed:
>>> reverse_dict({'a': 'one', 'b': 'two', 'c': 'three', 'd': 'four', 'e': 'five', 'f': 'six'})
{'one': 'a', 'three': 'c', 'five': 'e', 'six': 'f', 'two': 'b', 'four': 'd'}
You may assume that both the keys and the values are unique and are immutable types (so it's possible for them to serve as either keys or values in a dictionary).
(e) Develop a program to solve the Anteater Bed and Breakfast problem. You may start with either partner's Stage I solution from the previous lab, or you may decide to start from the beginning this week.
Develop this code in its own Python file, separate from the rest of this assignment. Pay very close attention to the instructions, especially about developing the program in incremental stages.
[If you find yourself printing menus or calling input(), for example, you are doing it wrong: You did not read and understand the problem description. This is serious; this is the big time, relatively speaking. You have to follow the methodology we've been teaching all quarter long.]
And have some perspective here: This is one part of one assignment in the course. People will probably get full credit on this assignment without completing the entire B&B program. And even one slightly lower score on one lab assignment isn't likely to have a major effect on your ultimate grade in the course. But if you turn in code with bugs, or even worse, if you turn in code that was developed through impermissible collaboration, that could be a serious problem.