Tilbake
9.1

9.1 Inversjonsmetoden og numpy-verktøykassen

Fra $F^{-1}$ på papir til kjørbar numpy-funksjon: simulering fra en vilkårlig fordeling med inversjonsmetoden.

60 min
12 oppgaver
Inversjonsmetodennumpy-verktøykassen
Din fremgang i kapitlet
0 / 12 oppgaver
Forkunnskaper: Dette kapitlet bygger rett på kap. 2.4 — der du drillet å gå fra en oppgitt tetthet f(x)f(x) til fordelingsfunksjonen F(x)F(x) og til kvantiler ved å løse F(xp)=pF(x_p)=p. Nettopp den ferdigheten er motoren i inversjonsmetoden. Fra kap. 4.1 tar vi med at fordelingen til en transformert variabel styres av fordelingsfunksjonen, og fra kap. 5.1 hvorfor empirisk varians deles på n1n-1 (det blir ddof=1 i numpy).

Sist du var her — de tre resultatene metoden hviler på (ferdig oppfrisket):

1. Fordelingsfunksjonen fra tettheten: F(x)=startxf(s)dsF(x)=\int_{\text{start}}^{x} f(s)\,ds, oppgitt komplett (0 under støtten, uttrykket inne, 1 over).
2. Kvantilen løser F(xp)=pF(x_p)=p — løs likningen for xx, aldri f(x)=pf(x)=p. Den inverse funksjonen F1F^{-1} er nettopp «løs F(x)=uF(x)=u for xx».
3. Halesannsynlighet: P(X>x)=1F(x)P(X>x)=1-F(x).

Matematisk trenger du å kunne integrere polynomer og potenser, løse en enkel likning for xx, og lese grunnleggende numpy. Ingen ny sannsynlighetsteori innføres her — bare et nytt verktøy for den du alt kan.

Hverdagsanker: en terning gir deg tilfeldige heltall 1–6. Men hva om du trenger tilfeldige bølgehøyder, levetider eller ventetider med en helt bestemt fordeling? Datamaskinen kan bare gi deg ett råstoff: uniformt fordelte tall mellom 0 og 1 (np.random.uniform). Inversjonsmetoden er oppskriften som gjør om det råstoffet til trekk fra hvilken som helst fordeling du kan skrive FF for — du «mater» de uniforme tallene inn i F1F^{-1}.

Kapitlet går i fire løkker: inversjonsmetoden og beviset (løkke 1), et hale-eksempel fra bunnen (løkke 2), numpy-verktøykassa med de idiomene sensor ser etter (løkke 3), og kodelesing som egen ferdighet (løkke 4). Hver løkke går teori \to eksempel \to oppgave.

Samlet kjernetid ≈ 60 min. Naturlige pausepunkter er markert mellom løkkene. All kode i kapitlet er komplett og faktisk kjørt — du kan lime den rett inn og få tallene som står.

Løkke 1 — Inversjonsmetoden og beviset (~16 min)

📜Inversjonsmetoden
La FF være en (kontinuerlig, strengt voksende) fordelingsfunksjon med invers F1F^{-1}. Er Uuniform(0,1)U\sim \text{uniform}(0,1), så har

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

nøyaktig fordelingsfunksjon FF.

Beviset i tre linjer. Vi regner ut fordelingsfunksjonen til X=F1(U)X=F^{-1}(U) direkte. Fordi FF er voksende, er F1(U)xF^{-1}(U)\le x det samme som UF(x)U\le F(x):

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

Det siste likhetstegnet er selve nøkkelen: for Uuniform(0,1)U\sim\text{uniform}(0,1) er P(Uu)=uP(U\le u)=u for u[0,1]u\in[0,1], og her er u=F(x)u=F(x). Dermed har XX fordelingsfunksjon FF — akkurat det vi ønsket. \blacksquare

Intuisjon: F(x)F(x) er «hvor stor andel av massen som ligger til venstre for xx». Trekker vi en uniform andel UU og spør hvilken xx-verdi som har akkurat den andelen til venstre for seg (det er F1(U)F^{-1}(U)), lander vi tett der massen er — altså med riktig fordeling.

