Prøveeksamen

28. april 2026
4 timer
Lukket digital eksamen (med safe exam browser).
Tillatte hjelpemiddel: bøker frå litteraturlista og opp til 6 tosidige A4 ark med eigne notat.
 

I tillegg til å være en mulighet for studenter å prøve ut en eksamen-setting, var prøveeksamen dette semesteret også delvis utviklet for forskningsformål. Oppgaveforfatterne har blitt inspirert av tidligere eksamener i emnet og har fått noen inspill fra emneansvarlig før utviklingen av prøven. Noen av oppgavene kommer også fra tidligere eksamener i emnet. Noen av oppgavene i de tre variantene av prøveeksamen var skrevet med ulike bakgrunnshistorier, men løsningene på problemene var i stor grad identisk.

Oppgavetekster, løsningsforslag og sensorveiledning finner du på denne siden.

  1. Automatisk rettet
  2. Forklaring
  3. Kodeskriving

Fordi eksamen var en lukket digital eksamen uten tilgang til å kjøre koden eller bruke internett, bes sensor ikke gi poengtrekk for forhold som enkelt ville blitt oppdaget og raskt rettet ved kjøring av koden. Dette inkluderer blant annet:

  • manglende import-setninger,
  • manglende kodeord (som f. eks. manglende def foran funksjonsdefinisjoner),
  • feil navn på funksjoner og metoder i standardbiblioteket, i egen kode eller i eksterne moduler (såfremt det fremgår noenlunde av funksjonsnavnet hva kandidaten egentlig mener),
  • feil navn på variabler (f. eks. kalle den samme variabelen både total og sum i ulike deler av koden),
  • enkle syntaks-feil (f. eks. manglende kolon etter if-setninger),
  • og så videre.

Logiske feil skal det likevel (som hovedregel) bli trukket litt for; selv om man kunne ha oppdaget at noe var feil ved kjøring av koden. Dette inkluderer blant annet:

  • presedensfeil,

  • forveksling av indekser og elementer,

  • off-by-one -feil,

  • feil i algoritmer,

  • og så videre.

1 Automatisk rettet

Finn riktig datatype til hvert uttrykk (eller identifiser Error)

67//76
"smør" in "smørbrødet"
[123456789]
"1. Januar, 1970"[0]

Klikk på de grå feltene for å se svaret.

Hva skriver dette programmet ut? (Hvis programmet krasjer, skriv kun Error)

print( (1.2 < 1.19) or (9 > 99) )
print( 'aaa' > 'bbb' )
print( 100 != 100 )
def wut(x):
  if x[0] == 'a':
    if 'c' in x[2]:
      if x[1] == 5:
        return True
      elif x[1] != 10:
        y = str(x[1])
        return 'y'
      else:
        if len(x) == 3:
          y = x[1] + x[2]
          return y
        else:
          return x[1]

result = wut(['a', '123', 'bc'])
print(result)

Hva skriver dette programmet ut (hvis programmet krasjer, skriv kun Error)?

For å se fasit, klikk «kjør».

def foxtrot(x):
  if x >= 10:
    return 10
  else:
    x += 10
    if x > 10:
      if x % 2 == 0:
        x += 1
      elif x >= 15:
        x -= 1
    else:
      return 42
  return x - 10

result = foxtrot(4)
print(result)

Hva skriver dette programmet ut (hvis programmet krasjer, skriv kun Error)?

For å se fasit, klikk «kjør».

def add(a, b, c):
  val = 0
  if (a < b) and (a < c):
    val = b + c
    return val
  elif (b < a) and (b < c):
    val = a + c
    return val
  else:
    val = a + b
    return val
  print(val)

result = add(50, 17, 24)
print(result)

Hva skriver dette programmet ut (hvis programmet krasjer, skriv kun Error)?

For å se fasit, klikk «kjør».

def calculate(x):
  x = x + x
  x = x + x 
  x = x + x 
  return x

