计算大数的生日概率

时间:2017-02-08 15:58:21

标签: c probability

两个人在一个满是 n 的人的房间里过生日的概率 1-p 。其中:

p = 365! / 365^n(365 - n)!

显然这些数字太大了,无法解决这个问题,有什么创意可以解决这个问题?

我已经使用模拟以不同的方式解决了这个问题,但我认为公式可能更优雅。

6 个答案:

答案 0 :(得分:5)

冬青通心粉!真是个节目!

无论如何,使用大型中间体计算此类事物的正确方法是log()它们

p = exp(log(p))

log(p) = log(365!) - n*log(365) - log((365 - n)!)

对于阶乘,使用Gamma函数,G(n + 1)= n !,在C库中有非常方便的函数来计算log(G(x)):lgamma(x)

没有更多的循环,没有长的双打,没有bignum库,没有溢出......

代码

#include <math.h>
#include <stdio.h>

double b(int n) {
    double l = lgamma(365.0 + 1.0) -
               (double)n * log(365.0) -
               lgamma(365.0 - (double)n + 1.0);

    return exp(l);
}

int main() {
    double p = b(20);
    printf("%e %e\n", p, 1.0 - p);

    return 0;
}

答案 1 :(得分:3)

您不想计算全部因子。相反,计算每个术语并乘以结果。

您与以下人员共享生日的概率:

  • 1人:364/365
  • 2人:364/365 * 363/365
  • 3人:364/365 * 363/365 * 362/365
  • ...

鉴于此,您可以按如下方式计算p

int n = 30;
int i;
double p = 1;
for (i = 1; i < n; i++) {
    p *= (365 - i) / 365.0;
    printf("i=%d, p=%f\n", i, 1-p);
}

答案 2 :(得分:3)

你可以利用365!/(365-n)! = 365 * 364 * ... *(365-(n-1))

所以计算这个术语(让它是A = 365!/(365-n)!)你可以简单地用上面这些数字:

unsinged double A=1; // to make sure there is no overflow
for(int i=0;i<n;i++) A*=365-i;

更进一步:p = A / 365 ^ n =(364 * 363 * ... *(365-(n-1)))/ 365 ^(n-1)= 364/365 * 363/365 * ...(365-(n-1))/ 365。

所以p可以像这样计算:

unsigned double p=1;
for(int i=0;i<n;i++) p*= (365-i)/365.0;

以线性时间

我认为这应该有效:P

答案 3 :(得分:0)

我会写一个看起来像这样的函数:

double p(int n){
    double res = 1;
    while (n>0){
        res *= (365 - (n--))/365.0;
    }
    return res;
}

答案 4 :(得分:0)

另一种解决方案(近似值):

任何两个人没有同一个生日的概率是364/365。在一个包含n个人的房间里,有C(n,2)= n(n - 1)/ 2对人。所以:

p(n) = 364/365 ^ (n * (n-1)/2)

对于大于n = 100的值,您可以安全地使用下一个表:

n   p(n)
1   0.0%
5   2.7%
10  11.7%
20  41.1%
23  50.7%
30  70.6%
40  89.1%
50  97.0%
60  99.4%
70  99.9%
100 99.99997%
200 99.9999999999999999999999999998%
300 (100 − (6×10−80))%
350 (100 − (3×10−129))%
365 (100 − (1.45×10−155))%
366 100%
367 100%

答案 5 :(得分:0)

tgamma(n+1)非常接近n!。无需循环数百次,这会降低准确性,因为每次*/会在每次迭代时失去一点精度。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>

long double fact(int n) {
  return roundl(tgammal(n + 1));
}

double bd_prob(int n) {
  return fact(365)/(powl(365,n)*fact(365-n));
}

int main(void){
  // No problem with 365!
  printf("fact(365) %Le\n", fact(365));
  // No problem with 365 to the 365 power 
  printf("365^365 %Le\n", powl(365, 365));

  printf("prob(22) %f\n", bd_prob(22));
  exit(EXIT_SUCCESS);
}

输出

fact(365) 2.510413e+778
365^365 1.725423e+935  
prob(22) 0.524305