9.2 Avsluttende øvingseksamen 1 — epidemi-innpakning
Komplett 4-timers avsluttende sett etter 13–16-oppgavemalen: et flervalgsskall etterfulgt av full kodeskriving, med epidemimodell som ODE-innpakning.
Settet består av 13–16 nummererte oppgaver, og formen er fast: et flervalgsskall først, med kodesporing og matche-tabeller som gir 15–25 % av poengene, og deretter fritekst-koding som gir 75–85 %. Dette settet følger den malen:
| Del | Oppgaver | Poeng | Andel | Tid |
|---|---|---|---|---|
| Flervalgsskall | 1–6 | 16 | 21 % | ~30 min |
| Kodeskriving | 7–14 | 59 | 79 % | ~210 min |
| Totalt | 14 + poengfelt | 75 | 100 % | 240 min |
Oppgave 15 skal ikke besvares. På hvert reelle sett er den siste «oppgaven» (nummerert 15, 16 eller 17, avhengig av året) bare et felt der sensor legger inn poengene dine fra midtveiseksamen, slik at de to eksamenene til sammen blir 100 poeng. Den er med her også, som en påminnelse — se helt nederst.
Tre sensorregler som gjelder hele settet. De står i innledningen på hvert reelle sett, og de er verdt poeng:
1. Kort, idiomatisk kode. Sensor forventer korte svar. Ingen
main-program, ingen ekstra utskrift, ingen kommentarer for kommentarenes skyld.2. Rimelige antakelser er tillatt når oppgaveteksten er underspesifisert — men de skal dokumenteres i en kommentar.
3. Nødvendige import skal med når oppgaven ber om kjørbar kode.
Og den viktigste: delvis riktig kode gir uttelling. Riktig overordnet struktur — riktig løkke, riktig klasseoppsett, riktig ODE-oppsett — belønnes selv med feil i indekser eller detaljer. Å få alle indekser 100 % riktig under tidspress er nesten umulig, og de offisielle løsningsforslagene sier det rett ut. Deloppgaver kan dessuten løses uavhengig av hverandre: du kan skrive testfunksjonen i oppgave 14 selv om du ikke fikk klassen i oppgave 9 til å virke.
Settet er nyskrevet av Skolesaga. Det er ikke et offisielt UiO-sett, og det forutsier ikke hva som kommer på din eksamen.
- kap. 3.1 og kap. 3.2 — testfunksjoner og rekker (oppgave 7 og 14)
- kap. 5.1 og kap. 5.2 — __init__, __call__, __str__ og __mul__ (oppgave 2 og 9)
- kap. 6.2 — differenslikninger og oppdateringsrekkefølge (oppgave 10)
- kap. 7.2 og kap. 7.3 — det utleverte ODESolver-hierarkiet og ODE-systemer (oppgave 6 og 11)
- kap. 8.1 og kap. 8.2 — fillesing til nøstet dictionary, filtrering og skriving (oppgave 12 og 13)
- kap. 4.3 — plotting med label= og legend() (oppgave 10b og 11c)
Innpakningen i dette settet er en epidemi: smittetall, en smitterate som varierer med årstiden, en diskret modell med karantene, og et vaksinasjonsscenario løst som ODE-system. Innpakningen roterer år for år på den reelle eksamenen — det er kodemønsteret bak som gjentar seg.
Slik tar du settet
Sett av fire timer i ett stykke, og bruk minuttbudsjettet under som klokke. Skriv koden for hånd eller i en editor uten å kjøre den — i desember har du bare tekstfeltet i Inspera. Å oppdage at man er avhengig av å trykke «kjør» er den dyreste erkjennelsen man kan gjøre på eksamensdagen, og den bør komme nå.
| Oppgave | Sjanger | Poeng | Tid |
|---|---|---|---|
| 1 | A — kodesporing | 2 | 4 min |
| 2 | A — kodesporing (klasse) | 3 | 5 min |
| 3 | A — kodesporing (array) | 3 | 5 min |
| 4 | B — indekserings-matche | 2 | 5 min |
| 5 | C — exception-matche | 3 | 6 min |
| 6 | E — hvilken linje mangler | 3 | 5 min |
| Skall | 16 | 30 min | |
| 7 | F — rekke + testfunksjon | 6 | 20 min |
| 8 | A — skriv utskriften | 4 | 12 min |
| 9 | G — klasse med spesialmetoder | 10 | 35 min |
| 10 | H — differenslikning + plott | 9 | 30 min |
| 11 | J — ODE-system med hierarkiet | 10 | 35 min |
| 12 | L — fillesing til nøstet dictionary | 8 | 30 min |
| 13 | L — filtrering og skriving | 6 | 25 min |
| 14 | F — testfunksjon og bruk | 6 | 23 min |
| Kodedel | 59 | 210 min | |
| Sum | 75 | 240 min |
Disponering. Skallet er billige poeng: seks oppgaver, 16 poeng, en halvtime. Bruker du 45 minutter der, stjeler du fra oppgave 9 og 11, som er de tyngste enkeltoppgavene i settet. Er du usikker på et flervalg, kryss av og gå videre — blankt og feil gir det samme.
De store oppgavene har deloppgaver som kan løses uavhengig. Får du ikke klassen i oppgave 9 til å virke, skriv testfunksjonen i oppgave 14 likevel: du vet hva klassen skal gjøre, og det er nok.
Etter oppgave 6 er det et naturlig pausepunkt. Tar du settet som lesetrening over flere kvelder, er skallet én økt, oppgave 7–10 en annen, og oppgave 11–14 en tredje.
Del 1 — flervalgsskallet
Oppgave 1–6 · 16 poeng · ~30 minutter.
Fire «select one» og to matche-tabeller. I matche-tabellene er hver rad 0,5 poeng og vurderes for seg.
Tre kohorter i en smittesporing er lagt i en nøstet liste. Hva skriver programmet ut?
kohort = [[3, 5], [2], [7, 1, 4]]
total = 0
for gruppe in kohort:
total += len(gruppe) * gruppe[-1]
print(total, len(kohort))a) 24 3
b) 22 3
c) 24 6
d) 12 3
Hva skriver programmet ut?
class Teller:
def __init__(self, start):
self.n = start
def __call__(self, okning):
self.n = self.n + okning
return self.n
t = Teller(4)
print(t(3), t(3), t.n)a) 7 7 4
b) 7 10 10
c) 4 7 10
d) 7 10 4
Hva skriver programmet ut? Legg merke til at NumPy skriver arrayer uten komma.
import numpy as np
u = np.zeros(6)
u[0] = 100.0
for n in range(3):
u[n + 1] = u[n] * 0.8
print(u[:4], u.size, u[-1])a) [100. 80. 64. 51.2] 6 0.0
b) [100. 80. 64. 51.2] 4 51.2
c) [100. 80. 64.] 6 0.0
d) [80. 64. 51.2 41.0] 6 0.0
Gitt
uker = [10, 11, 12]
tall = [[41, 3], [58, 6], [73, 9]]Par hvert uttrykk med verdien.
| Rad | Uttrykk |
|---|---|
| 1 | tall[uker[0] - 10][1] |
| 2 | tall[-1][:1] |
| 3 | tall[1][uker[1] - 11:] |
| 4 | uker[tall[0][1] - 2] |
Alternativer:
1.
[58, 6]2.
113.
34.
[73]5. [73, 9]
6. 41
7. [3]
8. 12
Filen andel.py inneholder:
import sys
def andel(smittede, testede):
assert testede > 0, 'ingen tester'
return 100 * smittede / testede
verdier = {'u10': ['41', '500'], 'u11': ['58', '0'], 'u12': ['sju', '300']}
uke = sys.argv[1]
par = verdier[uke]
print(andel(int(par[0]), int(par[1])))Par hver kommandolinje med det som skjer.
| Rad | Kommandolinje |
|---|---|
| 1 | python andel.py u10 |
| 2 | python andel.py u11 |
| 3 | python andel.py u12 |
| 4 | python andel.py |
| 5 | python andel.py U10 |
| 6 | python andel.py u10 ekstra |
Alternativer:
1.
AssertionError2.
KeyError3. Ingen feil — programmet skriver et tall
4.
ValueError5.
IndexError6.
ZeroDivisionErrorProgrammet skal løse fra til med 100 steg, med u(0) = 50, ved hjelp av det utleverte ODESolver-hierarkiet. Én linje er fjernet:
from ODESolver import RungeKutta4
def f(t, u):
return -0.2 * u
solver = RungeKutta4(f)
# HER MANGLER ÉN LINJE
t, u = solver.solve((0, 10), 100)
print(u[-1])Hvilken linje hører på plassen?
a) solver.u0 = 50.0
b) solver.set_initial_condition(50.0)
c) solver.solve(50.0)
d) solver = RungeKutta4(f, 50.0)
— naturlig pausepunkt —
Del 2 — kodeskriving
Oppgave 7–14 · 59 poeng · ~210 minutter.
Herfra er alt fritekst: du skriver kode i et tekstfelt. Skriv den ferdig uten å kjøre den, og skriv den kort. Deloppgavene kan løses uavhengig av hverandre.
Cosinus kan skrives som en uendelig rekke:
a) Skriv en funksjon cos_rekke(x, N) som returnerer summen av leddene fra til og med . (4 poeng)
b) Skriv en testfunksjon test_cos_rekke() som sjekker at cos_rekke gir omtrent samme svar som math.cos for med . Testfunksjonen skal ikke ta argumenter og ikke skrive ut noe. (2 poeng)
Skriv nøyaktig hva programmet skriver ut, med mellomrom og desimaler slik terminalen viser det.
tilfeller = {'u10': 41, 'u11': 58, 'u12': 73}
forrige = 0
for uke in tilfeller:
if forrige == 0:
vekst = 0.0
else:
vekst = (tilfeller[uke] - forrige) / forrige
print(f'{uke:>4s}{tilfeller[uke]:5d}{vekst:7.2f}')
forrige = tilfeller[uke]Smitteraten i en luftveisinfeksjon varierer med årstiden. En vanlig modell er
der måles i døgn, er grunnraten, er svingningens relative størrelse og er perioden i døgn.
a) Skriv en klasse Smitterate som tar , og i konstruktøren, og som gjør instansen kallbar, slik at beta(t) gir . (5 poeng)
b) Utvid klassen med to spesialmetoder: __str__, som returnerer en lesbar formel for raten, og __mul__, slik at beta * k gir en ny Smitterate der grunnraten er ganget med k — det er slik man modellerer et smitteverntiltak som reduserer kontaktraten. (5 poeng)
Oppgave 10 har en detalj som avgjør om svaret blir riktig eller meningsløst, og som er en av de mest belagte fellene i faget (#2 — feil oppdateringsrekkefølge i koblede differenslikninger).
Når den ene likningen bruker den nye verdien av den andre — altså i uttrykket for — må du regne dem i riktig rekkefølge inne i løkka. Bytter du om linjene, leser du en plass i arrayet som ennå er 0.0, og feilen forplanter seg gjennom hele beregningen. Slik ser det ut i praksis med tallene i oppgaven:
| Variant | S[1] | I[1] | Maks I |
|---|---|---|---|
Riktig rekkefølge (S først) | 4990.0000 | 17.5000 | 2309.6 på dag 12 |
Byttet om (I først) | 4990.0000 | 5007.5000 | 8745.6 på dag 2 |
Den nederste raden er ikke «litt feil» — den er en helt annen modell. Les alltid likningene og spør: bruker den ene den nye verdien av den andre?
En kommune modellerer et utbrudd dag for dag. er antall mottakelige og antall smittsomme på dag . Alle som smittes, settes i karantene og regnes som smittsomme; en fast andel av de smittsomme friskmeldes hvert døgn:
Legg merke til at bruker : antall nysmittede er nettopp nedgangen i .
a) Skriv en funksjon karantene(S0, I0, beta, gamma, N) som regner ut steg og returnerer de to arrayene S og I. (6 poeng)
b) Skriv koden som plotter de to kurvene i samme figur med S0 = 5000, I0 = 10, beta = 2e-4, gamma = 0.25 og N = 60, med aksenavn og forklaring (legende). (3 poeng)
ODE-oppgavene på eksamen kommer med ODESolver-hierarkiet som vedlegg, og grensesnittet har endret seg over tid (#14 — ODESolver-API-forveksling). Bokas standard, og formen som gjelder fra 2023, er:
- høyresiden skrives f(t, u) — tid først
- løseren kalles solver.solve((0, T), N) — et tidsintervall som tuppel, og antall steg
I eldre sett (2020–2022) var rekkefølgen omvendt i høyresiden, f(u, t), og solve tok én liste med ferdig utregnede tidspunkter i stedet for intervall og antall steg. Begge variantene er beskrevet i kap. 7.2, der du også finner Skolesagas egen referanseimplementasjon av grensesnittet.
Praktisk regel på eksamen: bruk to minutter på å lese vedleggets solve-signatur og advance-metode før du skriver noe. Det er to minutter som kan redde en tipoengsoppgave. Skriv deretter de tre stegene — konstruktør, set_initial_condition, solve — ned i rekkefølge; de bærer strukturpoengene.
En befolkning på deles i fire grupper: mottakelige , smittsomme , immune og vaksinerte . Modellen er
med , og (vaksinasjonsraten). Ved er , og . Tiden måles i døgn.
Du har ODESolver-modulen med klassene ForwardEuler og RungeKutta4 tilgjengelig.
a) Skriv høyresiden som en funksjon f(t, u) som pakker ut tilstandsvektoren og returnerer de fire deriverte. (4 poeng)
b) Skriv koden som løser systemet fra til med 1800 steg ved hjelp av RungeKutta4, og som pakker ut de fire komponentene i egne variabler. (3 poeng)
c) Skriv koden som plotter de fire kurvene i samme figur med forklaring, og beskriv hva figuren viser. (3 poeng)
Filen smittetall.txt har én linje per bydel og uke, med semikolon som skilletegn og en overskriftslinje først:
bydel;uke;smittede;innlagte
Storhaug;10;41;3
Storhaug;11;58;6
Storhaug;12;73;9
Hillevag;10;22;1
Hillevag;11;19;2
Hillevag;12;31;4
Tasta;10;7;0
Tasta;11;12;1
Tasta;12;9;1a) Skriv les_smittetall(filnavn) som returnerer en nøstet dictionary der ytre nøkkel er bydelsnavnet, indre nøkkel er ukenummeret som heltall, og verdien er en dictionary {'smittede': ..., 'innlagte': ...} med heltall. (5 poeng)
b) Skriv toppuke(data, bydel) som returnerer ukenummeret der bydelen hadde flest smittede. (3 poeng)
Bruk dictionaryen fra oppgave 12. Du kan løse denne oppgaven selv om du ikke fikk den forrige til å virke — du vet hvordan strukturen ser ut.
a) Skriv filtrer(data, grense) som returnerer en ny dictionary med bare de bydelene der summen av smittede over alle uker er minst grense. Argumentet data skal ikke endres. (3 poeng)
b) Skriv skriv_totaler(data, filnavn) som skriver en fil med overskriftslinja bydel;total og deretter én linje per bydel på formen navn;sum. (3 poeng)
Denne oppgaven bruker Smitterate-klassen fra oppgave 9. Du kan løse den selv om du ikke fikk klassen til å virke: du vet at beta(t) gir , og at beta * k gir en ny Smitterate med grunnraten ganget med k.
a) Skriv to testfunksjoner: test_smitterate(), som sjekker at raten er riktig i to punkter du kan regne ut for hånd, og test_mul(), som sjekker at beta * k gir riktig type og riktige verdier. (4 poeng)
b) Skriv koden som lager en Smitterate med , , , lager et tiltak som reduserer grunnraten til 60 %, og skriver en tabell over begge ratene ved . (2 poeng)
Oppgave 15 — skal ikke besvares
På det reelle settet står det til slutt en «oppgave» med et tomt svarfelt og en beskjed om at den ikke skal besvares. Nummeret varierer — 15, 16 eller 17, avhengig av hvor mange oppgaver settet har — men funksjonen er alltid den samme: det er feltet der sensor legger inn poengene dine fra midtveiseksamen.
Regnestykket er enkelt. Den avsluttende eksamenen gir maks 75 poeng, midtveiseksamen maks 25, og til sammen blir det 100. Ved å legge midtveispoengene inn i samme oppgavesett kan Inspera regne ut totalen automatisk.
Hva du skal gjøre med den: ingenting. La feltet stå tomt og gå videre. Hvert år er det kandidater som bruker minutter på å lure på hva som skal stå der, og noen som skriver inn en gjetning på egen midtveisscore. Det siste er ikke farlig, men det er bortkastet tid — og tid er det du har minst av på et firetimerssett.
Rett deg selv
| Oppgave | Sjanger | Riktig | Poeng |
|---|---|---|---|
| 1 | A | a | 2 |
| 2 | A | b | 3 |
| 3 | A | a | 3 |
| 4 | B | rad 1→3, 2→4, 3→1, 4→2 | 2 |
| 5 | C | rad 1→3, 2→1, 3→4, 4→5, 5→2, 6→3 | 3 |
| 6 | E | b | 3 |
| 7 | F | rekke + testfunksjon | 6 |
| 8 | A | tre linjer med riktige kolonner | 4 |
| 9 | G | klasse med tre spesialmetoder | 10 |
| 10 | H | differenslikning + plott | 9 |
| 11 | J | ODE-system + plott | 10 |
| 12 | L | nøstet dictionary | 8 |
| 13 | L | filtrering + skriving | 6 |
| 14 | F | testfunksjoner + bruk | 6 |
| Sum | 75 |
Slik gir du deg selv poeng på kodeoppgavene. Gå gjennom «Dette gir uttelling»-listen i hver fasit og kryss av det du faktisk skrev. Regelen er mildere enn du tror: riktig struktur teller mest, og en indeksfeil i en ellers riktig løkke koster ett poeng, ikke hele oppgaven. Er du i tvil om noe er «riktig nok», er svaret nesten alltid ja — de offisielle løsningsforslagene i dette faget presenterer sin egen kode som bare ett eksempel på en besvarelse som ville fått full score, ikke som den eneste.
Selvdiagnose — kryss av:
- ☐ Satte du startverdiene før løkka i differenslikningen, og oppdaterte du
S før I? (kap. 6.2)- ☐ Ga
range(N) deg N oppdateringer og N + 1 punkter i både oppgave 10 og 11? (kap. 7.1)- ☐ Returnerte
__mul__ en ny instans av klassen, ikke et tall eller en tuppel? (kap. 5.2)- ☐ Sto
self både i metodesignaturene og foran attributtene? (kap. 5.1)- ☐ Skrev du
f(t, u) med tiden først, og solve((0, T), N)? (kap. 7.3)- ☐ Pakket du ut systemkomponentene med
u[:, k] og ikke u[k]? (kap. 7.3)- ☐ Opprettet du den indre dictionaryen før du satte noe inn i den? (kap. 8.1)
- ☐ Bygde
filtrer en ny dictionary uten å endre argumentet? (kap. 8.2)- ☐ Brukte testfunksjonene
abs(expected - computed) < tol og ikke ==? (kap. 3.1)- ☐ Hadde hvert
plot-kall label=, og fulgte legend() etter? (kap. 4.3)- ☐ Lot du oppgave 15 stå tom?
Om karakteren. Ikke regn poengsummen om til en bokstav. Karakteren settes samlet av begge eksamenene, og C er en god og vanlig karakter i IN1900. Målet med dette settet er ikke å bevise noe, men å finne ut hvilke to eller tre mønstre du fortsatt må skrive ut i sin helhet noen ganger til.
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.
Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.