同じ誕生日の確率

  1. 同じ誕生日の確率

    1. 目的
      k人の人が集まったとき、その中に同じ誕生日の人がいる確率を計算します。実際の誕生日は1年の間均一ではありませんが、ここでは均一に分布するものととします。
       k人の確率を求めるだけなら、簡単な計算ですが、ここでは、k人までの確率をすべて求め、グラフ表示をしてみましょう。

    2. 計算式
      k人の中に同じ誕生日の人がいる確率は、1-(k人の誕生日がすべて異なる確率) になります。二人の誕生日が異なる確率は、二人目の誕生日が一人目の誕生日と異なればよいので(364/365)です。三人の誕生日が異なる確率は、二人の誕生日が異なり、さらに三人目の誕生日が前の二人の誕生日と異なればよいので、(364/365)*(363/365) となります。どうように、かんがえると、k人の誕生日がすべて異なる確率は (364/365)*(363/365)*.. *(365-k+1)/365 となります。したがって、k人の中に同じ誕生日の人がいる確率は

       P=1-(364/365)*(363/365)*.. *(365-k+1)/365

      となります。
    3. 配列
       javaでは配列を次のように確保します。prob が配列の名前で、ここでは、確率を記録するので、double型配列を確保します。newは配列などのオブジェクトを生成するときに利用するキーワードです。

       double prob[]=new double [80];

       これで、prob[0]、prob[1]、prob[79]、までが利用できます。

    4. グラフィックライブラリ
       グラフィック表示は、paint(Graphics g) という関数の内部で利用します。アプレット内部でpaint()関数を定義すると、ホームページを表示するブラウザが、アプレットを表示するときに、自動的に呼び出してくれます。
       グラフィクスは、アプレットの画面の左上を原点とします。座標は画素単位になります。高さ方向は上が0になり、y座標が大きくなると座標は下に移動します。


      ここで、利用するグラフィックス関数は

       g.drawLine(ox,oy,px,py)

      で、(ox,oy)から、(px,py)まで直線を描きます。
       ここでは、prob[]に記録された確率の値を棒グラフで表示します。(ox,oy)が原点の座標です。棒グラフは、3画素単位で表示します。prob[i] の値は 0..1 ですから、これを100倍して、0..100の長さに変換して表示します。
          int ox=50,oy=200;
          for(i=0;i<knum;i++){
            // (int)は整数型に変換
            g.drawLine(ox+i*3,oy,ox+i*3,(int)(oy-prob[i]*100));
          }

  2. プログラム

    1. 画面レイアウト
       n を入力し、確率を求めるのが目的ですから、前回の画面が、ほぼ、そのまま利用できます。ボタンは一つだけでよいので、「計算」のラベルを付けておきます。ウインドウの幅、高さは300程度が必要です。

    2. 計算プログラム
       計算ボタンを押すと probbutton_actionPerformed() が呼び出されるよう設定しておきます。この関数は確率を計算します。Integer.parseInt() は k の値を整数に変換し、nnumに代入します(前回やりました)。knumの値が80以上になると配列に記憶できなくなりますし、表示の範囲からはみ出してしまいます。そこで、80以上のときは、80に設定を変更します。
       確率の計算は、(365-i)/365 の値を順にかけていきます。また、prob[]に確率を記録します。ここで、1-prb の値が求める確率であることに注意してください。
      最後に repaint(); を呼び出し、再表示を要求しています。

      void button1_actionPerformed(ActionEvent e) {
        //確率計算
          knum=Integer.parseInt(knumField.getText());
          int i;
          double prb=1.0;
          //80人以上の場合、80にする
          if(knum>80) {
            knum=80;
            knumField.setText("80");
          }
          //確率計算、配列記録
          for(i=1;i<knum;i++){
            prb = prb*(365-i)/365;
            prob[i]=1-prb;
          }
          //k人の確率表示
          label1.setText(Double.toString(1-prb));
          //再表示 paint()を呼び出す
          repaint();
        }

    3. 表示関数
       paint()関数は、アプレットを表示するブラウザが必要なときに自動的に呼び出します。また、repaint()で呼び出すこともできます。
        public void paint(Graphics g){
          //グラフ表示
          int i;
          //グラフの原点
          int ox=50,oy=200;
          for(i=0;i<knum;i++){
            // (int)は整数型に変換
            g.drawLine(ox+i*3,oy,ox+i*3,(int)(oy-prob[i]*100));
          }
        }

    4. ソース
      import java.awt.*;
      import java.awt.event.*;
      import java.applet.*;
      
      public class Birthday extends Applet {
      
          private boolean isStandalone = false;
          int knum = 10;
          double prob[] = new double[80];
      
          private TextField knumField = new TextField();
          private Label label1 = new Label();
          private Button button1 = new Button();
      
          //コンポーネントの初期化
          public void init()  {
              //GUI部品の配置、設定
              knumField.setText("10");
              knumField.setBounds(new Rectangle(40, 40, 50, 20));
              this.setLayout(null);
              label1.setBackground(SystemColor.activeCaptionText);
              label1.setText("label1");
              label1.setBounds(new Rectangle(200, 40, 70, 20));
              button1.setLabel("計算");
              button1.setBounds(new Rectangle(120, 40, 60, 20));
              //ボタンを押したとき呼び出す関数を指定
              button1.addActionListener(new java.awt.event.ActionListener() {
                  public void actionPerformed(ActionEvent e) {
                      button1_actionPerformed(e);
                  }
              });
              this.add(knumField, null);
              this.add(button1, null);
              this.add(label1, null);
          }
      
      
          void button1_actionPerformed(ActionEvent e) {
              //確率計算
              int i;
              double prb = 1.0;
              
              //人数を読む
              knum = Integer.parseInt(knumField.getText());
      
              //80人以上の場合、80にする
              if (knum > 80) {
                  knum = 80;
                  knumField.setText("80");
              }
              //確率計算、配列記録
              for (i = 1; i < knum; i++) {
                  prb = prb * (365 - i) / 365;
                  prob[i] = 1 - prb;//i人が同じ誕生日となる確率
              }
              //k人の確率表示
              label1.setText(Double.toString(1 - prb));
              //再表示 paint()を呼び出す
              repaint();
          }
      
          public void paint(Graphics g) {
              //グラフ表示
              int i;
              //グラフの原点
              int ox = 50, oy = 200;
              //y軸は方向を反転する
              for (i = 0; i < knum; i++) {
                  // (int)は整数型に変換
                  g.drawLine(ox + i * 3, oy, ox + i * 3, (int) (oy - prob[i] * 100));
              }
          }
      
      }

    5. 実行
      kを設定し、「計算」ボタンを押します。kが80以上の時は80に設定を変更します。k人の中で少なくとも二人の誕生日が一致する確率を表示します。グラフは左から、2,3,4..人の場合の、一致する確立を表示します。23人で確率0.5になり、40人になるとほとんど確率1になります。



  3. 演習
     
    1. 演習1
      プログラムのソースをファイルに保存し、コンパイル&実行をして下さい。

    2. 演習2
       表示する確率が0.5を超えたら、色を変えて表示してください。色は

       Color cred=new Color(220,50,50);
       g.setColor(cred);

      で設定します。Color() の3個の整数が、順にR,G,B の色の強さになります。Color(0,0,0); で黒になります。