Tilbake
7.1

7.1 Inversjonsmetoden og numpy-verktøykassen

Fra F⁻¹ på papir til kjørbar numpy-funksjon: simuler fra en vilkårlig fordeling med inversjonsmetoden.

60 min
8 oppgaver
Inversjonsmetodennumpy-verktøykassen
Din fremgang i kapitlet
0 / 8 oppgaver
Forkunnskaper: Dette kapitlet bygger på kap. 2.3 (fordelingsfunksjonen FF og dens inverse for egendefinerte/«eksotiske» tettheter) og kap. 4.2 (CDF-metoden for transformasjon). Fra kap. 5.1 trenger du bare grunnen til at empirisk varians deles på n1n-1 (det blir ddof=1 i koden).

Sist du var her — de tre tingene du må ha friskt:

1. Fordelingsfunksjonen F(x)=P(Xx)F(x) = P(X \le x) — voksende, fra 0 til 1.
2. Den inverse F1F^{-1}: løser du F(x)=uF(x) = u for xx, får du x=F1(u)x = F^{-1}(u). For en voksende, kontinuerlig FF finnes den entydig for 0<u<10 < u < 1.
3. Eksponensialfordelingen (forventnings-parametrisering, forventning μ\mu): tetthet f(x)=1μex/μ\displaystyle f(x) = \frac{1}{\mu}e^{-x/\mu}, fordelingsfunksjon F(x)=1ex/μF(x) = 1 - e^{-x/\mu} for x>0x > 0. (Rate-parametriseringen f(x)=λeλxf(x) = \lambda e^{-\lambda x}, F(x)=1eλxF(x) = 1 - e^{-\lambda x} bruker vi der oppgaven gjør det — les alltid oppgavens egen definisjon.)

Tenk deg at du vil vite hvor ofte en komponent med tilfeldig levetid varer i mer enn 8 år, eller hva forventet verdi av en komplisert funksjon av flere variable er — men integralet er stygt eller finnes ikke i lukket form. I stedet for å regne kan du la datamaskinen «kaste terning» én million ganger og telle. Det er hele idéen bak simulering: en sannsynlighet er en andel, og en forventning er et gjennomsnitt, så trekker vi mange nok realisasjoner, leser vi svaret rett ut av tallene.

Men datamaskinen kan i utgangspunktet bare trekke uniforme tall mellom 0 og 1 (np.random.uniform). Spørsmålet blir da: hvordan lager vi tall fra vår fordeling — eksponensial, Pareto, Rayleigh — ut fra uniforme tall? Svaret er inversjonsmetoden, og den er kapitlets første halvdel. Andre halvdel er numpy-verktøykassen: de få funksjonene og idiomene sensor forventer å se, og hvordan du leser en simuleringssnutt.

Kapitlet er bygget som fire korte læringsløkker — teori → gjennomregnet eksempel → øvingsoppgave — så du kan ta én om gangen. Med fire løkker à ~13 min passer det med en kort pause etter løkke 2.

Løkke 1 — Inversjonsmetoden (~15 min)

📜Inversjonsmetoden
La UU være uniformt fordelt på (0,1)(0,1), og la FF være en voksende, kontinuerlig fordelingsfunksjon med invers F1F^{-1}. Da har den transformerte variabelen

X=F1(U)X = F^{-1}(U)

nettopp fordelingsfunksjonen FF. Med andre ord: trekker du et uniformt tall uu og setter det inn i F1F^{-1}, får du en realisasjon fra fordelingen med fordelingsfunksjon FF.

Beviset er tre linjer. Vi regner ut fordelingsfunksjonen til X=F1(U)X = F^{-1}(U) direkte:

P(Xx)=P(F1(U)x)=P(UF(x))=F(x).P(X \le x) = P\big(F^{-1}(U) \le x\big) = P\big(U \le F(x)\big) = F(x).

Intuisjon steg 1: Vi bruker at FF er voksende, så F1(U)xF^{-1}(U) \le x er det samme som UF(x)U \le F(x) (vi «kjører FF på begge sider»).

Intuisjon steg 2: Siden UU er uniform på (0,1)(0,1), er P(Uc)=cP(U \le c) = c for enhver cc mellom 0 og 1 — og F(x)F(x) ligger nettopp mellom 0 og 1. Derfor blir P(UF(x))=F(x)P(U \le F(x)) = F(x).

