2015年10月4日日曜日

クラスの誕生日がかぶる確率を計算してみる[C#]

 クラスの中で同じ誕生日の人が2人いるという経験がある人は多いのではないだろうか。私は昔クラスの中で同じ誕生日の人が4人いたという経験がある。
 その確率を数式でゴリゴリ計算するのは面倒くさい。なのでその確率をクラスのシミュレーションを行うことで近似的に求めてみた。

1.結果

まぁ結果だけ知りたいという人のほうが多いだろうから先に結果を示しておく。クラスの人数は40人,1年の日数は365日に設定してある。ただシミュレーションはパーセント表示で少数第2位ぐらいまでしか合っていないと思うのでそこは自己責任でよろしくお願いします。
 1億回シミュレーションした結果はこうなった。

 俺の経験した「同じ誕生日の人がクラスに4人いる確率」は0.17%,つまり500回に1回ぐらいだということ。結構珍しかったみたい。
 とは言うものの満月の日は子供が産まれやすいとか,早生まれを避けたりするからもっと確率は上がると思う。
 ちなみに全員違う確率が10%と,結構低いのは誕生日のパラドックスといって結構有名。全員違う確率は次の式で表され,10.8768……%となるので多分式に間違いはないはず。

2.求め方

求める方法はモンテカルロ法。シミュレーションの内容は,「40人の誕生日をランダムに決め,被った回数を数える」ということをしている。言葉で説明しにくいのでプログラムを見ればわかるだろうと,とりあえずプログラムを貼っておきます。言語はC#。
/**
 * 2015/10/04
 * モンテカルロ法で誕生日がかぶる確率を計算する
 */
using System;

class Program
{
   // 実験回数
   const int N = 10000; 
   // クラスの人数
   const int MEMBER = 40;
   
   static void Main()
   {
      var ans = new int[MEMBER];         //シミュレーション結果を格納する配列
      var rand = new Random();         //乱数生成用クラス
      
      for(int i = 0 ; i < N ; i++ )
      {
         var table = new int[365];      //どの誕生日の人が何人いるかを格納する配列
         var max = 0;               //最も被った数が多い回数を数える
         
         //40人の誕生日をランダムに決めていく
         for(int j = 0 ; j < MEMBER ; j++ )
            table[rand.Next(365)]++;
         
         //被った人数の最大値を探す
         for(int j = 0 ; j < 365 ; j++ )
            max = max < table[j] ? table[j] : max;
         
         //結果に反映
         ans[max]++;
      }
      
      for(int i = 1 ; i < MEMBER ; i++ )
         Console.WriteLine("{0}人,{1}",i,1.0*ans[i]/N);
   }
}

 配列tableのi番目の要素には,1月1日から数えてi日目が誕生日の人の人数が格納されている。出力で1.0を掛けているのはint型をdouble型にするため。あと出力は割合になっています(パーセント表示ではない)。
 こういったシミュレーションもいいけれど,いつか数学的に求めてみたい。