Eksamen: REA3049-PY | Semester: Vår 2024 | Varighet: 5 timer | Tema: OOP, pseudokode, Fibonacci, kalkulator, deepfakes, tidsbruk-datasett, Game of Life
En klasse definerer hvilke egenskaper og metoder objektene skal ha — selve objektet er en instans av klassen. De andre alternativene blander begrepene: et objekt er en instans av klassen (ikke omvendt), en klasse er ikke en funksjon, og en klasse handler om mer enn bare datalagring.
Når B arver fra A, er B en mer spesialisert versjon av A — B utvider A med ny eller endret funksjonalitet. Generaliseringen går motsatt vei: A er generaliseringen av B (det felles, abstrakte). Termene «assosiert» og «avhengig» beskriver andre relasjonstyper og passer ikke for arv.
| a | DISPLAY c | b etter DECREMENT | c etter c+b |
|---|---|---|---|
| 2 | 5 | 3 | 5+3 = 8 |
| 3 | 8 | 2 | 8+2 = 10 |
| 4 | 10 | 1 | 10+1 = 11 |
Display-sekvensen blir 5, 8, 10. Verdien 11 vises ikke fordi DISPLAY skjer før de to siste operasjonene i siste iterasjon.
SET num to -2
FOR hver num LESSER THAN OR EQUAL TO 2
IF num LESSER THAN 0
IF num % 2 EQUAL TO 0
DISPLAY num + " er negativt og partall"
ELSE
DISPLAY num + " er negativt og oddetall"
ENDIF
ELSE IF num EQUAL TO 0
DISPLAY num + " er null"
ELSE
IF num % 2 EQUAL TO 0
DISPLAY num + " er positivt og partall"
ELSE
DISPLAY num + " er positivt og oddetall"
ENDIF
ENDIF
ENDFOR
Den ytre FOR-løkken iterer fra -2 til 2. Den ytre IF-strukturen skiller på fortegn (negativ / null / positiv), og de to indre IF-strukturene skiller på partall/oddetall innenfor hver gren. ENDIF-ene speiler nestingen.
Sekvensen er Fibonacci-tall: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55. a[10] = 55.
Algoritmen genererer Fibonacci-tallene. Hvert nytt tall er summen av de to foregående, med startverdiene 0 og 1. Dette er en av de mest kjente sekvensene i matematikk og dukker opp i naturen (blomsterblader, kongleskjell, kanin-populasjoner). Algoritmen bruker iterativ tabulering: vi fyller opp en liste fra bunnen og opp, slik at vi unngår den eksponensielle tidsbruken man får ved naiv rekursjon. Tidskompleksiteten er O(n), plass O(n).
# oppgave5c.py — første ti Fibonacci-partall
def fibonacci_partall(antall: int = 10) -> list[int]:
a, b = 0, 1
partall = []
while len(partall) < antall:
if a % 2 == 0:
partall.append(a)
a, b = b, a + b
return partall
if __name__ == "__main__":
print(fibonacci_partall())
# [0, 2, 8, 34, 144, 610, 2584, 10946, 46368, 196418]
len(partall) < 10? → JA: er a % 2 == 0? → JA: legg a i partall → uansett: oppdater (a, b) ← (b, a+b) → tilbake til LOOP. NEI: SKRIV partall → Slutt.
# kalkulator.py
class KalkulatorFeil(Exception):
"""Egendefinert feil for kalkulator-operasjoner."""
class Kalkulator:
def pluss(self, a: float, b: float) -> float:
return a + b
def minus(self, a: float, b: float) -> float:
return a - b
def gange(self, a: float, b: float) -> float:
return a * b
def dele(self, a: float, b: float) -> float:
if b == 0:
raise KalkulatorFeil("Kan ikke dele på null.")
return a / b
def test_kalkulator() -> None:
k = Kalkulator()
assert k.pluss(2, 3) == 5
assert k.minus(10, 4) == 6
assert k.gange(3, 7) == 21
assert k.dele(20, 4) == 5
# Feil 1: Deling på null
try:
k.dele(10, 0)
except KalkulatorFeil as e:
print(f"OK – fanget: {e}")
# Feil 2: Ikke-tall input (TypeError fra Python selv)
try:
k.pluss("to", 3) # type: ignore[arg-type]
except TypeError as e:
print(f"OK – fanget TypeError: {e}")
# Feil 3: Overflow (oppstår sjelden i Python pga. arbitrary precision int)
# I språk med fast int-størrelse må man være obs på dette.
print("Alle tester passert.")
if __name__ == "__main__":
test_kalkulator()
Identifiserte feil/unntak: (1) ZeroDivisionError ved dele(x, 0) – håndtert med KalkulatorFeil. (2) TypeError hvis brukeren sender inn ikke-numeriske argumenter – Python kaster den selv. (3) OverflowError kan oppstå i andre språk; i Python støtter int arbitrær størrelse, men float kan gi inf ved svært store tall.
| Spm. | Riktig svar | Begrunnelse |
|---|---|---|
| a) Hva er deepfakes? | manipulerte medier | Deepfakes er video, lyd eller bilde manipulert med KI/maskinlæring. |
| b) Mulig konsekvens? | økt risiko for politisk manipulering | Som artikkelen viser (Sivertsen, Grut) – deepfakes brukes for å sverte politiske motstandere og spre desinformasjon. |
| c) Hvordan avsløre? | ved å bruke teknikker fra kildekritikk | Sjekke avsender, søke etter samme video andre steder, vurdere sannsynlighet, lete etter visuelle artefakter. |
Hvordan avsløre: I jobbintervjuet kan jeg trekke fram konkrete teknikker for å avsløre deepfake-svindel: lytte etter unaturlige pauser, robotiske intonasjoner og lyd som ikke matcher leppene perfekt. På video bør man se etter ujevn belysning på ansiktet, kanter mellom ansikt og bakgrunn som «flimrer», unaturlige blunkemønstre og hender/ører som ikke ser riktige ut. Ved kritiske beslutninger – særlig økonomiske transaksjoner – bør man alltid verifisere gjennom en annen kanal: ringe tilbake på et kjent telefonnummer, sjekke gjennom intern chat, eller kreve fysisk møte.
Hva bedriften kan gjøre forebyggende:
Bedriften beskriver en prosess der deepfakes brukes i intervjuet for å vurdere kandidatens reaksjoner. Dette reiser flere etiske spørsmål.
Argumenter for: Realistiske press-scenarier kan avsløre faglig modenhet og sosial kompetanse bedre enn et tradisjonelt intervju. I noen yrker (sikkerhet, ledelse, krisehåndtering) er nettopp evnen til å håndtere uventede situasjoner kritisk. Hvis prosessen er åpen og kandidaten har samtykket, kan det være et legitimt verktøy.
Argumenter mot:
Konklusjon: Deepfakes i ansettelsesprosesser kan ha legitime formål, men forutsetter eksplisitt samtykke, klare retningslinjer for sletting av biometriske data, og en grundig etisk evaluering før innføring. Uten disse rammene er ulempene større enn fordelene.
# oppgave9.py — analyse av SSB-tidsbruk-datasett (2000)
import csv
import matplotlib.pyplot as plt
from pathlib import Path
DATAFIL = Path("tidsbruk_2000.csv")
def tid_til_minutter(verdi: str) -> int:
"""Konverter f.eks. '7.30' (7 timer, 30 min) til 450 minutter."""
if not verdi:
return 0
timer, _, minutter = verdi.partition(".")
return int(timer) * 60 + (int(minutter) if minutter else 0)
def minutter_til_tid(minutter: int) -> str:
h, m = divmod(minutter, 60)
return f"{h}.{m:02d}"
def les_data(filsti: Path = DATAFIL) -> list[dict]:
with filsti.open(encoding="utf-8") as f:
return list(csv.DictReader(f, delimiter=";"))
def vis_tabell(data: list[dict], kjonn: str = "Alle") -> None:
rader = data if kjonn == "Alle" else [r for r in data if r["Kjønn"] == kjonn]
print(f"\n{'Aktivitet':<28}{'Kjønn':<10}{'Tidsbruk':>10}")
print("-" * 48)
for r in rader:
print(f"{r['Aktivitet']:<28}{r['Kjønn']:<10}{r['Tidsbruk']:>10}")
def stolpediagram(data: list[dict], kjonn: str) -> None:
# Bruk kun aktivitetene (ikke kategoriene som er summer)
rader = [r for r in data if r["Kjønn"] == kjonn and r.get("Type", "aktivitet") == "aktivitet"]
navn = [r["Aktivitet"] for r in rader]
minutter = [tid_til_minutter(r["Tidsbruk"]) for r in rader]
plt.figure(figsize=(10, 6))
plt.bar(navn, minutter, color="#1976D2")
plt.title(f"Tidsbruk per aktivitet — {kjonn}")
plt.ylabel("Minutter per dag")
plt.xticks(rotation=45, ha="right")
plt.tight_layout()
plt.savefig(f"stolpe_{kjonn.lower()}.png", dpi=120)
plt.show()
def sektordiagram(data: list[dict], kjonn: str) -> None:
# Bruk kategoriene (med summer)
rader = [r for r in data if r["Kjønn"] == kjonn and r.get("Type") == "kategori"]
navn = [r["Aktivitet"] for r in rader]
minutter = [tid_til_minutter(r["Tidsbruk"]) for r in rader]
plt.figure(figsize=(8, 8))
plt.pie(minutter, labels=navn, autopct="%1.1f%%", startangle=90)
plt.title(f"Andel av døgnet per kategori — {kjonn}")
plt.axis("equal")
plt.tight_layout()
plt.savefig(f"sektor_{kjonn.lower()}.png", dpi=120)
plt.show()
def main() -> None:
data = les_data()
kjonn = input("Velg kjønn (Alle / Menn / Kvinner): ").strip().capitalize()
if kjonn not in {"Alle", "Menn", "Kvinner"}:
print("Ugyldig valg, viser Alle.")
kjonn = "Alle"
vis_tabell(data, kjonn)
if kjonn != "Alle":
stolpediagram(data, kjonn)
sektordiagram(data, kjonn)
if __name__ == "__main__":
main()
tid_til_minutter konverterer til minutter for korrekte beregninger og diagrammer. Skill mellom kategorier (med innrykk) og enkelt-aktiviteter ved å lese inn en ekstra kolonne Type, eller ved å filtrere på navn-prefiks.
# game_of_life.py — Conways Game of Life (objektorientert)
from __future__ import annotations
import random
import tkinter as tk
RUTER = 30
CELLE_PX = 18
START_LEVENDE_SANNSYNLIGHET = 1 / 3
TICK_MS = 200
class Celle:
def __init__(self, rad: int, kol: int, levende: bool = False) -> None:
self.rad = rad
self.kol = kol
self.levende = levende
def veksle(self) -> None:
self.levende = not self.levende
class Spillebrett:
def __init__(self, rader: int = RUTER, kolonner: int = RUTER) -> None:
self.rader = rader
self.kolonner = kolonner
self.celler: list[list[Celle]] = [
[Celle(r, k, random.random() < START_LEVENDE_SANNSYNLIGHET)
for k in range(kolonner)]
for r in range(rader)
]
def tom(self) -> None:
for rad in self.celler:
for c in rad:
c.levende = False
def _levende_naboer(self, c: Celle) -> int:
antall = 0
for dr in (-1, 0, 1):
for dk in (-1, 0, 1):
if dr == 0 and dk == 0:
continue
r, k = c.rad + dr, c.kol + dk
if 0 <= r < self.rader and 0 <= k < self.kolonner:
if self.celler[r][k].levende:
antall += 1
return antall
def neste_generasjon(self) -> None:
# Beregn ny status for alle celler simultant.
ny: list[list[bool]] = [[False] * self.kolonner for _ in range(self.rader)]
for rad in self.celler:
for c in rad:
naboer = self._levende_naboer(c)
if c.levende:
ny[c.rad][c.kol] = naboer in (2, 3)
else:
ny[c.rad][c.kol] = naboer == 3
for rad in self.celler:
for c in rad:
c.levende = ny[c.rad][c.kol]
class Spill:
def __init__(self, brett: Spillebrett) -> None:
self.brett = brett
self.kjorer = False
self.root = tk.Tk()
self.root.title("Game of Life")
self.canvas = tk.Canvas(
self.root,
width=brett.kolonner * CELLE_PX,
height=brett.rader * CELLE_PX,
bg="white", highlightthickness=0,
)
self.canvas.pack()
self.canvas.bind("<Button-1>", self._klikk)
knapper = tk.Frame(self.root)
knapper.pack(pady=6)
tk.Button(knapper, text="Start/Pause", command=self._toggle).pack(side="left", padx=4)
tk.Button(knapper, text="Tøm", command=self._tom).pack(side="left", padx=4)
tk.Button(knapper, text="Steg", command=self._ett_steg).pack(side="left", padx=4)
self.celleids: dict[tuple[int, int], int] = {}
self._tegn_init()
def _tegn_init(self) -> None:
for rad in self.brett.celler:
for c in rad:
x1, y1 = c.kol * CELLE_PX, c.rad * CELLE_PX
rid = self.canvas.create_rectangle(
x1, y1, x1 + CELLE_PX, y1 + CELLE_PX,
fill=self._farge(c), outline="#bbb",
)
self.celleids[(c.rad, c.kol)] = rid
def _farge(self, c: Celle) -> str:
return "#1a2b4a" if c.levende else "#fafafa"
def _oppdater_tegning(self) -> None:
for rad in self.brett.celler:
for c in rad:
self.canvas.itemconfig(self.celleids[(c.rad, c.kol)], fill=self._farge(c))
def _klikk(self, event) -> None:
kol = event.x // CELLE_PX
rad = event.y // CELLE_PX
if 0 <= rad < self.brett.rader and 0 <= kol < self.brett.kolonner:
self.brett.celler[rad][kol].veksle()
self._oppdater_tegning()
def _toggle(self) -> None:
self.kjorer = not self.kjorer
if self.kjorer:
self._loop()
def _tom(self) -> None:
self.kjorer = False
self.brett.tom()
self._oppdater_tegning()
def _ett_steg(self) -> None:
self.brett.neste_generasjon()
self._oppdater_tegning()
def _loop(self) -> None:
if not self.kjorer:
return
self.brett.neste_generasjon()
self._oppdater_tegning()
self.root.after(TICK_MS, self._loop)
def kjor(self) -> None:
self.root.mainloop()
if __name__ == "__main__":
Spill(Spillebrett()).kjor()
Celle/Spillebrett håndterer regler; Spill-klassen håndterer Tkinter-GUI.neste_generasjon som levende → naboer in (2, 3) og død → naboer == 3.Foreslått mappestruktur (kandidatnummer.zip):
123456/
├── oppgave5c/
│ ├── fibonacci_partall.py
│ └── flytskjema.pdf
├── oppgave6/
│ └── kalkulator.py
├── oppgave9/
│ ├── oppgave9.py
│ ├── tidsbruk_2000.csv
│ └── stolpe_menn.png
├── oppgave10/
│ └── game_of_life.py
└── README.md