Intuisjon steg 3: Resultatet P(Xx)=F(x)P(X \le x) = F(x) er per definisjon fordelingsfunksjonen til XX. Ferdig — XX har fordelingen vi ville ha.

Arbeidsflyten blir dermed alltid den samme:

1. Skaff fordelingsfunksjonen F(x)F(x) (utledet i kap. 2.3).
2. Løs likningen F(x)=uF(x) = u for xx — det gir formelen x=F1(u)x = F^{-1}(u).
3. Implementer: trekk uniforme uu-er og sett dem inn i formelen.

✏️Eksempel 1: Inversjon for eksponensialfordelingen

Levetiden XX (i år) til en sensor er eksponensialfordelt med forventning μ=5\mu = 5, altså F(x)=1ex/μF(x) = 1 - e^{-x/\mu} for x>0x > 0. Utled inversjonsformelen F1(u)F^{-1}(u), og skriv en Python-funksjon som trekker nn realisasjoner. Kontroller at gjennomsnitt og empirisk varians stemmer med teorien (E(X)=μ=5E(X) = \mu = 5, Var(X)=μ2=25\text{Var}(X) = \mu^2 = 25).

Steg 2 — løs F(x)=uF(x) = u for xx:

1ex/μ=u    ex/μ=1u    xμ=ln(1u)    x=μln(1u).1 - e^{-x/\mu} = u \;\Rightarrow\; e^{-x/\mu} = 1 - u \;\Rightarrow\; -\frac{x}{\mu} = \ln(1-u) \;\Rightarrow\; x = -\mu\ln(1-u).

F1(u)=μln(1u)F^{-1}(u) = -\mu\ln(1-u).

Steg 3 — implementer og kontroller:

import numpy as np

def simuler_eksp(n, mu):
    u = np.random.uniform(size=n)
    return -mu * np.log(1 - u)

np.random.seed(1)
x = simuler_eksp(10**6, 5.0)
print(np.mean(x), np.var(x, ddof=1))
# 4.9971...  24.9402...

Gjennomsnittet ligger på 5,00\approx 5{,}00 og den empiriske variansen på 24,94\approx 24{,}94 — akkurat de teoretiske verdiene μ=5\mu = 5 og μ2=25\mu^2 = 25, med litt Monte Carlo-støy. Koden kjører og bekrefter at inversjonsformelen er riktig.

Merk ddof=1: np.var(x, ddof=1) deler på n1n-1 og gir den forventningsrette empiriske variansen (kobling kap. 5.1). Standard np.var deler på nn; med n=106n = 10^6 er forskjellen mikroskopisk, men sensor vil se ddof=1 der du estimerer en varians.

📝Oppgave 1

(Innsteg.) En variabel XX har fordelingsfunksjon F(x)=1ex/μF(x) = 1 - e^{-x/\mu} for x>0x > 0 (eksponensial, forventning μ\mu). Løs likningen F(x)=uF(x) = u for xx, og skriv ned inversjonsformelen F1(u)F^{-1}(u).

📝Oppgave 2

En oppgave bruker rate-parametriseringen av eksponensialfordelingen: f(x)=λeλxf(x) = \lambda e^{-\lambda x}, altså F(x)=1eλxF(x) = 1 - e^{-\lambda x} for x>0x > 0, med rate λ\lambda.

a) Utled inversjonsformelen F1(u)F^{-1}(u) i denne parametriseringen.

b) Skriv en Python-funksjon simuler(n, lam) som trekker nn realisasjoner, og forklar hvorfor svaret er konsistent med forventnings-formen x=μln(1u)x = -\mu\ln(1-u) når μ=1/λ\mu = 1/\lambda.

Løkke 2 — numpy-verktøykassen (~15 min)

— naturlig pausepunkt etter denne løkka —

Inversjonsmetoden lager tallene; numpy og scipy.stats regner på dem. Her er hele verktøykassen sensor forventer — det er ikke mange funksjoner:

