(일부) Pedantic Birthday Paradox


20

배경

생일 역설 하는 무시 확률 이론에서 인기있는 문제 (대부분의 사람들의) 수학적 직관이다. 문제는 다음과 같습니다.

N 명이 주어지면 둘 중 적어도 둘이 같은 생일을 가질 확률은 얼마입니까 (연도 무시).

문제는 일반적으로 윤일을 완전히 무시함으로써 단순화됩니다. 이 경우 N = 23에 대한 답 은 P (23) ≈ 0.5072972입니다 (일반적인 예). 링크 된 Wikipedia 기사는이 확률에 도달하는 방법을 설명합니다. 또는 이 Numberphile 비디오 는 정말 잘 작동합니다.

그러나이 도전에 우리는 제대로하고 싶고 윤년을 무시 하지 않습니다 . 2 월 29 일을 추가해야하기 때문에이 과정은 약간 더 복잡하지만이 특정 생일은 다른 모든 생일보다 적습니다.

또한 전체 윤년 규칙을 사용합니다 .

  • 1 년을 400으로 나눌 수 있다면 윤년입니다.
  • 그렇지 않으면 1 년을 100으로 나눌 수 있다면 윤년이 아닙니다.
  • 그렇지 않으면 1 년을 4로 나눌 수 있다면 윤년입니다.
  • 그렇지 않으면, 윤년이 아닙니다.

혼란 스러운가? 이는 1700 년, 1800 년, 1900 년, 2100 년, 2200 년, 2300 년이 윤년이 아니라 1600, 2000, 2400 년 (4 년으로 나눌 수있는 다른 해)임을 의미합니다. 이 달력은 400 년마다 반복되며 400 년 동안 균일 한 생일 분포를 가정합니다.

N = 23에 대한 수정 결과 는 이제 P (23) ≈ 0.5068761 입니다.

도전

정수가 주어지면 윤년 규칙을 고려하여 둘 이상의 생일이 같은 생일을 가질 1 ≤ N < 100확률을 결정하십시오 N. 결과는 소수점 이하 6 자리까지 정확한 부동 소수점 또는 고정 소수점 숫자 여야합니다. 후행 0을자를 수 있습니다.

STDIN (또는 가장 가까운 대안), 명령 행 인수 또는 함수 인수를 통해 입력을 받아 프로그램 또는 함수를 작성하고 STDOUT (또는 가장 가까운 대안), 함수 리턴 값 또는 함수 (out) 매개 변수를 통해 결과를 출력 할 수 있습니다.

솔루션은 몇 초 안에 99 개의 입력 모두에 대한 출력을 생성 할 수 있어야합니다. 이것은 주로 수많은 샘플로 Monte Carlo 방법을 배제하기위한 것이므로 지나치게 느리게 밀착 된 언어로 주로 빠르고 정확한 알고리즘을 사용하는 경우이 규칙에 대해 기꺼이 설명하겠습니다.

테스트 사례

전체 결과 표는 다음과 같습니다.

 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

(물론, P (99) 는 반올림으로 인해 1.0 에 불과 합니다. P (367) 까지 확률은 정확히 1.0에 도달하지 않습니다 .)


7
1. 만약 당신이 pedantic이라면, 당신은 생일이 일년 내내 균등하게 분배되지 않는다는 것을 고려해야합니다. 2. 윤년 규칙의 정확한 관련성은 인간의 수명에 대해 어떤 가정을하는지에 달려 있습니다. 전체 400 년주기 동안 상각 할 생각입니까?
피터 테일러

1
@PeterTaylor 예, 전체 400 년주기에 걸쳐 균일 한 분포를 가정합니다. 나는 N 명의 사람들이 동시에 살아 있다고 말한 적이 없다 . ;)
Martin Ender 2016

답변:


6

Pyth, 31 34 바이트

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

데모 , 테스트 하니스

이것은 (366 + n * (.2425-1)) 값을 별도로 생성하고 곱하는 대신 366에서 365-n + 2까지 확장되는 목록을 생성하는 것으로 시작한다는 점을 제외하면 이전 버전과 유사하게 작동합니다. 그런 다음 366을 수정하여 (366 + n * (.2425-1))가되도록하고 목록의 곱을 가져옵니다. 또한 상수 366 및 -.7575가 365 및 .2425 대신 사용됩니다.


구 버전:

Pyth, 34 바이트

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

같은 생일을 가진 사람이 없을 수있는 두 가지 방법이 있습니다. 모두 다른 생일을 보내고 다른 사람이 2 월 29 일에 생일을 갖고 있지 않으며 다른 사람이 29 일에 생일을 갖고 다른 사람이 다른 것을 가질 수 있습니다. 정상적인 날에 생일.

첫 발생 확률은 (365 * 364 * ... 365-n + 1) / (365.2425 ^ n)입니다.

두 번째 발생 확률은 (365 * 364 * ... 365-n + 2) * .2425 * n / (365.2425 ^ n)입니다.

이들은 (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).

이것은 쌍이 없을 확률이므로, 적어도 한 쌍의 확률은 1에서 위의 숫자를 뺀 것입니다.

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

5

파이썬 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)결과를 제공합니다. 변경 .2425하는 fractions.Fraction(97,400)정확한 결과를 얻을 수 있습니다.


사이에 공백이 필요 없습니다 2and.
isaacg

당신은 넣어 수 없습니다 1/에 대한 g나눕니다 대신으로?
xnor

@ xnor Yep, 시간이 지남에 따라 이러한 것들이 사라집니다 :) 한 번 최적화 된 것은 나중에 차선책이됩니다.
orlp

당신은 소개 할 수 e=365 및 전자에 의해 전자 365 및 367을 대체 + 2
윌렘

@willem 그것은 짧지 않습니다.
orlp

2

파이썬 155 153 151 142 140 바이트

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)

호출 a(n)결과에 대해. 정확한 결과를 얻으려면 d분수로 포장하십시오 .

여기서 테스트

here 와 동일한 기술을 사용 하지만 상수를 수정했습니다.


2와 사이에 공백이 필요하지 않습니다 and.
isaacg

나는 그것이 98이라고 생각했습니다 (계산 실수
Tim

1

80386 기계 코드, 43 바이트

코드의 16 진 덤프 :

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

나는 다음 공식에서 시작했습니다 (상보 확률).

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

(여기서는 d = 366 * 400 - 303400 년의 일수가 있습니다)

다음은 그것을 구현하는 C ++ 코드입니다 (이미 약간 최적화되어 있습니다).

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

코드는 최소 수의 상수 (2 400 / d303 / d) 만 필요하도록 배열됩니다 . 나는 float공간을 덜 차지하기 때문에 유형을 사용합니다 (상수 당 4 바이트). 또한, 나는 다중 싶지 않았다 const2하여 k - 1(즉, 변환이 필요하기 때문 k - 1float); const2대신 코드가 반복적으로 뺍니다 .

다음은 어셈블리 언어 목록입니다.

    ; // 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.