(有点)小学生生日悖论


20

背景

生日悖论是概率论这颠覆流行的问题(大多数人)的数学直觉。问题陈述是:

给定N个人,他们中至少有两个生日相同(不考虑年份)的概率是多少?

通常通过完全忽略leap日来简化此问题。在这种情况下,N = 23的答案是P(23)≈0.5072972(作为常见示例)。链接的维基百科文章解释了如何得出这种可能性。另外,此Numberphile视频也做得很好。

但是,对于这个挑战,我们想做正确的事,不要忽略leap年。这有点复杂,因为现在需要添加2月29日,但是这个特定的生日比其他所有生日都少。

我们还将使用完整的leap年规则:

  • 如果一年可被400整除,则表示a年。
  • 否则,如果一年可被100整除,则不是a年。
  • 否则,如果将一年除以4,则表示a年。
  • 否则,这不是a年。

困惑?这意味着1700、1800、1900、2100、2200、2300年不是leap年,而是1600、2000、2400年(以及其他任何可以除以4的年份)。该日历每400年重复一次,我们将假设这400年中的生日统一分配。

现在N = 23的校正结果为P(23)≈0.5068761

挑战

给定一个整数1 ≤ N < 100,请N考虑the年规则,确定至少两个人中有相同生日的概率。结果应该是浮点数或定点数,至少精确到小数点后6位。截断尾随零是可以接受的。

您可以编写程序或函数,通过STDIN(或最接近的替代方案),命令行自变量或函数自变量获取输入,并通过STDOUT(或最接近的替代方案),函数返回值或函数(out)参数输出结果。

您的解决方案必须能够在几秒钟内产生所有99个输入的输出。这主要是为了排除使用大量样本的蒙特卡洛方法,因此,如果您以过于缓慢的深奥语言使用主要是快速且精确的算法,那么我愿意在这条规则上留有余地。

测试用例

这是完整的结果表:

 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.如果您要上学,那么应该考虑到生日并非全年都均匀分布。2. year年规则的确切相关性取决于对人类寿命的假设。是否打算在整个400年的周期内摊销?
彼得·泰勒

1
@PeterTaylor是的,假设在整个400年的周期内分布均匀。我从未说过这N个人同时活着。;)
Martin Ender 2015年

Answers:


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)。

这是没有对的概率,因此至少一对的概率为一减去上述数字。

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)给出结果。更改.2425fractions.Fraction(97,400)以获得准确的结果。


2和之间不需要空格and
isaacg 2015年

你不能把在1/g通过它,而不是鸿沟?
xnor

@xnor是的,随着时间的流逝,这些东西会丢失:)曾经是优化的东西后来变得次优。
orlp

您可以引入e=365并替换e的365和e + 2的367
Willem 2015年

@willem这并不短。
orlp 2015年

2

的Python 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一小部分。

在这里测试

使用与此处相同的技术,但修改了常量。


2和之间不需要空格and
isaacg 2015年

我以为是98岁(尽管我可能为计算错误而生气...)
蒂姆(Tim

1

80386机器代码,43个字节

代码的十六进制转储:

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 - 303是400年中的天数)

这是实现它的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;
}

代码经过排列,因此需要最少数量的常量-仅两个(400 / d303 / d)。我使用float类型来表示它们,因为它占用较少的空间(每个常量4个字节)。此外,我不想乘const2k - 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.