Arbeidsflyten (tre steg)

Uansett fordeling følger inversjonsmetoden samme tre steg:

1. Utled FF. Fra tettheten ff: F(x)=fF(x)=\int f (teknikken fra kap. 2.4). Er FF oppgitt, hopp rett videre.

2. Løs F(x)=uF(x)=u for xx. Det gir den inverse x=F1(u)x=F^{-1}(u). Dette er samme likning som kvantilregningen — bare med en generell uu i stedet for et tall.

3. Implementer. Trekk uu med np.random.uniform(size=n) og sett vektoren inn i uttrykket for F1(u)F^{-1}(u). Ferdig funksjon.

Merk at steg 2 er hele det matematiske arbeidet — resten er én linje numpy.

✏️Eksempel 1: Eksponensialfordelingen fra $F$ til kode

Levetiden XX (i år) til en pumpe er eksponensialfordelt med forventning β=4\beta=4, altså f(x)=1βex/β\displaystyle f(x)=\frac{1}{\beta}e^{-x/\beta} for x>0x>0. Utled F1F^{-1} og skriv en Python-funksjon som trekker nn levetider med inversjonsmetoden.

Steg 1 — utled FF. For eksponensialfordelingen er
F(x)=0x1βes/βds=1ex/β,x>0.F(x)=\int_0^x \tfrac{1}{\beta}e^{-s/\beta}\,ds=1-e^{-x/\beta},\qquad x>0.

Steg 2 — løs F(x)=uF(x)=u.
1ex/β=u    ex/β=1u    xβ=ln(1u)    x=βln(1u).1-e^{-x/\beta}=u\;\Rightarrow\; e^{-x/\beta}=1-u\;\Rightarrow\; -\tfrac{x}{\beta}=\ln(1-u)\;\Rightarrow\; x=-\beta\ln(1-u).
F1(u)=βln(1u)F^{-1}(u)=-\beta\ln(1-u).

Steg 3 — implementer.

import numpy as np

def simuler_eksp(n, beta):
    u = np.random.uniform(size=n)      # råstoffet: uniform(0,1)
    return -beta * np.log(1 - u)       # F^{-1}(u), vektorisert

np.random.seed(0)
x = simuler_eksp(10**6, 4.0)
print(np.mean(x))          # ~ 4.0  = beta      (E(X) = beta)
print(np.var(x, ddof=1))   # ~ 16.0 = beta**2   (Var(X) = beta^2)

Kjørt gir dette xˉ4,005\bar{x}\approx 4{,}005 og empirisk varians 16,03\approx 16{,}03 — nettopp E(X)=β=4E(X)=\beta=4 og Var(X)=β2=16\text{Var}(X)=\beta^2=16, som bekrefter at koden trekker riktig fordeling.

> Sensornotat: ln(1u)\ln(1-u), ikke ln(u)\ln(u) — det følger direkte av steg 2. (Siden UU og 1U1-U har samme fordeling, gir βln(u)-\beta\ln(u) også riktig fordeling, men skriv 1u1-u så utledningen henger sammen.)

📝Oppgave 1

(Innstegsoppgave — bare gjengivelse.) Hva er råstoffet inversjonsmetoden starter fra, og hvilken numpy-funksjon lager det?

a) Hvilken fordeling har trekkene du starter med?

b) Hvilken kodelinje trekker nn slike tall?

📝Oppgave 2

En ventetid er eksponensialfordelt med forventning β=5\beta=5.

a) Skriv opp F(x)F(x).

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

c) Fullfør returlinjen i def simuler(n): u = np.random.uniform(size=n); return ___.

— naturlig pausepunkt —

Løkke 2 — Et hale-eksempel fra bunnen (~14 min)

✏️Eksempel 2: Bølgehøyde med hale-tetthet