- np.random.uniform(size=n) — trekker nn uniforme tall på (0,1)(0,1). Dette er råstoffet til inversjonsmetoden.
- stats.norm.rvs(mu, sigma, size=n) — trekker nn normalfordelte tall. Advarsel: annet argument er STANDARDAVVIKET σ\sigma, ikke variansen. Skriver du variansen der, får du feil spredning (en klassisk sensorfelle). For XN(μ,σ2)X \sim N(\mu, \sigma^2) med σ2=9\sigma^2 = 9 skriver du stats.norm.rvs(mu, 3, size=n) — altså 3, ikke 9.
- np.mean(x) — gjennomsnittet, estimatet av E(X)E(X).
- np.var(x, ddof=1) og np.std(x, ddof=1) — empirisk varians og standardavvik. ddof=1 betyr «del på n1n-1», som gir den forventningsrette variansen (kobling kap. 5.1). Uten ddof=1 deler numpynn og gir en litt for liten varians.
- Vektorisering. Skriv -mu * np.log(1 - u) på hele vektoren u på én gang — ikke en for-løkke over enkeltelementer. Vektorisert kode er kortere, raskere og det sensor forventer.

Funksjonsmønsteret som går igjen:

def simuler(n):
    u = np.random.uniform(size=n)
    return ...   # inversjonsformelen anvendt vektorisert på u

✏️Eksempel 2: Trekke normalfordelte tall — pass på argumentet

Konsentrasjonen YY (mg/l) av et stoff er N(μ,σ2)N(\mu, \sigma^2) med forventning μ=10\mu = 10 og varians σ2=4\sigma^2 = 4. Trekk 10610^6 realisasjoner med stats.norm.rvs, og kontroller gjennomsnitt og empirisk standardavvik. Hva er den vanligste feilen her?

Variansen er σ2=4\sigma^2 = 4, så standardavviket er σ=4=2\sigma = \sqrt{4} = 2. Det er 22 — ikke 44 — som skal inn som annet argument:

import numpy as np
from scipy import stats

np.random.seed(3)
y = stats.norm.rvs(10, 2, size=10**6)   # 2 = standardavviket (ikke variansen 4!)
print(np.mean(y), np.std(y, ddof=1))
# 10.0039...  1.9966...

Gjennomsnittet ligger på 10\approx 10 og empirisk standardavvik på 2=σ\approx 2 = \sigma.

Den vanligste feilen er å skrive stats.norm.rvs(10, 4, ...) — da sender du variansen der funksjonen vil ha standardavviket, og får spredning σ=4\sigma = 4 i stedet for 22. Empirisk standardavvik ville da blitt 4\approx 4, og alle etterfølgende sannsynligheter feil. Les alltid dokumentasjonen: scale = standardavvik.

📝Oppgave 3

En medstudent vil trekke fra N(μ,σ2)N(\mu, \sigma^2) med μ=100\mu = 100 og varians σ2=25\sigma^2 = 25, og skriver stats.norm.rvs(100, 25, size=n). Forklar hva som er galt, og skriv den korrekte linjen.

📝Oppgave 4

Skriv om denne løkke-baserte koden til vektorisert numpy, og forklar én fordel utover hastighet:

x = []
for i in range(n):
    u = np.random.uniform()
    x.append(-mu * np.log(1 - u))

Løkke 3 — Haler og flere fordelinger (~15 min)

Inversjonsmetoden virker for enhver fordeling der du klarer å løse F(x)=uF(x) = u. De «eksotiske» fordelingene fra kap. 2.3 gir alle pene inversjonsformler:

- Pareto (hale, minsteverdi kk, formparameter θ\theta): F(x)=1(k/x)θF(x) = 1 - (k/x)^{\theta} for x>kx > k, som gir x=k(1u)1/θx = k(1-u)^{-1/\theta}.
- Weibull (skala β\beta, form α\alpha): F(x)=1e(x/β)αF(x) = 1 - e^{-(x/\beta)^{\alpha}}, som gir x=β(ln(1u))1/αx = \beta\big(-\ln(1-u)\big)^{1/\alpha}.
- Rayleigh (skala σ\sigma): F(x)=1ex2/(2σ2)F(x) = 1 - e^{-x^2/(2\sigma^2)}, som gir x=σ2ln(1u)x = \sigma\sqrt{-2\ln(1-u)}.