Hvilken av følgende kan brukes til å erstatte innholdet i funksjonen calculate slik at den modifisere versjonen av calculate returnerer det samme resultatet som den opprinnelige versjonen for alle verdier av x?

  • return 4 * x
  • return 3 * x
  • return 6 * x
  • return 8 * x

Svar : return 8 * x

n = 0
ls = [1, 2, 3]
for x in ls:
  for y in ls:
    if y % 2 == 0:
      continue
    n = n + 1
print(n)

Hva skriver dette programmet ut (hvis programmet krasjer, skriv kun Error ) ?

For å se fasit, klikk «kjør».

Hvor mange ganger vil dette programmet skrive ut noe (hvis programmet krasjer, skriv kun Error ) ?

n = 12
while n > 0:
  print(n)
  n = n - 2

Svar: 6

For å se fasit, klikk «kjør».

x = 999
y = {
  x: 999,
  'y': x,
  1: 3
}
y[999] = 'x'
z = [1, x, y, 'z']

Anta at kodesnutten over har blitt kjørt. Hva skrives ut i disse setningene? Dersom programmet ville krasjet på linjen, skriv kun Error .

print(y[1])
print(y['x'])
print(z[1])
print(x)
print(y[z[0]])
print(y[x])

Hva skriver dette programmet ut (hvis programmet krasjer, skriv kun Error ) ?

a = "ba"
b = "ab"
a, b = b, a
print( a + b )

For å se fasit, klikk «kjør».

2 Forklaring
(2.1)  Strikkeklubb (8 poeng)

Anta at du har en fil foo.csv som inneholder oversikten over fire venners prestasjoner gjennom dette semesterets strikkeklubb.

name, item, material, days
Lina, gloves, wool, 7
Peter, strip, wool, 20
Pål, sweater, cashmir, 76
Sophie, gloves, wool, 4
Lina, shirt, limum, 55
Sophie, sweater, viscose, 61

Se den vedlagte nedefor. Hva vil innholdet i filen bar.csv være etter at koden som vises nedefor er kjørt? Beskriv filens struktur, dens betydning, og gi anbefalinger om hvordan venner kan forbedre lesbarheten til koden.

from csv import DictReader, DictWriter
from io import StringIO
from pathlib import Path

a = Path('foo.csv').read_text(encoding='utf-8')
b = DictReader(StringIO(a), delimiter=',')

c = {}

for d in b:
    e = d['name']
    f = int(d['days'])

    if e not in c:
        c[e] = 0

    c[e] += f

g = StringIO()
h = DictWriter(
    g,
    fieldnames=['n', 't'],
    lineterminator='\n'
)

h.writeheader()
h.writerows([
    {'n': k, 't': v}
    for k, v in c.items()
])

Path('bar.csv').write_text(
    g.getvalue(),
    encoding='utf-8'
)

Struktur: Filen er en CSV-fil med to kolonner: n (navn) og t (totalt antall dager). Hver rad tilsvarer én person fra foo.csv. Rekkefølgen på radene følger den rekkefølgen personene dukker opp for første gang i foo.csv (Lina, Peter, Pål, Sophie).

Betydning: Filen viser hvor mange dager totalt hver person har brukt på strikkeprojektene sine gjennom semesteret. For eksempel har Lina brukt 7 + 55 = 62 dager (hansker + skjorte), mens Sophie har brukt 4 + 61 = 65 dager.

Anbefalinger for bedre lesbarhet:

Koden bruker gjennomgående enkeltbokstavs-variabelnavn (a, b, c, d, e, f, g, h) som gjør det svært vanskelig å forstå hva koden gjør uten å lese den nøye linje for linje. Følgende endringer anbefales:

  • Gi variablene meningsfulle navn, for eksempel file_content i stedet for a, reader i stedet for b, totals i stedet for c, row i stedet for d, name i stedet for e, days i stedet for f, output i stedet for g, og writer i stedet for h.
  • Feltnavnene 'n' og 't' i bar.csv er lite beskrivende. Å bruke 'name' og 'total_days' ville gjort filen langt lettere å forstå for en som åpner den uten å kjenne koden.
  • En kort kommentar øverst i koden som forklarer hva skriptet gjør ville hjulpet leseren å orientere seg raskt.