Den (skalerte) signifikante bølgehøyden XX i et havområde har tetthet f(x)=5x6f(x)=\dfrac{5}{x^6} for x>1x>1 og 0 ellers. Utled FF og F1F^{-1}, skriv en simuleringsfunksjon, og forklar hva np.mean(simuler(10**6) > 2) estimerer.

Steg 1 — utled FF. Tettheten er allerede normert (15x6dx=515=1\int_1^\infty 5x^{-6}\,dx=5\cdot\tfrac{1}{5}=1). Integrerer vi fra 1:
F(x)=1x5s6ds=[s5]1x=1x5,x>1.F(x)=\int_1^x 5s^{-6}\,ds=\left[-s^{-5}\right]_1^x=1-x^{-5},\qquad x>1.

Steg 2 — løs F(x)=uF(x)=u.
1x5=u    x5=1u    x=(1u)1/5.1-x^{-5}=u\;\Rightarrow\; x^{-5}=1-u\;\Rightarrow\; x=(1-u)^{-1/5}.
F1(u)=(1u)1/5F^{-1}(u)=(1-u)^{-1/5}.

Steg 3 — implementer.

import numpy as np

def simuler_bolge(n):
    u = np.random.uniform(size=n)
    return (1 - u)**(-1/5)         # F^{-1}(u)

np.random.seed(0)
x = simuler_bolge(10**6)
print(np.mean(x > 2))    # ~ 0.0313
print(np.mean(x))        # ~ 1.25

Hva estimerer np.mean(simuler(10**6) > 2)? Uttrykket x > 2 er en vektor av True/False; np.mean av den er andelen trekk over 2 — altså et estimat på P(X>2)P(X>2). Fasit analytisk: P(X>2)=1F(2)=25=1320,03125P(X>2)=1-F(2)=2^{-5}=\tfrac{1}{32}\approx 0{,}03125. Kjørt gir koden 0,0314\approx 0{,}0314. ✓

(Til sammenligning er E(X)=1x5x6dx=51x5dx=54=1,25E(X)=\int_1^\infty x\cdot 5x^{-6}\,dx=5\int_1^\infty x^{-5}\,dx=\tfrac{5}{4}=1{,}25, som np.mean(x) bekrefter.)

> Sensornotat: np.mean av en betingelse = andel = sannsynlighetsestimat. Dette er broen til hele kap. 9.2.

📝Oppgave 3

En hale-tetthet er f(x)=3x4f(x)=\dfrac{3}{x^4} for x>1x>1 og 0 ellers.

a) Utled F(x)F(x).

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

c) Skriv en komplett funksjon simuler(n) som trekker fra denne fordelingen.

d) Hva estimerer np.mean(simuler(10**6) > 2)? Regn også fasit analytisk.

— naturlig pausepunkt —

Løkke 3 — numpy-verktøykassa (~16 min)

np.random.uniform — det uniforme råstoffet
np.random.uniform(size=n) returnerer en vektor med nn uavhengige trekk fra uniform(0,1)(0,1). Dette er startpunktet for inversjonsmetoden. Vil du ha uniform på et annet intervall, er det np.random.uniform(low, high, size=n), men i inversjonsmetoden er det alltid standardvarianten (0,1)(0,1), siden det er den F1F^{-1} forventer.

Én trekning gir hele vektoren på én gang — det er vektorisering: du slipper en løkke. All videre regning (som -beta*np.log(1-u)) skjer elementvis på hele vektoren samtidig.

np.random.normal — pass på at scale er standardavviket
np.random.normal(loc, scale, size=n) trekker fra normalfordelingen, der loc er forventningen μ\mu og scale er standardavviket σ\sigma — ikke variansen. Dette er den vanligste numpy-fella i emnet: vi skriver XN(μ,σ2)X\sim N(\mu,\sigma^2) med variansen som andre argument, men numpy vil ha standardavviket.

Skal du simulere XN(100,25)X\sim N(100,\,25) (altså varians σ2=25\sigma^2=25, standardavvik σ=5\sigma=5), skriver du np.random.normal(loc=100, scale=5, size=n) — send 25=5\sqrt{25}=5, ikke 25. Sender du scale=25, simulerer du i praksis N(100,625)N(100,\,625): en fordeling med feil, altfor stor spredning.

