Bursdagsproblemet¶

For én tilfeldig valgt person antar vi at alle datoer i året (365) er like sannsynlige å ha som bursdag. Vi skal trekke $n$ personer tilfeldig og regne ut sannsynligheten for at minst to av disse har bursdag på samme dag. Under de gitte antagelsene kan du regne ut dette "for hånd", men her skal vi estimere denne sannsynligheten ved å kjøre en simulering.

I hver iterasjon i simuleringen (for-løkka) trekker vi tilfeldige tall mellom 1 og 365 for de $n$ personene. Dersom to eller flere personer ble tildelt det samme tallet, registrerer vi en "suksess" ($x = 1$). Dersom alle de $n$ personene fikk tildelt ulike bursdager, registrerer vi en "fiasko" ($x = 0$). Til slutt teller vi opp alle suksesser og deler på antall simuleringer for å få et estimat på sannsynligheten for at minst to personer i en gruppe på $n$ mennesker har bursdag på samme dag.

In [2]:
import numpy as np

Nyttige funksjoner¶

In [ ]:
np.random.choice(5,3) # for å trekke tilfeldige tall, 5 betyr at vi trekker fra tallene (0,1,2,3,4), og 3 at vi trekker tre tall
In [ ]:
np.unique([1,2,4,5,2,4,2]) # returnerer kun de unike tallene i denne arrayen

Simulering 1¶

Her kan man regne eksakt - og det er lagt opp til det i oppgaven - men du kan også simulere. Slik gjøres simuleringen.

In [ ]:
n_sim = 100000 # Antall simuleringer du vil gjøre
n = 23 # Antall personer som skal velges ut
dager = 365

x =  np.zeros(n_sim) # initialiserer x-vektor med bare 0-eller

for i in range(n_sim):
    bursdager = np.random.choice(dager, n) # trekker tilfeldige datoer
    if len(np.unique(bursdager)) < n:  # Dersom det ikke er like mange unike bursdager som det er personer
        x[i] = 1
        
estimat = sum(x)/n_sim
print(estimat)

Oppdatering 29 AUG: Den følgende simuleringen er gjennomført for mange ulike n. Resultatene fra simuleringen kan du bruke for å løse STACK-oppgaven uten tilgang til huben.

In [3]:
n_sim = 100000  # Antall simuleringer du vil gjøre
dager = 365

# Vi vil prøve oss frem med mange n
n_values = range(10, 51)

# Her vil vi lagre resultat (sannsynlighet for minst to har samme bursdag) for hver n
results = {}

for n in n_values:
    x = np.zeros(n_sim)  # initialiserer x-vektor med bare 0-eller
    
    for i in range(n_sim):
        bursdager = np.random.choice(dager, n)  # trekker tilfeldige datoer
        if len(np.unique(bursdager)) < n:  # Dersom det ikke er like mange unike bursdager som det er personer
            x[i] = 1
    
    estimat = sum(x) / n_sim
    results[n] = estimat

# Skriv ut resultater for ulike n
print("n\tSannsynlighet")
print("-" * 20)
for n, estimat in results.items():
    print(f"{n}\t{estimat:.6f}")
n	Sannsynlighet
--------------------
10	0.118420
11	0.141690
12	0.166710
13	0.195740
14	0.223560
15	0.251970
16	0.283420
17	0.316280
18	0.346750
19	0.380470
20	0.412580
21	0.443310
22	0.472830
23	0.506100
24	0.538750
25	0.567560
26	0.595950
27	0.623940
28	0.653820
29	0.678590
30	0.707830
31	0.728880
32	0.755250
33	0.773200
34	0.797740
35	0.811650
36	0.832500
37	0.847380
38	0.864500
39	0.879830
40	0.891230
41	0.903650
42	0.914210
43	0.923580
44	0.933390
45	0.939990
46	0.947250
47	0.955790
48	0.960350
49	0.965530
50	0.968850

Simulering 2¶

I simulering 1 antok vi at for en tilfeldig valgt person var alle dager i året like sannsynlige for å ha bursdag (1/365). I realiteten vet vi at noen måneder er mer "populære" enn andre. Vi har hentet data på antall fødsler i Norge per måned i 2001 (ikke et skuddår), fra SSB: https://www.ssb.no/statbank/table/05531/. Disse dataene bruker vi til å anslå sannsynlighet for å være født i de ulike månedene. Vi antar at alle dager innenfor en gitt måned er like sannsynlige.

In [4]:
antall_fødsler = np.array([4959, 4495, 4958, 5009, 5018, 4955, 4919, 4852, 4742, 4555, 4153, 4081])
andel_fødsler = antall_fødsler/sum(antall_fødsler)

mnd_dager = [31,28,31,30,31,30,31,31,30,31,30,31]

daglig_prob = [andel_fødsler[0]/mnd_dager[0]]*mnd_dager[0] # initialisere

for i in range(1,12):
    daglig_prob = daglig_prob + [andel_fødsler[i]/mnd_dager[i]]*mnd_dager[i]
    
# set(daglig_prob) # se på daglige sannsynligheter i hver mnd
# 1/365            # sammenligne med antagelsen i simulering 1
In [5]:
print(np.unique(daglig_prob)) # har fått 12 unike sannsynligheter, en for dagene i hver måned - men husk at daglig_prob har 365 elementer - og det er 12 unike verdier
[0.00232195 0.00244168 0.00259164 0.00276062 0.00278797 0.00279874
 0.00282093 0.0028215  0.00283152 0.00285507 0.0029132  0.00294495]
In [ ]:
n_sim = 100000 # Antall simuleringer du vil gjøre
n = 50 # Antall personer som skal velges ut
dager = 365

y = np.zeros(n_sim) # initialiserer y-vektor med bare 0-eller

for i in range(n_sim):
    bursdager = np.random.choice(dager, n, daglig_prob) # trekker tilfeldige datoer
    if len(np.unique(bursdager)) < n:  # Dersom det ikke er like mange unike bursdager som det er personer
        y[i] = 1
        
estimat = sum(y)/n_sim
print(estimat)

Oppdatering 29 AUG: Den følgende simuleringen er gjennomført for mange ulike n. Resultatene fra simuleringen kan du bruke for å løse STACK-oppgaven uten tilgang til huben.

In [6]:
n_sim = 100000  # Antall simuleringer du vil gjøre
dager = 365

# Vi vil prøve oss frem med mange n
n_values = range(15, 30)

# Her vil vi lagre resultat (sannsynlighet for minst to har samme bursdag) for hver n
results = {}

for n in n_values:
    y = np.zeros(n_sim) # initialiserer y-vektor med bare 0-eller

    for i in range(n_sim):
        bursdager = np.random.choice(dager, n, daglig_prob) # trekker tilfeldige datoer
        if len(np.unique(bursdager)) < n:  # Dersom det ikke er like mange unike bursdager som det er personer
            y[i] = 1
        
    estimat = sum(y)/n_sim
    results[n] = estimat

# Skriv ut resultater for ulike n
print("n\tSannsynlighet")
print("-" * 20)
for n, estimat in results.items():
    print(f"{n}\t{estimat:.6f}")
n	Sannsynlighet
--------------------
15	0.251210
16	0.282150
17	0.316310
18	0.342800
19	0.378690
20	0.409900
21	0.444470
22	0.478380
23	0.510650
24	0.537390
25	0.570040
26	0.596530
27	0.626510
28	0.652520
29	0.678260