(2.2)  Treningsprogrammet (7 poeng)

I treningsprogrammet er en utøver begrenset til fire ukentlige økter med to treningsblokker hver uke. Simulatoren for treningsprogrammet presenteres nedefor.

Simuleringen viser tilfeldigvis at utøveren trene for alltid. Hvordan forhindre dette? Forklar hvor alt gikk galt, og foreslå en løsning og hvordan du kan teste det.

current_block = 2
current_week = 2

while current_week <= 4:

    while current_block <= 2:
        print(f"Another week of training!")
        current_week += 1
    
        current_week = 0
    
    current_block += 1

print(f"Training completed. Good job!")

Koden inneholder feil som fører til at den indre løkken kjører for alltid.

current_week = 0 tilbakestiller uketelleren inne i den indre løkken.

Linjen current_week = 0 er plassert inne i den indre while-løkken. Dette betyr at hver gang en treningsblokk er gjennomført, nullstilles current_week tilbake til 0 — like etter at den ble økt med 1. current_week vil derfor aldri nå verdien 5, og den ytre løkkens betingelse (current_week <= 4) vil aldri bli usann. Linjen current_week = 0 skal fjernes.

Forslag til rettet kode:

current_week = 1

while current_week <= 4:
    current_block = 1

    while current_block <= 2:
        print(f"Week {current_week}, block {current_block}")
        current_block += 1

    print(f"Another week of training!")
    current_week += 1

print(f"Training completed. Good job!")

3 Kodeskriving
(3.1)  Lister i minnet (5 poeng)
Illustrasjon av variabler og minnets tilstand slik det skal gjenskapes

Skriv en kodesnutt slik at minnets tilstand blir som vist over. Du vil ikke få trekk i poeng dersom du definerer andre variabler i tilegg.

Klikk på «se steg» -knappen for å verifisere at denne koden gir riktig bilde av minnet.

a = ["a", "b"]
b = ["a", ["a", "b"], a]

test cases:

points = sum([
    a == ["a", "b"],
    b == ["a", ["a", "b"], ["a", "b"]],
    b[2] is a,
    (b[1] == a) and (b[1] is not a),
    (b[1] is not b[2]),
])
(3.2)  Forgasser (10 poeng)

En forgasser har som oppgave å blande drivstoff og luft i forbrenningsmotorer. Den fysiske mekanismen er beskrevet av Bernoullis ligning, som sier at følgende størrelse er konstant langs en strømlinje:

$$ \rho v^2 + \rho g h + P = const $$

Der:

  • $\rho$ (gresk “rho”) er væsketetthet,
  • $v$ er strømningshastighet,
  • $h$ er høyden til væskeelementet og
  • $P$ er statisk trykk.

Når dysene i forgasseren tettes til, faller strømningshastigheten. Ifølge Bernoullis prinsipp fører dette til at det statiske trykket stiger — summen avviker fra sin forventede konstante verdi, blandingen blir mager og motoren går ustabilt.

Et diagnosesystem registrerer et sett med strømningsmålinger for hver forgasserer under en testkjøring og sammenligner Bernoulli-summen med en kjent optimal verdi. En forgasser flagges for rengjøring dersom avviket overstiger en akseptabel terskel.

Skriv en funksjon check_carburetor() som henter oppslagsverk over forgassersmålinger og returnerer en list med forgassersartikler som krever rengjøring. Forgasseren bør anses som ugyldig hvis den faktiske verdien er større eller mindre enn forgasseren optimale verdi pluss eller minus en viss terskelverdi på 500. Ta 9.81 som en gravitasjonskonstant.

