【问题标题】:Calculating Birthday Probability for large numbers计算大数的生日概率
【发布时间】:2017-02-10 13:33:37
【问题描述】:

在满是 n 人的房间里,两个人生日相同的概率是 1-p。地点:

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

显然数字太大而无法解决这个方程,有什么创造性的方法来解决这个问题?

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

【问题讨论】:

标签: c probability


【解决方案1】:

冬青通心粉!什么节目!

无论如何,使用大型中间体计算此类事物的正确方法是 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;
}

【讨论】:

  • 这是最好的方法。次要:不需要(double) 演员表。
【解决方案2】:

您可以利用 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】:

    您不想计算全阶乘。相反,计算每一项并乘以结果。

    您不与他人分享生日的概率:

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

    【讨论】:

      【解决方案4】:

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

      任何两个人生日不同的概率是 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】:

        我会写一个如下所示的函数:

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

        【讨论】:

        • 对不起,我的意思是浮动,或双
        • 现在不应该了。从1开始,那么产品应该是好的。
        • 现在你明白了。
        【解决方案6】:

        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
        

        【讨论】:

        • @SeverinPappadeux Mars 看起来不错。 fact(1754) --> 1.979262e+4930。 Martian year Ceres "year" 也有好处,因为它有大约 1680 个地球日。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-03
        • 2018-10-01
        相关资源
        最近更新 更多