(Un po ') Paradosso di compleanno pedante


20

sfondo

Il paradosso del compleanno è un problema popolare nella teoria della probabilità che sfida l'intuizione matematica (della maggior parte delle persone). L'affermazione del problema è:

Date le persone N , qual è la probabilità che almeno due di loro abbiano lo stesso compleanno (trascurando l'anno).

Il problema è di solito semplificato ignorando completamente i giorni bisestili. In questo caso, la risposta per N = 23 è P (23) ≈ 0,5072972 (come esempio comune). L'articolo di Wikipedia collegato spiega come arrivare a questa probabilità. In alternativa, questo video di Numberphile fa davvero un buon lavoro.

Tuttavia, per questa sfida vogliamo farlo nel modo giusto e non farlo ignorare gli anni bisestili. Questo è leggermente più complicato, dal momento che ora il 29 febbraio deve essere aggiunto, ma questo particolare compleanno è meno probabile di tutti gli altri.

Useremo anche le regole per l' anno bisestile completo :

  • Se un anno è divisibile per 400 è un anno bisestile.
  • Altrimenti, se un anno è divisibile per 100 non è un anno bisestile.
  • Altrimenti, se un anno è divisibile per 4 è un anno bisestile.
  • Altrimenti, non è un anno bisestile.

Confuso? Significa che gli anni 1700, 1800, 1900, 2100, 2200, 2300 non sono anni bisestili, ma 1600, 2000, 2400 lo sono (così come ogni altro anno divisibile per 4). Questo calendario si ripete ogni 400 anni e ipotizzeremo una distribuzione uniforme dei compleanni in questi 400 anni.

Il risultato corretto per N = 23 è ora P (23) ≈ 0,5068761 .

La sfida

Dato un numero intero 1 ≤ N < 100, determinare la probabilità che tra le Npersone almeno due abbiano lo stesso compleanno in considerazione delle regole dell'anno bisestile. Il risultato dovrebbe essere un numero a virgola mobile o fisso, preciso con almeno 6 cifre decimali. È accettabile troncare gli zero finali.

È possibile scrivere un programma o una funzione, prendendo l'input tramite STDIN (o l'alternativa più vicina), l'argomento della riga di comando o l'argomento della funzione e producendo il risultato tramite STDOUT (o l'alternativa più vicina), il valore di ritorno della funzione o il parametro della funzione (out).

La tua soluzione deve essere in grado di produrre output per tutti i 99 input in pochi secondi. Ciò serve principalmente a escludere i metodi Monte Carlo con tonnellate di campioni, quindi se stai usando un algoritmo principalmente veloce ed esatto in un linguaggio esoterico eccessivamente lento, sono disposto a dare libero sfogo a questa regola.

Casi test

Ecco la tabella completa dei risultati:

 1 => 0.000000
 2 => 0.002737
 3 => 0.008195
 4 => 0.016337
 5 => 0.027104
 6 => 0.040416
 7 => 0.056171
 8 => 0.074251
 9 => 0.094518
10 => 0.116818
11 => 0.140987
12 => 0.166844
13 => 0.194203
14 => 0.222869
15 => 0.252642
16 => 0.283319
17 => 0.314698
18 => 0.346578
19 => 0.378764
20 => 0.411063
21 => 0.443296
22 => 0.475287
23 => 0.506876
24 => 0.537913
25 => 0.568260
26 => 0.597796
27 => 0.626412
28 => 0.654014
29 => 0.680524
30 => 0.705877
31 => 0.730022
32 => 0.752924
33 => 0.774560
34 => 0.794917
35 => 0.813998
36 => 0.831812
37 => 0.848381
38 => 0.863732
39 => 0.877901
40 => 0.890932
41 => 0.902870
42 => 0.913767
43 => 0.923678
44 => 0.932658
45 => 0.940766
46 => 0.948060
47 => 0.954598
48 => 0.960437
49 => 0.965634
50 => 0.970242
51 => 0.974313
52 => 0.977898
53 => 0.981043
54 => 0.983792
55 => 0.986187
56 => 0.988266
57 => 0.990064
58 => 0.991614
59 => 0.992945
60 => 0.994084
61 => 0.995055
62 => 0.995880
63 => 0.996579
64 => 0.997169
65 => 0.997665
66 => 0.998080
67 => 0.998427
68 => 0.998715
69 => 0.998954
70 => 0.999152
71 => 0.999314
72 => 0.999447
73 => 0.999556
74 => 0.999645
75 => 0.999717
76 => 0.999775
77 => 0.999822
78 => 0.999859
79 => 0.999889
80 => 0.999913
81 => 0.999932
82 => 0.999947
83 => 0.999959
84 => 0.999968
85 => 0.999976
86 => 0.999981
87 => 0.999986
88 => 0.999989
89 => 0.999992
90 => 0.999994
91 => 0.999995
92 => 0.999996
93 => 0.999997
94 => 0.999998
95 => 0.999999
96 => 0.999999
97 => 0.999999
98 => 0.999999
99 => 1.000000

(Naturalmente, P (99) è solo 1,0 a causa dell'arrotondamento. La probabilità non raggiungerà esattamente 1,0 fino a P (367) .)


7
1. Se vuoi essere pedante, dovresti tenere conto del fatto che i compleanni non sono distribuiti uniformemente durante l'anno. 2. La pertinenza precisa delle regole dell'anno bisestile dipende dalle ipotesi sulla longevità umana. L'idea è di ammortizzare lungo l'intero ciclo di 400 anni?
Peter Taylor

1
@PeterTaylor Sì, ipotizza una distribuzione uniforme per l'intero ciclo di 400 anni. Non ho mai detto che l'insieme di N persone fosse vivo allo stesso tempo. ;)
Martin Ender,

Risposte:


6

Pyth, 31 34 byte

Jt.2425K366-1c.xX0rK-KQ*JQ^+KJQ

Dimostrazione , test Harness

Funziona in modo simile alla versione precedente, tranne per il fatto che invece di generare separatamente il valore (366 + n * (.2425 - 1)) e moltiplicarlo, inizia generando un elenco che si estende da 366 a 365 - n + 2, quindi modifica il 366 in posizione per diventare (366 + n * (.2425 - 1)), quindi accetta il prodotto dell'elenco. Inoltre, vengono utilizzate le costanti 366 e -.7575 anziché 365 e .2425.


Vecchia versione:

Pyth, 34 byte

J.2425K365-1c*+hK*QtJ.xrK-hKQ^+KJQ

Ci sono due modi possibili per non avere una coppia di persone con lo stesso compleanno: tutti hanno compleanni diversi, e nessuno ha un compleanno il 29 febbraio, e qualcuno ha un compleanno il 29, e tutti gli altri hanno un altro compleanni nei giorni normali.

La probabilità del primo verificarsi è (365 * 364 * ... 365 - n + 1) / (365.2425 ^ n).

La probabilità che si verifichi il secondo è (365 * 364 * ... 365 - n + 2) * .2425 * n / (365.2425 ^ n)

Questi possono essere scritti insieme come (365 * 364 * ... 365 - n + 2) * (365 - n + 1 + .2425 * n) / (365.2425 ^ n) = (365 * 364 * ... 365 - n + 2) * (365 + 1 + (.2425 - 1) * n) / (365.2425 ^ n).

Questa è la probabilità di nessuna coppia, quindi la probabilità di almeno una coppia è meno un numero sopra indicato.

J = .2425
K = 365
.xrK-hKQ = (365 * 364 * ... 365 - n + 2)
+hK*QtJ = (365 + 1 + n * (.2425 - 1))
^+KJQ = (365.2425 ^ n)

5

Python, 179 178 144 143 140 136 135 133

f=.2425
g=365+f
a=lambda n:(n and(365-n)*a(n-1)or 365)/g
b=lambda n:(n<2and f or(367-n)*b(n-1)+a(n-2)*f)/g
p=lambda n:1-a(n-1)-b(n)

p(n)dà il risultato. Passare .2425a fractions.Fraction(97,400)per ottenere un risultato esatto.


Non è necessario uno spazio tra 2e and.
isaacg,

Non si può mettere in 1/per ge dividere per esso, invece?
xnor

@xnor Sì, con il passare del tempo queste cose si perdono :) Quella che una volta era un'ottimizzazione diventa non ottimale in seguito.
orlp,

potresti introdurre e=365e sostituire 365 con e e 367 con e + 2
Willem,

@willem Non è più breve.
orlp

2

Python 155 153 151 142 140 byte

d=146097
b=d/400
c=97/d
e=lambda n:n<2and 1-97/d or e(n-1)*(366-n)/b
f=lambda n:n<2and c or f(n-1)*(367-n)/b+e(n-1)*c
a=lambda n:1-e(n)-f(n)

Chiama a(n)per il risultato. Per risultati esatti, avvolgi din una frazione.

Prova qui

Usa la stessa tecnica di qui , ma con costanti modificate.


Non è necessario uno spazio tra 2e and.
isaacg,

Pensavo fosse il 98 (anche se potrei aver commesso un errore di calcolo ...)
Tim,

1

80386 codice macchina, 43 byte

Hexdump del codice:

68 75 6e 33 3b 68 5a eb 07 3b 8b c4 49 d9 e8 d9
e8 d8 00 d9 e8 d9 40 04 de ea d8 c9 d9 00 de eb
e2 f3 d8 ca d9 e8 d8 e1 58 58 c3

Ho iniziato dalla seguente formula (per la probabilità complementare):

\ Prod \ limits_ {i = 0} ^ {k-2} (1- \ frac {97 + 400 * i} {d}) * (1- \ frac {303 * (k-1)} {d})

(ecco d = 366 * 400 - 303il numero di giorni in 400 anni)

Ecco il codice c ++ che lo implementa (è già un po 'ottimizzato):

double it_opt(int k)
{
    double d = 366 * 400 - 303; // number of days in 400 years
    double result = 1; // probability of no coincidences
    const float const1 = float(400 / d);
    const float const2 = float(303 / d);
    double v1 = 1 + const2;
    double v2 = 1;

    for (int i = 0; i < k - 1; ++i)
    {
        v1 -= const1;
        result *= v1;
        v2 -= const2;
    }
    result *= v2;
    return 1 - result;
}

Il codice è organizzato in modo da richiedere il numero minimo di costanti - solo due ( 400 / de 303 / d). Uso il floattipo per rappresentarli perché occupa meno spazio (4 byte per costante). Inoltre, non volevo moltiplicare const2per k - 1(perché ciò richiederebbe la conversione k - 1in float); il codice viene sottratto const2invece ripetutamente.

Ecco l'elenco delle lingue dell'assembly:

    ; // fastcall convention - parameter k is in ecx
    ; // result must be returned in st
    push dword ptr 0x3b336e75; // const1 = [esp + 4]
    push dword ptr 0x3b07eb5a; // const2 = [esp]
    mov eax, esp;              // use [eax] instead of [esp] - 1 byte less
    dec ecx;                   // calculate k - 1
    fld1;                      // initiaze result = 1
    fld1;                      // initialize v1
    fadd [eax];
    fld1;                      // initialilze v2
myloop:
    fld dword ptr [eax + 4];
    fsubp st(2), st;            // update v1
    fmul st, st(1);             // update result
    fld dword ptr [eax];
    fsubp st(3), st;            // update v2
    loop myloop;                // loop
    fmul st, st(2);             // update result by v2
    fld1;
    fsub st, st(1);             // complement result
    pop eax;                    // restore stack
    pop eax;                    // restore stack
    ret;                        // return
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.