Final Exam Model Paper 8
Theory paper: 50 marksTime: 2 hours
Practical exam: 20 marksTime: 1 hour 30 minutes
All questions are compulsory.
A full practice run of the Grade 12 final exam: the theory paper (50 marks, 2 hours) and, below it, the practical exam (20 marks, 1 hour 30 minutes, at a computer, marked by the school). In the theory paper, work out on paper what each program does, as in the exam hall: its programs have no Run button. In the practical exam, the programs run on this page.
A practice paper by Science Playground, made to its own course’s grid; not an official exam paper.
Write your answers on paper first. Then open each answer and its marking scheme, and give yourself one mark for each point you got. In multiple choice, your first choice counts.
Section A
10 × 1 = 101. Choose the correct option for each of the following questions.
What is a universal Turing machine?
Pick an answer to see why.
Anisha’s program checks the Collatz rule (halve an even number; multiply an odd one by 3 and add 1) for every starting number up to 10 billion, and every one reaches 1. She writes: “This proves that every starting number reaches 1.” Which judgment is right?
Pick an answer to see why.
In software design, what is a facade?
Pick an answer to see why.
A program for a cooperative keeps the milk prices for each fat level in a table that many functions read. Why might the team store it as a tuple rather than a list?
Pick an answer to see why.
What does consistent hashing let a big service do?
Pick an answer to see why.
A digital wallet company splits its users into 10 cells that share nothing, each with its own servers and database. A faulty update is put on one cell first and breaks it. What happens?
Pick an answer to see why.
A trekking-permit system keeps 7 copies of its data that vote: a change counts only when a majority has it. How many copies must have a change, and how many copies can fail at once while it still accepts changes?
Pick an answer to see why.
What is the discrete logarithm problem?
Pick an answer to see why.
A school’s app makes the code that keeps a student signed in with random.randint(100000, 999999). Why should it be changed?
Pick an answer to see why.
Prakash, an engineer registered with the Nepal Engineering Council, finds that the software warning of cracks in a bridge was switched off to stop “too many alarms”. He says: “The Nepal Engineering Council’s code says nothing about reporting danger, so it isn’t my duty to report this.” Which judgment is right?
Pick an answer to see why.
Section B
8 × 3 = 24Write short answers to the following questions.
This Turing machine starts in state right on the first digit of a binary number, with blanks (_) on both sides:
| State | Reads | Writes | Moves | Next state |
|---|---|---|---|---|
| right | 0 | 0 | R | right |
| right | 1 | 1 | R | right |
| right | _ | _ | L | borrow |
| borrow | 0 | 1 | L | borrow |
| borrow | 1 | 0 | stay | done |
It stops in state done.
- i.Trace it on
1100. What is on the tape when it stops, and after how many steps? - ii.What does the machine do to a number?
- iii.What happens when it is given
000?
Answer
- i.In
rightit walks over 1, 1, 0, 0 (4 steps) and reads the blank (step 5), stepping back onto the last 0 inborrow. The last 0 becomes 1 (step 6), the next 0 becomes 1 (step 7), and the 1 before them becomes 0, in statedone(step 8). The tape reads 1011, after 8 steps. - ii.It subtracts 1: 1100 is twelve and 1011 is eleven. Like borrowing in subtraction by hand, every 0 at the end becomes 1 until the first 1, which becomes 0.
- iii.It turns every 0 into 1 walking left and then reads the blank before the number in state
borrow. There is no rule forborrowreading a blank, so the machine is stuck (after 7 steps) and never reachesdone: there is no 1 to borrow from, since 0 − 1 can’t be written as a binary number like these.
Marking scheme
- i. The trace to 1011, in 8 steps.
- ii. It subtracts 1 (twelve to eleven).
- iii. It reaches a blank in
borrowwith no rule, so it is stuck: there is nothing to borrow from in 0.
A dairy cooperative in Lalitpur records each farmer’s milk every morning. When a farmer’s milk is recorded, the farmer must get an SMS, and the quality officer an alert if its fat is under 3.5%; more things may be added later. Using the observer pattern, write a class MilkCollection with subscribe() and record(farmer, liters, fat), the two subscribers, and a few lines that show it working.
Answer
class MilkCollection: """Tells every subscriber when a farmer's milk is recorded.""" def __init__(self): self.listeners = [] def subscribe(self, listener): self.listeners.append(listener) def record(self, farmer, liters, fat): for listener in self.listeners: listener(farmer, liters, fat) def sms_farmer(farmer, liters, fat): print(f"SMS to {farmer}: {liters} L recorded today, fat {fat}%") def quality_alert(farmer, liters, fat): if fat < 3.5: print(f"Quality officer: {farmer}'s milk has only {fat}% fat") dairy = MilkCollection()dairy.subscribe(sms_farmer)dairy.subscribe(quality_alert)dairy.record("Maya Tamang", 12.5, 4.1)dairy.record("Bishnu Lama", 8.0, 3.2)Output
SMS to Maya Tamang: 12.5 L recorded today, fat 4.1%SMS to Bishnu Lama: 8.0 L recorded today, fat 3.2%Quality officer: Bishnu Lama's milk has only 3.2% fatA monthly total, or a message to the buyer, can later subscribe without MilkCollection changing at all.
Marking scheme
- A class that keeps a list of subscribers, with
subscribe()adding to it. record()calls every subscriber with the farmer, liters and fat.- The two subscribers (the alert only under 3.5%), subscribed and shown working.
The students who built the dairy app leave school, and next year’s class will look after it.
- i.What should its README tell them?
- ii.Write a docstring for a function
fat_bonus(liters, fat)that gives the extra rupees a farmer earns for milk with more fat. - iii.Rewrite this comment to be useful:
rate = rate * 1.1 # multiply rate by 1.1
Answer
- i.What the app is for; how to install it and run it; how to run its tests; how to help (for example, how to propose a change); and its license.
- ii.For example:
def fat_bonus(liters, fat): """The extra rupees for a day's milk: liters (a number) at fat (a percentage). Gives 0 when fat is 4.0% or less."""iii. A comment should say why, not repeat the code. For example: # The cooperative pays 10% more in the dry season (Mangsir to Jestha), as agreed in its 2082 meeting.
Marking scheme
- i. Its purpose, how to install and run it, how to run the tests (with how to help or its license).
- ii. A docstring saying what it does, what it takes and what it gives back.
- iii. A comment giving the reason for the 1.1, not repeating the code.
A streaming company releases a popular Nepali film, and 200,000 people in Nepal watch it on the first evening.
- i.The company keeps copies of the film on edge servers of a content delivery network inside Nepal’s internet companies. Why does this make the film start faster?
- ii.Why does the internet company gain too?
- iii.Name one way a viewer’s phone is sent to the nearest edge server.
Answer
- i.The film comes from a server a short distance away, inside the viewer’s own internet company, not from a data center abroad: the data has less distance to travel, crosses fewer busy links, and each server has fewer viewers to serve.
- ii.Without the edge servers, each of the 200,000 viewers would pull the film across the internet company’s expensive links across the border. With them, it crosses once, to fill the cache, and the rest stays inside the country, so the company pays for less traffic abroad and its links stay free for everything else.
- iii.DNS: the CDN’s DNS servers see roughly where the question comes from and answer with the address of a nearby edge server. Or anycast: the same address is announced from many places, and the routers carry each packet to the nearest one.
Marking scheme
- i. Shorter distance and fewer busy links: the film comes from inside the viewer’s internet company.
- ii. Less traffic over the expensive links across the border.
- iii. DNS answering with a nearby server, or anycast.
A ride app tests a new booking screen with 2 million users chosen at random. The share who book a ride rises from 31.0% to 31.1%, and a shuffle test gives p = 0.01. The manager says: “Statistically significant, so the new screen is a big success.”
- i.What does p = 0.01 mean here?
- ii.Why doesn’t it make the new screen a big success?
- iii.What should the team report?
Answer
- i.If the screen made no difference, chance alone would give a difference this big only about 1 time in 100. So the difference is probably real.
- ii.A p-value says nothing about how big or how useful a difference is. With 2 million users, even a tiny difference, 0.1 of a percentage point, can be significant. One more booking in every 1,000 users may not be worth the cost of the change.
- iii.The size of the difference (0.1 points, about 1 more booking per 1,000 users) with its 95% interval, and the p-value; then whether it is worth the cost.
Marking scheme
- i. Chance alone would give a difference this big about 1 time in 100 if the screen did nothing.
- ii. Significant isn’t big: with millions of users a tiny difference is significant.
- iii. Report the size of the difference with its interval, and weigh it against the cost.
The visitors to a national park, in thousands (made-up numbers):
| Year | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 |
|---|---|---|---|---|---|---|---|---|---|---|
| Visitors | 52 | 55 | 58 | 61 | 20 | 22 | 40 | 57 | 60 | 62 |
The park’s report says: “Visitors almost tripled from 2020 to 2023!” A critic answers: “Visitors grew less than 2% from 2019 to 2025: the park is failing.” Evaluate both claims, and say what an honest summary would be.
Answer
Both claims are cherry-picked: each chooses the start that gives the answer it wants. The report starts in 2020, the year visitors fell to a third (when travel stopped worldwide), so “tripled” measures mostly the recovery from that fall, not growth. The critic starts in 2019, the last year before the fall, and ends in 2025: 61 to 62 is true, but it hides that the park regained everything it lost and was growing again by 2024–2025.
An honest summary uses the whole series: visitors grew steadily from 52,000 to 61,000 between 2016 and 2019, fell to about 20,000 in 2020, recovered to the 2019 level by 2024, and reached 62,000 in 2025, the most so far.
Marking scheme
- The report’s claim judged: it starts at the unusual low of 2020, so it measures recovery, not growth.
- The critic’s claim judged: true but chosen to hide the fall and the recovery.
- An honest summary from the whole series (the rise, the fall and the recovery).
A savings group keeps a toy hash chain on paper. Each record is an amount in rupees, and each block’s hash is (the previous block’s hash × 31 + the amount) mod 97; the first block’s previous hash is 0.
- i.Work out the four hashes for the records 500, 1,200, 3,000 and 800.
- ii.The last hash was read out at the group’s meeting. Later someone changes the third record to 300 and works out every hash again. What does the group see?
- iii.Show a different change to the third record that this toy hash would not catch, and say why real chains use SHA-256.
Answer
- i.(0 × 31 + 500) mod 97 = 500 mod 97 = 15; (15 × 31 + 1,200) mod 97 = 1,665 mod 97 = 16; (16 × 31 + 3,000) mod 97 = 3,496 mod 97 = 4; (4 × 31 + 800) mod 97 = 924 mod 97 = 51.
- ii.The third hash becomes (496 + 300) mod 97 = 796 mod 97 = 20, and the last (20 × 31 + 800) mod 97 = 1,420 mod 97 = 62. Every link checks, but the last hash is 62, not the 51 read out at the meeting, so the change is caught.
- iii.With only 97 possible hashes, an amount 97 more or less gives the same hash: 3,000 − 97 = 2,903 gives (496 + 2,903) mod 97 = 3,399 mod 97 = 4 again, so every later hash, and the last one, stays the same. SHA-256 has 2²⁵⁶ possible hashes, and nobody knows how to find two inputs with the same one, so a change can’t be hidden that way.
Marking scheme
- i. The hashes 15, 16, 4 and 51.
- ii. The rebuilt last hash 62 differs from the 51 read out, so the change is seen.
- iii. A change such as 2,903 (or 3,097) giving the same hash, and SHA-256’s huge number of hashes, with no way known to find two inputs that match.
What is the hard problem of consciousness? Describe functionalism and one other position on whether a machine could be conscious.
Answer
- The hard problem (named by the philosopher David Chalmers in 1995): science explains more and more of what a brain does, but not why any of it is felt, why seeing red feels like something at all. Nobody has solved it, so nobody can simply check whether a machine is conscious.
- Functionalism: consciousness depends on what a system does, the way information is organized and used, so a machine organized the right way could be conscious, whatever it is made of.
- Another position: consciousness needs a living body and brain (Searle and others: a simulated brain would no more feel than a simulated storm makes anything wet); or, we can’t know yet, without an agreed theory, so we should be careful in both directions.
Marking scheme
- The hard problem: why brain activity is felt, which nobody has explained.
- Functionalism: what matters is how the system works, not what it is made of.
- One other position (it needs biology; or we can’t know yet).
Section C
2 × 8 = 16Write long answers to the following questions.
A tractor owner in Nawalparasi is offered six ploughing jobs for the coming days of Asar (made-up days and payments). A job takes the tractor every day from its first day to its last, so two jobs can’t share a day.
| Job | Days | Pays (Rs) |
|---|---|---|
| J1 | 2 to 4 | 6,000 |
| J2 | 3 to 5 | 4,000 |
| J3 | 4 to 6 | 11,000 |
| J4 | 4 to 7 | 13,000 |
| J5 | 7 to 8 | 10,000 |
| J6 | 8 to 11 | 5,000 |
- i.A greedy method takes the job that ends first, then the next to end that doesn’t share a day with those taken, and so on. Which jobs does it take, and what do they pay? (2)
- ii.Another greedy method takes the job that pays most first, then the next best that fits, and so on. Which jobs, and what do they pay? (1)
- iii.Dynamic programming: the jobs are numbered in order of their last day. The best pay from the first k jobs is the larger of the best from the first k − 1 (leaving job k), and job k’s pay plus the best from the jobs that end before job k starts. Fill in the best pay after each job, and find which jobs give the most. (4)
- iv.For 30 jobs, how many sets of jobs would trying every way look at, and how many steps does the table take? (1)
Answer
i. J1 ends first (day 4): take it. J2 and J3 and J4 start on or before day 4: they share a day with J1. J5 (days 7 to 8) fits: take it. J6 starts on day 8, J5’s last day: it doesn’t fit. J1 and J5: Rs 16,000.
ii. J4 (Rs 13,000) first. J3, J1 and J2 share days with it (J1’s day 4), and so does J5 (day 7). J6 (days 8 to 11) fits. J4 and J6: Rs 18,000.
iii.
| After job | Leave it | Take it: its pay + the best before it starts | Best so far |
|---|---|---|---|
| J1 | 0 | 6,000 + 0 (nothing ends before day 2) | 6,000 |
| J2 | 6,000 | 4,000 + 0 (nothing ends before day 3) | 6,000 |
| J3 | 6,000 | 11,000 + 0 (J1 ends on day 4, not before) | 11,000 |
| J4 | 11,000 | 13,000 + 0 (nothing ends before day 4) | 13,000 |
| J5 | 13,000 | 10,000 + 11,000 (best of J1 to J3: they end by day 6) | 21,000 |
| J6 | 21,000 | 5,000 + 13,000 (best of J1 to J4: they end by day 7) | 21,000 |
The most is Rs 21,000, from J3 and J5 (days 4 to 6, then 7 to 8): reading back, the 21,000 came from taking J5 with the best of J1 to J3, which was J3. Both greedy methods missed it: ending first took the small J1, which blocked J3; paying most took J4, which blocked J5.
iv. Each job is taken or not: 2³⁰, about a billion sets (1,073,741,824). The table has one row for each job, each a comparison of two numbers (plus finding the last job that ends before it starts): about 30 rows, a few hundred steps at most.
Marking scheme
- i. J1 then J5, with the others shown not to fit.
- i. Rs 16,000.
- ii. J4 and J6: Rs 18,000.
- iii. The rule applied row by row: leave it, or its pay plus the best before it starts.
- iii. The values found correctly for the earlier jobs (6,000, 6,000, 11,000, 13,000).
- iii. J5’s row: 10,000 + 11,000 = 21,000; J6’s row stays 21,000.
- iii. The jobs read back: J3 and J5, Rs 21,000.
- iv. 2³⁰ ≈ a billion sets, against a table of about 30 rows.
A traffic office plans cameras at red lights that read the number plates of vehicles crossing on red, and send each owner a fine automatically, with no person checking. It tested the plate reader on photos of 1,000 vehicles (the results are made up):
| Plates | Read correctly | Misread (read as another vehicle’s number) | Couldn’t read |
|---|---|---|---|
| New embossed plates (600) | 588 | 6 | 6 |
| Older hand-painted plates (400) | 320 | 40 | 40 |
- i.For each kind of plate, work out the share read correctly, and of the plates the reader gave a number for, the share that were wrong. (3)
- ii.Who pays for each kind of mistake? Is it fair? (2)
- iii.Should the fines be sent automatically? Argue your case, and say what must be in place. (3)
Answer
i.
- Embossed: read correctly 588 ÷ 600 = 98%. It gave a number for 588 + 6 = 594, and 6 ÷ 594 ≈ 1% were wrong.
- Hand-painted: read correctly 320 ÷ 400 = 80%. It gave a number for 320 + 40 = 360, and 40 ÷ 360 ≈ 11% were wrong: more than 1 in 9.
- So a fine sent for an older plate is about 11 times as likely to go to the wrong owner.
ii. A misread sends a fine to an innocent owner, who must prove it wasn’t them (travel to an office, lose a day’s work, or pay to make it go away): the cost falls on someone who did nothing. A plate it couldn’t read lets a driver who crossed on red go unfined, a cost to road safety but not to an innocent person. It isn’t fair: owners of older vehicles with hand-painted plates, often owners who can least afford a wrong fine, would get many more wrong fines than others for the same behavior.
iii. One well-argued case (other conclusions earn marks with good reasons): No fine without a person checking, at least for now. A wrong fine punishes an innocent person and destroys trust in the whole system; with about 1 in 9 numbers wrong for older plates, automatic fines would send many. What must be in place:
- Human review: an officer looks at the photo beside the number read before any fine is sent.
- The owner’s right to see and appeal: the fine comes with the photo, and a simple, free way to appeal.
- Fix the gap at its cause: train and test the reader on many more hand-painted plates; report its error by kind of plate, and check it again after release (new plates, dirt, rain and night photos can make it drift).
- A named office responsible for the system, and a way to stop it.
Marking scheme
- i. Embossed: 98% read correctly, about 1% of its numbers wrong.
- i. Hand-painted: 80% read correctly, about 11% of its numbers wrong.
- i. The comparison: wrong fines about 11 times as likely for older plates.
- ii. A misread fines an innocent owner; an unread plate lets an offender go.
- ii. Unfair: owners of older plates bear far more wrong fines.
- iii. A clear conclusion argued from the cost of a wrong fine to an innocent person.
- iii. Human review of each fine, with the photo and a free way to appeal.
- iii. The gap fixed (more data on hand-painted plates, errors reported by plate type, checked after release), with a way to stop.
Practical exam
20 marksMarked by the school. Work alone at a computer, with no internet and no AI, in front of the teacher, for 1 hour 30 minutes. Write and run each program, show it working, and answer the teacher’s questions. On this page, press Edit on a program to type in it, and Run to run it.
Cutting bamboo. A craftsman in Syangja cuts bamboo poles into pieces to sell; PRICE gives what a piece of each length fetches (made-up prices). Write best_cut(length), which uses dynamic programming to find the most rupees a pole of a whole number of meters can fetch, and the lengths of its pieces. Write automatic tests for it, run by a small test runner: at least a pole of 0 m, a pole of 1 m, a length where taking the most rupees a meter first would be wrong, a test that the pieces add up to the pole and to the rupees, and a test that compares it with trying every way, for every length up to 12 m. Then print the best way to cut a 10 m pole.
PRICE = {1: 20, 2: 100, 3: 170, 4: 180, 5: 240, 6: 330} # rupees for a piece of each length, in meters def best_cut(length): return 0, [] # Your tests hereAnswer
The best for a pole of n meters is the best, over every first piece, of that piece’s price plus the best for the rest. The table best keeps the answer for every shorter pole, so each is worked out once.
PRICE = {1: 20, 2: 100, 3: 170, 4: 180, 5: 240, 6: 330} # rupees for a piece of each length, in meters def best_cut(length): """(most rupees, piece lengths) for a pole of a whole number of meters.""" best = [(0, [])] # best[n]: the best for a pole n meters long for n in range(1, length + 1): options = [] for piece, rupees in PRICE.items(): if piece <= n: rest_rupees, rest_pieces = best[n - piece] options.append((rupees + rest_rupees, [piece] + rest_pieces)) best.append(max(options)) return best[length] def try_every(length): """The most rupees, by trying every first piece and every way to cut the rest.""" if length == 0: return 0 return max(rupees + try_every(length - piece) for piece, rupees in PRICE.items() if piece <= length) def test_nothing_to_cut(): assert best_cut(0) == (0, []) def test_one_meter(): assert best_cut(1) == (20, [1]) def test_greedy_would_fail(): # most rupees a meter first would take 3 m (Rs 170) and 1 m (Rs 20): Rs 190; two 2 m pieces give Rs 200 assert best_cut(4)[0] == 200, f"gave {best_cut(4)}" def test_pieces_add_up(): for n in range(13): rupees, pieces = best_cut(n) assert sum(pieces) == n and sum(PRICE[p] for p in pieces) == rupees, f"{n} m: {pieces}" def test_same_as_trying_every_way(): for n in range(13): assert best_cut(n)[0] == try_every(n), f"{n} m: {best_cut(n)[0]}, not {try_every(n)}" passed = failed = 0for name, test in list(globals().items()): if name.startswith("test_"): try: test() print("PASS", name) passed += 1 except AssertionError as problem: print("FAIL", name, problem) failed += 1print(passed, "passed,", failed, "failed") rupees, pieces = best_cut(10)print(f"A 10 m pole: Rs {rupees}, cut into {sorted(pieces, reverse=True)} m")Output
PASS test_nothing_to_cutPASS test_one_meterPASS test_greedy_would_failPASS test_pieces_add_upPASS test_same_as_trying_every_way5 passed, 0 failedA 10 m pole: Rs 540, cut into [3, 3, 2, 2] mMarking scheme
- A table (list or dictionary) of the best for every length from 0 up, each worked out once.
- Each entry the best, over every first piece that fits, of its price plus the best for the rest.
- Keeps the pieces as well as the rupees.
- A function that tries every way (recursion) to compare with.
- Tests as
test_functions withassert: 0 m, 1 m, the length where greedy fails (4 m: Rs 200). - The tests that the pieces add up, and that it matches trying every way up to 12 m; a runner reports passes and failures, and all pass.
- Prints the 10 m pole: Rs 540, as 3, 3, 2 and 2 m.
May the bus run? A bus company in Myagdi decides each morning whether its bus may take the hill road. The requirement says: “The bus may run only when the road is open and less than 50 mm of rain fell overnight.” Run the tests, find why some fail, fix the code, and run them again.
def can_run(road_open, rain_mm): """True when the bus may run: the road is open and less than 50 mm of rain fell overnight.""" if road_open or rain_mm < 50: return True return False def test_open_and_dry(): assert can_run(True, 12) is True def test_closed_and_wet(): assert can_run(False, 80) is False def test_open_but_heavy_rain(): assert can_run(True, 75) is False, f"gave {can_run(True, 75)}" def test_closed_on_a_dry_day(): assert can_run(False, 0) is False, f"gave {can_run(False, 0)}" passed = failed = 0for name, test in list(globals().items()): if name.startswith("test_"): try: test() print("PASS", name) passed += 1 except AssertionError as problem: print("FAIL", name, problem) failed += 1print(passed, "passed,", failed, "failed")Answer
The first run:
Output
PASS test_open_and_dryPASS test_closed_and_wetFAIL test_open_but_heavy_rain gave TrueFAIL test_closed_on_a_dry_day gave True2 passed, 2 failedLine 3 says or, so the bus may run when either condition holds: an open road after 75 mm of rain, or a closed road on a dry day. The requirement needs both. The two tests that passed had both conditions the same (both true, or both false), where or and and agree. The tests match the requirement, so the code is wrong: or becomes and.
def can_run(road_open, rain_mm): """True when the bus may run: the road is open and less than 50 mm of rain fell overnight.""" if road_open and rain_mm < 50: return True return False def test_open_and_dry(): assert can_run(True, 12) is True def test_closed_and_wet(): assert can_run(False, 80) is False def test_open_but_heavy_rain(): assert can_run(True, 75) is False, f"gave {can_run(True, 75)}" def test_closed_on_a_dry_day(): assert can_run(False, 0) is False, f"gave {can_run(False, 0)}" passed = failed = 0for name, test in list(globals().items()): if name.startswith("test_"): try: test() print("PASS", name) passed += 1 except AssertionError as problem: print("FAIL", name, problem) failed += 1print(passed, "passed,", failed, "failed")Output
PASS test_open_and_dryPASS test_closed_and_wetPASS test_open_but_heavy_rainPASS test_closed_on_a_dry_day4 passed, 0 failedMarking scheme
- Runs the tests and names the failing ones: heavy rain on an open road and a closed road on a dry day both give
True. - Finds the cause:
oron line 3 lets the bus run when only one condition holds. - Fixes the code (
and), not the tests, which match the requirement. - Runs all the tests again, and all pass.
Whose song is it? The spiny babbler is the only bird found in Nepal and nowhere else. Volunteers in Makwanpur left sound recorders in the forest and beside a road, and a model listened to 60 short recordings and said whether a spiny babbler was singing. An expert listened to the same recordings. In recordings.csv, babbler is the expert’s answer and model the model’s. (The recordings and results are made up.)
Write a program that reads the file, counts the four outcomes, and works out the model’s precision and recall for all 60 recordings, for the forest and for the road. Then write one limit of the model, in a sentence for the volunteers, with the numbers that show it.
recordings.csv
place,babbler,modelforest,no,noroad,no,noroad,no,noroad,no,noforest,no,noroad,yes,noroad,no,yesroad,no,noroad,no,noforest,no,noforest,yes,yesroad,no,noforest,no,noroad,yes,noroad,yes,noforest,yes,yesroad,no,noforest,no,noroad,no,noforest,yes,yesroad,no,noroad,no,yesforest,no,noroad,yes,yesroad,yes,yesforest,yes,yesroad,yes,noroad,no,yesforest,no,noforest,no,noroad,no,noroad,no,noforest,yes,yesforest,yes,noroad,no,yesroad,yes,noroad,no,noforest,yes,noforest,no,noroad,no,noforest,yes,yesforest,no,noforest,no,noroad,yes,yesroad,yes,yesforest,no,yesforest,yes,yesforest,no,noroad,yes,noroad,no,noforest,yes,yesforest,yes,yesforest,yes,yesforest,yes,yesroad,yes,yesforest,no,noroad,no,noforest,yes,yesforest,no,noforest,no,noimport csv with open("recordings.csv") as f: rows = list(csv.DictReader(f)) # Your program hereAnswer
import csv with open("recordings.csv") as f: rows = list(csv.DictReader(f)) def measure(name, places): """Count the outcomes for the recordings made in these places.""" tp = fp = fn = tn = 0 for row in rows: if row["place"] not in places: continue real, said = row["babbler"] == "yes", row["model"] == "yes" if real and said: tp += 1 elif real: fn += 1 elif said: fp += 1 else: tn += 1 print(f"{name:7} TP {tp:2} FP {fp} FN {fn} TN {tn:2} precision {tp / (tp + fp):.0%} recall {tp / (tp + fn):.0%}") measure("All", ["forest", "road"])measure("Forest", ["forest"])measure("Road", ["road"])Output
All TP 17 FP 5 FN 8 TN 30 precision 77% recall 68%Forest TP 12 FP 1 FN 2 TN 15 precision 92% recall 86%Road TP 5 FP 4 FN 6 TN 15 precision 56% recall 45%One limit: “Beside the road the model heard only 5 of the 11 recordings in which a spiny babbler sang, and almost half of its ‘babbler’ answers there were wrong, so its counts near roads can’t be trusted.” Traffic noise is a likely reason; a count from road recordings would make the birds there look rarer than they are.
Marking scheme
- Reads the file (with
csv) and counts true positives, false positives, false negatives and true negatives. - Precision and recall from the counts: 77% and 68% for all 60.
- The forest and the road measured apart (recall 86% and 45%).
- One limit stated in a sentence with its numbers (the road’s recall or precision).
Viva. The teacher asks each student about their own work, for example:
- i.Why does
best_cutkeep a table instead of calling itself for the rest of the pole each time? Why does taking the most rupees a meter first fail for 4 m? (2) - ii.In the bus program, why did two tests pass, though the code was wrong? (1)
- iii.The model’s precision over all 60 recordings is 77%. Why would it be wrong to use it to count babblers beside roads? What would you do? (2)
Answer
- i.Trying every way calls itself for the same shorter poles again and again (the rest of a 10 m pole after a 1 m piece, and after two 1 m pieces, and so on: the same smaller problems overlap), so the work grows very fast with the length. The table works out each length once, from the ones before it: about 6 tries for each meter. For 4 m, the most a meter is the 3 m piece (about Rs 57 a meter), which leaves 1 m worth only Rs 20: Rs 190. Two 2 m pieces give Rs 200: the first choice that looked best spoiled what was left.
- ii.Those two tests had both conditions true or both false, and then
orandandgive the same answer. Only a test with one true and one false shows the difference. - iii.The overall numbers mix the forest, where it works well (recall 86%), with the road, where it misses more than half the songs (recall 45%) and almost half its “babbler” answers are wrong. Beside roads its counts would be wrong both ways. I would record more by roads, train and test it on that noise, and report its results by place, checking road counts by ear until it does better.
Marking scheme
- i. The table works out each length once; calling itself repeats the same smaller problems many times.
- i. Most rupees a meter takes 3 m + 1 m (Rs 190), but 2 m + 2 m gives Rs 200.
- ii. Those tests had both conditions the same, where
orandandagree. - iii. The overall numbers hide the road’s poor recall (45%) and precision (56%).
- iii. More road recordings to train and test on, results reported by place, and checks by ear meanwhile.
Your score
0 / 50 theory
0 / 20 practical
24 questions not marked yet
| Section A | – | Full marks 10 |
| Section B | – | Full marks 24 |
| Section C | – | Full marks 16 |
| Practical exam | – | Full marks 20 |
Marks lost, by unit
Mark a question to see your score here.