np.var med ddof=1 — empirisk varians
np.var(x) deler som standard summen av kvadratavvik på nn (ddof=0). Men den forventningsrette empiriske variansen — utvalgsvariansen S2S^2 fra kap. 5.1 — deler på n1n-1. I numpy får du den med np.var(x, ddof=1) (ddof = «delta degrees of freedom», trekker 1 fra nn).

Hvorfor n1n-1? Fordi vi bruker det estimerte gjennomsnittet xˉ\bar{x} i stedet for den (ukjente) sanne forventningen. Å dele på nn undervurderer da variansen systematisk; korreksjonen til n1n-1 gjør E(S2)=σ2E(S^2)=\sigma^2 (forventningsrett — vist i kap. 5.1). Når du estimerer en varians fra simulerte data, bruk derfor ddof=1. (For gigantiske nn er forskjellen forsvinnende, men vanen skal sitte.)

✏️Eksempel 3: funksjonsmønsteret og numpy-idiomene

Skriv en funksjon som simulerer nn trekk fra XN(50,9)X\sim N(50,\,9) (varians 9), og bruk den til å estimere P(X>56)P(X>56), forventningen og variansen. Pek på hvor scale-fella og ddof kommer inn.

XN(50,9)X\sim N(50,9) betyr μ=50\mu=50 og σ2=9\sigma^2=9, altså σ=9=3\sigma=\sqrt{9}=3.

import numpy as np

def simuler_normal(n):
    return np.random.normal(loc=50, scale=3, size=n)  # scale = SD = sqrt(9) = 3

np.random.seed(0)
x = simuler_normal(10**6)
print(np.mean(x > 56))     # ~ 0.0228  = P(X>56)
print(np.mean(x))          # ~ 50.0
print(np.var(x, ddof=1))   # ~ 9.0     (empirisk varians, delt paa n-1)

- scale-fella: scale=3 (standardavviket), ikke scale=9 (variansen). Sender du 9, får du en fordeling med σ=9\sigma=9 og alt blir galt.
- ddof: np.var(x, ddof=1) gir den forventningsrette variansen (9\approx 9).
- Fasit: P(X>56)=P ⁣(Z>56503)=P(Z>2)=1Φ(2)0,0228\displaystyle P(X>56)=P\!\left(Z>\frac{56-50}{3}\right)=P(Z>2)=1-\Phi(2)\approx 0{,}0228. Koden kjørt gir 0,0228\approx 0{,}0228. ✓

Funksjonsmønsteret går igjen overalt: def simuler(n): u = ...; return <uttrykk> — trekk råstoffet på én linje, transformer vektorisert på neste.

📝Oppgave 4

En dimensjon XX er N(20,0,04)N(20,\,0{,}04) (altså varians 0,040{,}04).

a) Hva skal scale være i np.random.normal?

b) Skriv linjen som trekker n=105n=10^5 slike verdier.

c) En medstudent skriver np.random.normal(loc=20, scale=0.04, size=n). Hvilken fordeling simulerer hen egentlig, og er spredningen for stor eller for liten?

📝Oppgave 5

Du har simulert en stor vektor x av observasjoner og vil estimere fordelingens forventning og varians.

a) Hvilken numpy-funksjon gir forventningsestimatet?

b) Hvordan skriver du variansestimatet slik at det er forventningsrett (som S2S^2)?

c) Med én setning: hvorfor ddof=1 og ikke standard ddof=0?

— naturlig pausepunkt —

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

Kodelesing: identifiser fordeling og estimat

En egen sjanger N-ferdighet er å lese ferdig kode og svare på to spørsmål:

1. Hvilken fordeling genererer snutten? Se på transformasjonen av u. Mønsteret -beta*np.log(1-u) er inversjonsmetoden for eksponensial med forventning beta; (1-u)**(-1/k) er en hale-tetthet med F=1xkF=1-x^{-k}; np.random.normal(loc, scale, ...) er normal med forventning loc og standardavvik scale.
2. Hva estimeres? En np.mean(betingelse) estimerer en sannsynlighet P(betingelse)P(\text{betingelse}); np.mean(x) estimerer en forventning E(X)E(X); np.var(x, ddof=1) en varians.

Grepet er å lese baklengs: transformasjonen forteller fordelingen, og det ytterste kallet (mean av en betingelse vs. mean av vektoren) forteller hva tallet er et estimat på.

✏️Eksempel 4: Les koden

Forklar hva denne snutten gjør — hvilken fordeling genererer f, og hva estimerer siste linje?

import numpy as np

def f(n):
    u = np.random.uniform(size=n)
    return -3 * np.log(1 - u)

np.random.seed(0)
print(np.mean(f(10**6) > 5))

Fordelingen: transformasjonen -3 * np.log(1 - u) er nøyaktig inversjonsmetoden F1(u)=βln(1u)F^{-1}(u)=-\beta\ln(1-u) med β=3\beta=3. Så f(n) trekker nn verdier fra en eksponensialfordeling med forventning β=3\beta=3.

Hva estimeres: f(10**6) > 5 er en boolsk vektor (er trekket over 5?), og np.mean av den er andelen over 5 — altså et estimat på P(X>5)P(X>5).

Fasit: for eksponensial er P(X>5)=1F(5)=e5/β=e5/30,1889P(X>5)=1-F(5)=e^{-5/\beta}=e^{-5/3}\approx 0{,}1889. Kjørt gir koden 0,1889\approx 0{,}1889. ✓

> Sensornotat: les baklengs — transformasjonen gir fordelingen (β=3\beta=3), og det ytterste np.mean(...>5) gir estimatet (P(X>5)P(X>5)).

📝Oppgave 6

Les koden:

def g(n):
    u = np.random.uniform(size=n)
    return (1 - u)**(-1/4)

print(np.mean(g(10**6)))
print(np.mean(g(10**6) > 3))

a) Hvilken fordeling (fordelingsfunksjon FF) genererer g?

b) Hva estimerer hver av de to print-linjene?

c) Regn den andre fasiten analytisk.

📝Oppgave 7

En materialstyrke XX (i passende enhet) har tetthet f(x)=cx3f(x)=\dfrac{c}{x^3} for x>1x>1 og 0 ellers.

a) Bestem cc.

b) Utled F(x)F(x) og F1(u)F^{-1}(u).

c) Skriv en komplett funksjon simuler(n) som trekker fra fordelingen med inversjonsmetoden.

d) Skriv én kodelinje som estimerer P(X>4)P(X>4), og regn fasiten analytisk.

📝Oppgave 8

En kollega vil simulere XN(10,4)X\sim N(10,\,4) (varians 4) og estimere P(8<X<13)P(8<X<13), men koden gir rare tall:

def simuler(n):
    return np.random.normal(loc=10, scale=4, size=n)

print(np.mean(8 < simuler(10**6) < 13))

a) Pek på to feil i koden.

b) Skriv en korrekt versjon.

c) Hva blir fasiten for P(8<X<13)P(8<X<13) analytisk? (Bruk Φ(1,5)=0,9332\Phi(1{,}5)=0{,}9332, Φ(1)=0,8413\Phi(1)=0{,}8413.)

Begrepsbank

Kjernekortene for inversjonsmetoden og numpy-verktøykassa, samlet.

Flashcard-stoff — hopp trygt over ved førstegangslesing; tidsanslaget gjelder kjernestoffet.

Kort: inversjonsmetoden

Er Uuniform(0,1)U\sim\text{uniform}(0,1), har X=F1(U)X=F^{-1}(U) fordelingsfunksjon FF. Metoden gjør uniforme tall om til trekk fra hvilken som helst fordeling du kan skrive FF for. Kjerneregelen: gå via FF (ikke ff).