Hver av dem er samme tre-stegs oppskrift: skaff FF, løs for xx, implementer vektorisert.

✏️Eksempel 3: Pareto-hale og et sannsynlighetsestimat

Bølgehøyden XX (i meter) har tetthet f(x)=4/x5f(x) = 4/x^5 for x>1x > 1. Finn FF og F1F^{-1}, skriv en Python-funksjon som trekker nn realisasjoner, og bruk den til å estimere P(X>2)P(X > 2). Sammenlign med den eksakte verdien.

Fordelingsfunksjon. Dette er Pareto med k=1k = 1, θ=4\theta = 4 (siden f(x)=θkθ/xθ+1=4/x5f(x) = \theta k^{\theta}/x^{\theta+1} = 4/x^5). Integrerer vi:

F(x)=1x4t5dt=[t4]1x=1x4,x>1.F(x) = \int_1^x \frac{4}{t^5}\,dt = \Big[-t^{-4}\Big]_1^x = 1 - x^{-4}, \qquad x > 1.

Invers. 1x4=ux4=1ux=(1u)1/41 - x^{-4} = u \Rightarrow x^{-4} = 1 - u \Rightarrow x = (1-u)^{-1/4} — altså x=k(1u)1/θx = k(1-u)^{-1/\theta} med k=1,θ=4k=1, \theta=4.

Eksakt P(X>2)P(X > 2): P(X>2)=1F(2)=24=1/16=0,0625P(X > 2) = 1 - F(2) = 2^{-4} = 1/16 = 0{,}0625.

Kode:

import numpy as np

def simuler_pareto(n, k, theta):
    u = np.random.uniform(size=n)
    return k * (1 - u)**(-1/theta)

np.random.seed(2)
x = simuler_pareto(10**6, 1.0, 4.0)
print(np.mean(x > 2))
# 0.06287...

Her er x > 2 et array med True/False; np.mean av det er andelen True — altså estimatet av P(X>2)P(X > 2). Vi får 0,0629\approx 0{,}0629, mot den eksakte 0,06250{,}0625. Stemmer.

📝Oppgave 5

Vindstyrken XX er Rayleigh-fordelt med skala σ\sigma: F(x)=1ex2/(2σ2)F(x) = 1 - e^{-x^2/(2\sigma^2)} for x>0x > 0.

a) Utled inversjonsformelen F1(u)F^{-1}(u).

b) Skriv en Python-funksjon simuler_rayleigh(n, sigma) og bruk den (med σ=3\sigma = 3) til å estimere forventningen. Den teoretiske er E(X)=σπ/2E(X) = \sigma\sqrt{\pi/2} — stemmer estimatet?

📝Oppgave 6

Levetiden XX (i tusen timer) til en fiber er Weibull-fordelt: F(x)=1e(x/β)αF(x) = 1 - e^{-(x/\beta)^{\alpha}} for x>0x > 0, med form α=1,5\alpha = 1{,}5 og skala β=4\beta = 4.

a) Utled F1(u)F^{-1}(u) og skriv en simuleringsfunksjon.

b) Bruk simulering til å estimere medianen, og sammenlign med den eksakte medianen β(ln2)1/α\beta(\ln 2)^{1/\alpha}.

c) Forklar kort hvorfor inversjonsmetoden fungerer like godt her som for eksponensial, selv om Weibull ikke har «minneløshet».

Løkke 4 — Kodelesing som ferdighet (~15 min)

Eksamen ber deg ikke bare skrive kode — den ber deg lese en snutt og svare på to spørsmål: (1) hvilken fordeling trekkes det fra, og (2) hva estimeres? Framgangsmåten:

1. Finn inversjonsformelen i koden og kjenn den igjen. -mu*np.log(1-u) er eksponensial; k*(1-u)**(-1/theta) er Pareto; sigma*np.sqrt(-2*np.log(1-u)) er Rayleigh.
2. Les den siste linjen — estimandet. np.mean(x) estimerer E(X)E(X); np.var(x, ddof=1) estimerer Var(X)\text{Var}(X); np.mean(x > a) estimerer P(X>a)P(X > a) (gjennomsnitt av en indikator er en sannsynlighet); np.mean(g(x)) estimerer E[g(X)]E[g(X)].