metrics = {
    "KRB-001": {"rho": 750, "v": 2.0, "h": 0.05, "P": 101325, "optimal": 104693},
    "KRB-002": {"rho": 750, "v": 1.6, "h": 0.05, "P": 103200, "optimal": 104693},
    "KRB-003": {"rho": 750, "v": 2.1, "h": 0.05, "P": 101100, "optimal": 104693},
    "KRB-004": {"rho": 750, "v": 1.5, "h": 0.05, "P": 103800, "optimal": 104693}, 
    "KRB-005": {"rho": 750, "v": 1.9, "h": 0.05, "P": 101500, "optimal": 104693}
}
G = 9.81
THRESHOLD = 500

def bernoulli_sum(rho, v, h, P):
    return rho * v**2 + rho * G * h + P

def check_carburetor(metrics):

    clogged = []

    for article, m in metrics.items():
        actual = bernoulli_sum(m["rho"], m["v"], m["h"], m["P"])
        deviation = abs(actual - m["optimal"])

        if deviation > THRESHOLD:
            clogged.append(article)

    return clogged

print(check_carburetor(metrics)) 

test cases:

  • check_carburetor({"KRB-001": {"rho": 750, "v": 2.0, "h": 0.05, "P": 101325, "optimal": 104693}})

    • []
  • check_carburetor({"KRB-002": {"rho": 750, "v": 1.6, "h": 0.05, "P": 103200, "optimal": 104693}})

    • ['KRB-004']
  • check_carburetor({"KRB-003": {"rho": 750, "v": 2.1, "h": 0.05, "P": 101100, "optimal": 104693}})

    • []
  • check_carburetor({"KRB-004": {"rho": 750, "v": 1.5, "h": 0.05, "P": 103800, "optimal": 104693}})

    • ['KRB-004']
  • check_carburetor({"KRB-005": {"rho": 750, "v": 1.9, "h": 0.05, "P": 101500, "optimal": 104693}})

    • []
(3.3)  Kalender (15 poeng)

Enterprenørfirma bruker en standardmetode for å beregne justerert forsendelsesdato for bestillinger. Regelen fungerer slik: ta datoen da bestillingen ble mottatt, legg til 7 dager (behandlingstid), og trekk deretter fra 3 måneder (sesongbasert etterslepsjustering).

Skriv en funksjon calc_dispatch_date() med en parameter dato i “dd.mm” formatet. La funksjonen ta mottaksdatoen for bestillingen og returnere den gyldige justerte forsendelsesdatoen. Alle datoer er av strengtypen.

Sørg for at datoene er gyldige. Du kan anta at året ikke er et skuddår.

Kalender av 2026 som en eksempel av hvis måneder har 30/31 dager
# get last month's day
def check_ddmm_correct(month):

    if month == 2:
        last_m_day = 28
    elif month in (1, 3, 5, 7, 8, 10, 12):
        last_m_day = 31
    else:
        last_m_day = 30

    return last_m_day

# check that this day is possible for that month
def check_overmonth(day, month):

    overflow = day // (check_ddmm_correct(month) + 1)   # 1 if overflowed, else 0
    day = day % check_ddmm_correct(month) or check_ddmm_correct(month) # keep last day if remainder is 0 
    month += overflow
    month = month % 12 or 12

    return day, month

# calculate exp due date
def calc_dispatch_date(dato):

    day, month = dato.split(".")

    day = int(day)
    month = int(month)
    
    day += 7                                        # step 1 
    day, month = check_overmonth(day, month)
    month = month - 3                               # step 2 
    day, month = check_overmonth(day, month)

    return f"{day}.{month}"

test cases:

  • “24.05” - “3.3” (overflow to march)

  • “25.03” - “1.1” (overflow to april)

  • “31.12” - “7.10”

  • “21.05” - “28.02”

  • “22.05” - “1.3”