Kort: beviset i tre linjer
P(F1(U)x)=P(UF(x))=F(x)P(F^{-1}(U)\le x)=P(U\le F(x))=F(x). Første likhet fordi FF er voksende; siste fordi P(Uu)=uP(U\le u)=u for uniform(0,1)(0,1) med u=F(x)u=F(x). Dermed har X=F1(U)X=F^{-1}(U) fordelingsfunksjon FF.
Oppskriftskort: de tre stegene
(1) Utled FF (integrer ff, teknikk fra kap. 2.4). (2) Løs F(x)=uF(x)=u for xx — det gir F1(u)F^{-1}(u). (3) Implementer: u = np.random.uniform(size=n), sett vektoren inn i F1(u)F^{-1}(u). Steg 2 er hele det matematiske arbeidet.
Formelkort: eksponensial-inversjon

For eksponensial med forventning β\beta: F(x)=1ex/βF(x)=1-e^{-x/\beta}, og F1(u)=βln(1u)F^{-1}(u)=-\beta\ln(1-u). Kode: -beta * np.log(1 - u). Skriv 1u1-u (fra utledningen); βln(u)-\beta\ln(u) gir samme fordeling, men 1u1-u er det riktige uttrykket.

Formelkort: hale-inversjon

For en hale-tetthet f(x)=kx(k+1)f(x)=k\,x^{-(k+1)}(1,)(1,\infty) er F(x)=1xkF(x)=1-x^{-k}, og F1(u)=(1u)1/kF^{-1}(u)=(1-u)^{-1/k}. Kode: (1 - u)**(-1/k). Halesannsynligheten er P(X>x)=xkP(X>x)=x^{-k}.

Feilkort: np.random.normal, scale = standardavvik
np.random.normal(loc, scale, size=n): loc =μ=\mu, scale =σ=\sigma (standardavviket). For XN(μ,σ2)X\sim N(\mu,\sigma^2) må du sende σ2\sqrt{\sigma^2} som scale, aldri variansen. Feil her (N(100,25)N(100,25)\to scale=25) gir en fordeling med altfor stor spredning.
Kort: np.mean som estimat
np.mean(x) estimerer forventningen E(X)E(X); np.mean(betingelse) estimerer sannsynligheten P(betingelse)P(\text{betingelse}) (andelen True). Dette siste — sannsynlighet som gjennomsnittlig indikator — er grunnprinsippet for Monte Carlo (kap. 9.2).
Kort: np.var(x, ddof=1)

Empirisk (forventningsrett) varians deler på n1n-1: np.var(x, ddof=1). Standard np.var(x) deler på nn. n1n-1 brukes fordi vi setter inn det estimerte xˉ\bar{x}; korreksjonen gir E(S2)=σ2E(S^2)=\sigma^2 (kap. 5.1).

Oppskriftskort: funksjonsmønsteret
def simuler(n): u = np.random.uniform(size=n); return <F-invers av u>. To linjer: trekk råstoffet, transformer vektorisert. Alt av simulering i boka følger dette skjelettet — bytt bare ut transformasjonen.
Feilkort: F(x)=uF(x)=u, ikke f(x)=uf(x)=u

Den hyppigste inversjonsfeilen: å løse tetthetslikningen f(x)=uf(x)=u i stedet for F(x)=uF(x)=u. Inversjonsmetoden bruker fordelingsfunksjonen. Integrer ff til FF først, løs så F(x)=uF(x)=u for xx. (Samme logikk som median-fella F(m)=1/2F(m)=1/2 i kap. 2.4.)

Kort: kodelesing baklengs

For å tolke ferdig kode: transformasjonen av u gir fordelingen (-b*np.log(1-u)\to eksponensial β=b\beta=b; (1-u)**(-1/k)\to hale med F=1xkF=1-x^{-k}), og det ytterste kallet gir estimatet (np.mean(x)E(X)\to E(X); np.mean(x>c)P(X>c)\to P(X>c)).

Repetisjonsoppgaver
Din fremgang
0 / 4 oppgaver
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 Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.