Nøkkelen til punkt 2: en sannsynlighet er en forventet indikator. x > a er en vektor av True/False (1/0), og gjennomsnittet av den er andelen som oppfyller betingelsen — altså et estimat av P(X>a)P(X > a).

✏️Eksempel 4: Les snutten — fordeling og estimand

Hva trekker denne koden fra, og hva estimerer siste linje?

import numpy as np
np.random.seed(4)
u = np.random.uniform(size=10**6)
x = -5 * np.log(1 - u)
print(np.mean(x > 2))

Fordeling: Inversjonsformelen -5*np.log(1-u) er μln(1u)-\mu\ln(1-u) med μ=5\mu = 5 — altså eksponensialfordeling med forventning μ=5\mu = 5.

Estimand: x > 2 er en indikatorvektor, og np.mean av den er andelen realisasjoner større enn 2 — et estimat av P(X>2)P(X > 2).

Kontroll: For eksponensial med forventning μ=5\mu = 5 er P(X>2)=e2/5=e0,40,6703P(X > 2) = e^{-2/5} = e^{-0{,}4} \approx 0{,}6703.

# kjører koden over gir:
# 0.67048...
# mot eksakt e^{-0.4} = 0.67032...

Svaret: koden estimerer P(X>2)0,670P(X > 2) \approx 0{,}670 for en eksponensialfordeling med forventning 5.

📝Oppgave 7

(Kodelesing.) Hva trekker koden fra, og hva estimerer siste linje? Gi også den eksakte verdien.

import numpy as np
np.random.seed(0)
u = np.random.uniform(size=10**6)
x = 1 * (1 - u)**(-1/4)
print(np.mean(x))

📝Oppgave 8

(Kodelesing + feilsøk.) En student vil estimere P(X>8)P(X > 8) for en eksponensialfordeling med forventning μ=5\mu = 5 og skriver:

import numpy as np
u = np.random.uniform(size=10**6)
x = -5 * np.log(u)
print(np.mean(x > 8))

a) Er koden riktig? Forklar hva -5*np.log(u) trekker fra.

b) Hva estimerer siste linje, og hva er den eksakte verdien den bør ligge nær?

Begrepsbank

Flashcard-/repetisjonsstoff — hopp trygt over ved førstegangslesing; tidsanslaget gjelder kjernestoffet. Kortene under samler metodene og idiomene til rask repetisjon.

Inversjonsmetoden

Å simulere fra en fordeling ved å trekke uniforme tall og sette dem inn i den inverse fordelingsfunksjonen. I symboler: er UU \sim uniform(0,1)(0,1), har X=F1(U)X = F^{-1}(U) fordelingsfunksjon FF. Arbeidsflyt: skaff FF, løs F(x)=uF(x) = u for xx, implementer.

Uniform(0,1)(0,1) som råstoff

Datamaskinens grunnleggende tilfeldighet: tall jevnt fordelt mellom 0 og 1. I kode np.random.uniform(size=n). All annen simulering (inversjonsmetoden) bygger på å transformere disse tallene. Egenskap: P(Uc)=cP(U \le c) = c for 0c10 \le c \le 1.

Fordelingsfunksjonen FF (repetisjon)

Sannsynligheten for å ligge til og med et punkt: F(x)=P(Xx)F(x) = P(X \le x). Voksende, går fra 0 til 1. Inversjonsmetodens første ingrediens — den skaffes ved å integrere tettheten ff.

Invers fordelingsfunksjon F1F^{-1}

Løsningen av F(x)=uF(x) = u med hensyn på xx; kalles også kvantilfunksjonen. For en voksende, kontinuerlig FF finnes den entydig for 0<u<10 < u < 1. Det er denne du setter uniforme tall inn i.

Inversjon: eksponensial (forventning μ\mu)

For F(x)=1ex/μF(x) = 1 - e^{-x/\mu} er inversjonsformelen x=μln(1u)x = -\mu\ln(1-u). Grunnformelen i simulering — kjenn den igjen både når du skriver og leser kode.

Inversjon: eksponensial (rate λ\lambda)

For F(x)=1eλxF(x) = 1 - e^{-\lambda x} er inversjonsformelen x=1λln(1u)\displaystyle x = -\frac{1}{\lambda}\ln(1-u). Samme tall som forventnings-formen når μ=1/λ\mu = 1/\lambda; les alltid oppgavens egen parametrisering.

