zoukankan      html  css  js  c++  java
  • 生日悖论

    今天http://weibo.com/2887339314/BcqXD9OKz 发了个问题:300个人,至少5个人同一天生
    日的概率,博主用蒙特卡罗方法算出来了结果,一时兴起,写了个算精确结果的代码
    (似乎结果有点问题。另外浮点数可能有精度问题)。


    考虑计算问题的反面:至多4个人在同一天生日,难度在于可能性太多。所以考虑状态压缩。
    用一个5元组。(A[0], A[1], A[2], A[3], A[4])表示这365天中有A[0]天,没有不论什么人的生日。
    有A[1]天。这些天中有一个人的生日,其他相似。5元组被映射到一个浮点数上,表示相应
    事件发生的概率。


    从N=1起。不断更新5元组。
    N = 1的时候,(364, 1, 0, 0, 0) = 1.0
    N = 2的时候。(363, 2, 0, 0, 0) = 364./365 (364, 0, 1, 0, 0) = 1./365
    N = 3的时候。(362, 3, 0, 0, 0) = 364./365*363/365
                 (363, 1, 1, 0, 0) = 1./365**363/365
                 (363, 1, 1, 0, 0) = 364./365*2/365
                 (364, 0, 0, 1, 0) = 1./365*1/365
                 当中同样的tuple出现多次表示每次出现的值应该累加。


    以此类推,考虑到5元组中的元素和一定是365,所以能够将5元组化为4元组。


    // 递推计算
    #include <cstdio>
    #include <iostream>
    #include <unordered_map>
    using namespace std;
    typedef long long int64;
    
    const int AT_LEAST = 5;
    const int AT_MOST = AT_LEAST - 1;
    const int N = 300;
    const int DAY_OF_YEAR = 365;
    const int64 one = 1;
    const int64 two = 1LL << 9;
    const int64 three = 1LL << 18;
    const int64 four = 1LL << 27;
    const int64 five = 1LL << 36;
    
    const int64 FLAG[] = {0, one, two, three, four, five};
    
    int main()
    {
    	unordered_map<int64, double> mem;
    	mem[1] = 1;
    	
    	for (int i = 2; i <= N; ++i)
    	{
    		cerr << i << " " << mem.size() << endl;
    		unordered_map<int64, double> next;
    		for (auto& curr : mem)
    		{
    			int how[AT_MOST+1];
    			int sum = 0;
    			for (int64 val = curr.first, i = 1; i <= AT_MOST; ++i)
    			how[i] = val & 511, val >>= 9, sum += how[i];
    			
    			next[curr.first+one] += curr.second * (DAY_OF_YEAR - sum) / DAY_OF_YEAR;
    			
    			for (int i = 1; i < AT_MOST; ++i) if (how[i] > 0)
    			{
    				next[curr.first-FLAG[i]+FLAG[i+1]] += curr.second * how[i] / DAY_OF_YEAR;
    			}
    		}
    		next.swap(mem);
    	}
    	
    	double ans = 0;
    	for (auto& curr : mem) ans += curr.second;
    	printf("%.16f
    ", 1-ans);
    	
    	return 0;
    }
    
    // 蒙特卡罗验证
    #include <cstdio>
    #include <cstdlib>
    #include <ctime>
    using namespace std;
    
    int main()
    {
    	int total = 0;
    	srand(time(NULL));
    	for (int i = 0; i < 1000000; ++i)
    	{
    		int data[365] = {0};
    		for (int j = 0; j < 300; ++j)
    		++data[rand()%365];
    		int ok = 0;
    		for (int j = 0; j < 365; ++j)
    		if (data[j] >= 5) {ok = 1; break;}
    		total += ok;
    	}
    	printf("%.16f
    ", 1.*total / 1000000);
    	return 0;
    }
    


  • 相关阅读:
    新内核2.6.30编译完之后在目标板上看不到ttyS1
    使用memset、memcpy等函数需要包含string.h而不是strings.h
    软件模式之原则设计
    由编译错误看L. lxxxx的正确位置
    设计模式之策略模式
    make menuconfig提示'make menuconfig' requires the ncurses libraries.
    抽取界面用 XML 和 XSL 构建有良好适应性的 Web 应用前端
    .Net框架下的XSLT转换技术简介
    派生和继承
    UML 类图介绍
  • 原文地址:https://www.cnblogs.com/mqxnongmin/p/10624022.html
Copyright © 2011-2022 走看看