Paradoxo do aniversário de Pedante

20

fundo

O paradoxo do aniversário é um problema popular na teoria da probabilidade que desafia a intuição matemática (da maioria das pessoas). A declaração do problema é:

Dadas as pessoas N , qual é a probabilidade de que pelo menos duas delas tenham o mesmo aniversário (independentemente do ano).

O problema geralmente é simplificado ignorando completamente os dias bissextos. Nesse caso, a resposta para N = 23 é P (23) ± 0,5072972 (como um exemplo comum). O artigo vinculado da Wikipedia explica como chegar a essa probabilidade. Como alternativa, este vídeo do Numberphile faz um bom trabalho.

No entanto, para esse desafio, queremos fazer o certo e não ignorar os anos bissextos. Isso é um pouco mais complicado, pois agora é necessário adicionar o dia 29 de fevereiro, mas esse aniversário em particular é menos provável que todos os outros.

Também usaremos as regras do ano bissexto completo :

  • Se um ano é divisível por 400, é um ano bissexto.
  • Senão, se um ano é divisível por 100, não é um ano bissexto.
  • Senão, se um ano é divisível por 4, é um ano bissexto.
  • Senão, não é um ano bissexto.

Confuso? Isso significa que os anos 1700, 1800, 1900, 2100, 2200, 2300 não são bissextos, mas 1600, 2000, 2400 são (assim como qualquer outro ano divisível por 4). Esse calendário se repete a cada 400 anos, e assumiremos uma distribuição uniforme de aniversários nesses 400 anos.

O resultado corrigido para N = 23 agora é P (23) ± 0,5068761 .

O desafio

Dado um número inteiro 1 ≤ N < 100, determine a probabilidade de que, entre as Npessoas, pelo menos duas tenham o mesmo aniversário, considerando as regras do ano bissexto. O resultado deve ser um número de ponto flutuante ou de ponto fixo, com precisão de pelo menos 6 casas decimais. É aceitável truncar zeros à direita.

Você pode escrever um programa ou função, recebendo entrada via STDIN (ou alternativa mais próxima), argumento da linha de comando ou argumento da função e gerar o resultado via STDOUT (ou alternativa mais próxima), valor de retorno da função ou parâmetro da função (saída).

Sua solução deve ser capaz de produzir saída para todas as 99 entradas em questão de segundos. Isso é principalmente para descartar os métodos de Monte Carlo com toneladas de amostras, por isso, se você estiver usando um algoritmo principalmente rápido e exato em uma linguagem esotérica excessivamente lenta, estou disposto a dar margem a essa regra.

Casos de teste

Aqui está a tabela completa de resultados:

 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

(É claro que P (99) é apenas 1,0 devido ao arredondamento. A probabilidade não alcançará exatamente 1,0 até P (367) .)

Martin Ender
fonte
7
1. Se você for pedante, deve levar em consideração que os aniversários não são distribuídos uniformemente ao longo do ano. 2. A relevância precisa das regras do ano bissexto depende de quais suposições são feitas sobre a longevidade humana. A idéia é amortizar durante todo o ciclo de 400 anos?
22415 Peter
1
@PeterTaylor Sim, assuma uma distribuição uniforme ao longo de todo o ciclo de 400 anos. Eu nunca disse que o grupo de N estava vivo ao mesmo tempo. ;)
Martin Enders

Respostas:

6

Pitão, 31 34 bytes

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

Demonstração , Arnês de Teste

Isso funciona de maneira semelhante à versão antiga, exceto que, em vez de gerar separadamente o valor (366 + n * (.2425 - 1)) e multiplicá-lo, ele começa gerando uma lista que se estende de 366 a 365 - n + 2, depois modifica o 366 no lugar para se tornar (366 + n * (0,2425 - 1)) e, em seguida, pega o produto da lista. Além disso, as constantes 366 e -.7575 são usadas em vez de 365 e .2425.


Versão antiga:

Pitão, 34 bytes

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

Existem duas maneiras possíveis de não haver um par de pessoas com o mesmo aniversário: todo mundo para ter aniversários diferentes, e ninguém tem um aniversário em 29 de fevereiro, e alguém para fazer um aniversário no dia 29, e todo mundo para ter um aniversário diferente. aniversários em dias normais.

A probabilidade da primeira ocorrência é (365 * 364 * ... 365 - n + 1) / (365,2425 ^ n).

A probabilidade da segunda ocorrência é (365 * 364 * ... 365 - n + 2) * .2425 * n / (365.2425 ^ n)

Estes podem ser escritos juntos como (365 * 364 * ... 365 - n + 2) * (365 - n + 1 + .2425 * n) / (365,2425 ^ n) = (365 * 364 * ... 365 - n + 2) * (365 + 1 + (0,2425 - 1) * n) / (365,2425 ^ n).

Essa é a probabilidade de não haver pares; portanto, a probabilidade de pelo menos um par é um menos o número acima.

J = .2425
K = 365
.xrK-hKQ = (365 * 364 * ... 365 - n + 2)
+hK*QtJ = (365 + 1 + n * (.2425 - 1))
^+KJQ = (365.2425 ^ n)
isaacg
fonte
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á o resultado. Mude .2425para fractions.Fraction(97,400)para obter um resultado exato.

orlp
fonte
Você não precisa de um espaço entre 2e and.
Isaacg
Você não pode colocar no 1/para ge dividir por ele em vez disso?
Xnor
@ xnor Sim, com o tempo essas coisas se perdem :) O que antes era uma otimização torna-se subótimo mais tarde.
orlp
você pode introduzir e=365e substituir 365 por e e 367 por e + 2
Willem
@ willem Isso não é mais curto.
Orlp 25/04/2015
2

Python 155 153 151 142 140 bytes

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)

Ligue a(n)para o resultado. Para resultados exatos, envolva dem uma fração.

Teste aqui

Usa a mesma técnica que aqui , mas com constantes modificadas.

O número um
fonte
Você não precisa de um espaço entre 2e and.
Isaacg
Eu pensei que era 98 (embora eu possa ter um cálculo erro louco ...)
Tim
1

Código da máquina 80386, 43 bytes

Hexdump do código:

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

Comecei da seguinte fórmula (para a probabilidade complementar):

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

(aqui d = 366 * 400 - 303é o número de dias em 400 anos)

Aqui está o código c ++ que o implementa (já está otimizado um pouco):

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;
}

O código é organizado de forma que exija o número mínimo de constantes - apenas duas ( 400 / de 303 / d). Eu uso o floattipo para representá-los porque ocupa menos espaço (4 bytes por constante). Além disso, não queria multiplicar const2por k - 1(porque isso exigiria a conversão k - 1para float); o código subtrai const2repetidamente.

Aqui está a lista da linguagem 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
anatolyg
fonte