Inversjon: Pareto

For F(x)=1(k/x)θF(x) = 1 - (k/x)^{\theta}, x>kx > k, er inversjonsformelen x=k(1u)1/θx = k(1-u)^{-1/\theta}. Hale-fordeling med minsteverdi kk og form θ\theta.

Inversjon: Weibull

For F(x)=1e(x/β)αF(x) = 1 - e^{-(x/\beta)^{\alpha}} er inversjonsformelen x=β(ln(1u))1/αx = \beta\big(-\ln(1-u)\big)^{1/\alpha}. Skala β\beta, form α\alpha; eksponensial er spesialtilfellet α=1\alpha = 1.

Inversjon: Rayleigh

For F(x)=1ex2/(2σ2)F(x) = 1 - e^{-x^2/(2\sigma^2)} er inversjonsformelen x=σ2ln(1u)x = \sigma\sqrt{-2\ln(1-u)}. Ta den positive roten siden x>0x > 0.

`np.random.uniform(size=n)`

Trekker nn uniforme tall på (0,1)(0,1) som et array. Råstoffet til inversjonsmetoden. Argumentet size bestemmer antallet — bruk det i stedet for en løkke.

`stats.norm.rvs(mu, sigma, size=n)`

Trekker nn normalfordelte tall. Annet argument er standardavviket σ\sigma, ikke variansen σ2\sigma^2. For N(μ,σ2)N(\mu, \sigma^2) med σ2=9\sigma^2 = 9: skriv stats.norm.rvs(mu, 3, size=n).

Vektorisering

Å regne på hele arrayet på én gang — -mu*np.log(1-u) — i stedet for en for-løkke over enkeltelementer. Kortere, raskere, og det sensor forventer. np.log, *, > og lignende virker elementvis.

`np.mean(x)` som estimat av E(X)E(X)

Gjennomsnittet av realisasjonene er estimatet av forventningen. Store talls lov gjør at 1nxiE(X)\displaystyle \frac1n\sum x_i \to E(X) når nn er stor.

`np.var(x, ddof=1)`

Den empiriske variansen med divisor n1n-1 — den forventningsrette varianten (kobling kap. 5.1). Estimerer Var(X)\text{Var}(X).

`np.std(x, ddof=1)`

Det empiriske standardavviket, empirisk varians\sqrt{\text{empirisk varians}}, med divisor n1n-1. Brukes som estimat av standardavvik og som byggekloss i standardfeil.

Hvorfor `ddof=1`

«Delta degrees of freedom» = 1 gjør at det deles på n1n-1 i stedet for nn. Det gir en forventningsrett estimator av variansen; med divisor nn blir estimatet systematisk litt for lite (kobling kap. 5.1).

Sannsynlighet som gjennomsnitt av indikator

En sannsynlighet er en andel: np.mean(x > a) estimerer P(X>a)P(X > a) fordi x > a er en vektor av 1/0 (True/False) og gjennomsnittet er andelen som er sanne. Generelt estimerer np.mean(g(x)) forventningen E[g(X)]E[g(X)].

Kodelesing: fordeling + estimand

Egen ferdighet: gitt en snutt, identifiser (1) fordelingen fra inversjonsformelen i koden, og (2) estimandet fra siste linje (np.meanE(X)E(X), np.mean(x>a)P(X>a)P(X>a), np.var(...,ddof=1) → varians).

Funksjonsmønsteret `def simuler(n)`

Standardoppsettet: def simuler(n): u = np.random.uniform(size=n); return <inversjonsformel>. Gjør simuleringen gjenbrukbar og lett å teste — kall simuler(10**6) og send resultatet til np.mean osv.

1U1-U og UU som likeverdig input

Er UU \sim uniform(0,1)(0,1), er også 1U1-U \sim uniform(0,1)(0,1). Derfor gir både μln(1u)-\mu\ln(1-u) og μln(u)-\mu\ln(u) korrekte eksponensiale tall. Snarveien er vanlig, men skriv 1u1-u når du følger inversjonsformelen bokstavelig.

Repetisjonsoppgaver
Symbol- og formelliste

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.