3  Samansette forsøk - choice

No skal me sjå på korleis me kan simulera eit enkelt samansett forsøk. Oppgåva me skal sjå på er denne:

I ei skål ligg det 7 banan-twist og 3 daim-twist. Kva er sannsynet for at me får banan når me trekk ut to bitar frå skåla. (Både med og utan tilbakelegging)

3.1 Teoretisk sannsyn

Først kan me sjå på kva det teoretiske sannsynet er for desse to. Ofte når me bruker simulering er det fordi det er vanskeleg å finna svaret ved rekning, men i dette dømet er det ikkje så vanskeleg.

Med tilbakelegging

\[ P(\text{BB}) = \frac{7}{10} \cdot \frac{7}{10} = \frac{49}{100} = 0.490 \]

Utan tilbekelegging \[ P(\text{BB}) = \frac{7}{10} \cdot \frac{6}{9} = \frac{42}{90} \approx 0.467 \]

3.2 Simulering av twist-trekket

# importerar og lagar ein random generator
from numpy.random import default_rng
rng = default_rng()

# antall simuleringar
N = 1000000

# lagar liste med twistskåla
twistskål = ["Banan"]*7 + ["Daim"]*3

# skriv ut twistskåla
print(twistskål)
['Banan', 'Banan', 'Banan', 'Banan', 'Banan', 'Banan', 'Banan', 'Daim', 'Daim', 'Daim']

3.2.1 Med tilbakelegging

BB = 0

for i in range(N):
    twist = rng.choice(twistskål, size = 2)
    if twist[0] == "Banan" and twist[1] == "Banan":
        BB += 1

rel_frek = BB/N

print(f"Sannsynet for at me trekk to banantwist er {rel_frek}")
Sannsynet for at me trekk to banantwist er 0.489409
Tips

For ein litt meir elegant kode kan me droppa if-setningen i løkka vår. Dette kan me gjera ved å gjera ein boolsk variabel (True eller False) om til eit heiltal. Då blir True = 1 og False = 0

BB = 0

for i in range(N):
    twist = rng.choice(twistskål, size = 2)
    BB += int(twist[0] == "Banan" and twist[1] == "Banan")

rel_frek = BB/N

print(f"Sannsynet for at me trekk to banantwist er {rel_frek}")
Sannsynet for at me trekk to banantwist er 0.489524

Me kan sjå kor langt unna den teoretiske verdien me kjem:

feil = abs(rel_frek - 49/100) 
print(f"Feilen blir {round(feil, 6)} når me gjer {N} simuleringar")
Feilen blir 0.000476 når me gjer 1000000 simuleringar

Dersom me vil ha eit enno meir nøyaktig resultat kan me gjera fleire simuleringar, dette kjem me litt attende til seinare. Merk at programmet vil fort ta ganske lang tid å køyra etter kvart som talet på simuleringar aukar.

3.2.2 Utan tilbakelegging

Forskjellen blir ikkje stor her. Det einaste me gjer er å legga til replace = False som argument i choice-funksjonen

BB = 0

for i in range(N):
    twist = rng.choice(twistskål, size = 2, replace = False)
    if twist[0] == "Banan" and twist[1] == "Banan":
        BB += 1

rel_frek = BB/N

print(f"Sannsynet for at me trekk to banantwist er {rel_frek}")
Sannsynet for at me trekk to banantwist er 0.466383
feil = abs(rel_frek - 42/90) 
print(f"Feilen blir {round(feil, 6)} når me gjer {N} simuleringar")
Feilen blir 0.000284 når me gjer 1000000 simuleringar

3.3 Ikkje uniforme sannsynsmodellar

Dette dømet me har sett på er eit døme på ein ikkje-uniform sannsynsmodell, sidan sannsynet for Banan og Daim ikkje er det same. I starten laga me ei liste med alle twistane i skåla. I dette dømet er det praktisk, sidan me har eit lite utfallsrom (banan og daim) og kontroll på kor mange det er av kvar.

Av og til kan det vera nyttig å definera ikkje-uniforme sannsynsmodellar på ein litt anna måte.

twistar = ["Banan", "Daim"]
sannsyn = [7/10, 3/10]

to_twist = rng.choice(twistar, size = 2, p = sannsyn)

print(f"Me trekk {to_twist}")
Me trekk ['Banan' 'Daim']

Dette kan brukast viss me veit utfallsrommet og sannsynet for kvart av utfalla, ikkje nødvendigvis antallet. F.eks. blodtype hos tilfeldige personar i befolkningen.