PHPでモンティ・ホール問題をシミュレーション

(1/1)
モンティ・ホール問題
サムネイル
モンティ・ホール問題とは、条件付き確率に関する有名な問題であるが、これがアメリカのテレビ番組「Let's Make A Deal」の中でゲームとして行われたことから、番組司会者モンティ・ホールの名前をとって、そう名付けられた。

(2026年3月15日)PHP8.5対応, pahooInputData.php導入
ゲームのルールは次の通り――
  1. プレイヤーの前にA・B・Cの3つのドアがあり、3つとも扉は閉じている。
  2. そのうち1つのドアの奥には景品の自動車があり、残りはハズレのヤギがいる。
  3. プレイヤーはドアを1つ選択する。ここではドアを開けない。
  4. 正解のドアを知っているモンティは、残ったドアのうちハズレのドアを開ける。
  5. モンティは「今なら選択を変更してもいいですよ」とプレイヤーに問いかける。
――プレイヤーは最初の選択を変更した方が得だろうか、変更しない方が得だろうか、どちらでも結果は変わらないだろうか。

目次

サンプル・プログラムの実行例

モンティ・ホール問題
サンプル・プログラムを実行すると、最初の選択を変更した方が得策であることが分かる。
ランダムに1万回のドア開け試行を行い、ドアを変えない場合のアタリ確率は $ \displaystyle \frac{1}{3} $ に、変えた場合のアタリ確率は $ \displaystyle \frac{2}{3} $ に収束することが分かる。試行回数は横軸に対数目盛として示している。
この結論については。いくつかの数学的解説があるが、条件付き確率の問題として、ベイズの定理を用いるのが王道である。

サンプル・プログラム

圧縮ファイルの内容
MontyHall.phpサンプル・プログラム本体
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
MontyHall.php 更新履歴
バージョン 更新日 内容
1.4.0 2026/04/05 PHP8.5対応, pahooInputData.php導入
1.3.0 2023/05/05 グラフにフッタを追加
1.2 2022/04/17 PHP8対応,リファラ・チェック改良
1.1 2018/06/05 JpGraph 4.2.1対応
1.0 2018/02/11 初版
pahooInputData.php 更新履歴
バージョン 更新日 内容
2.0.1 2025/08/11 getParam() bug-fix
2.0.0 2025/08/11 pahooLoadEnv() 追加
1.9.0 2025/07/26 getParam() 引数に$trim追加
1.8.1 2025/03/15 validRegexPattern() debug
1.8.0 2024/11/12 validRegexPattern() 追加

準備:pahooInputData 関数群

PHPのバージョンや入力データのバリデーションなど、汎用的に使う関数群を収めたファイル "pahooInputData.php" が同梱されているが、include_path が通ったディレクトリに配置してほしい。他のプログラムでも "pahooInputData.php" を利用するが、常に最新のファイルを1つ配置すればよい。

また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。

準備:JpGraphライブラリ

Moore.php

  53: // JpGraph:include_pathが通ったディレクトリに配置すること
  54: require_once('jpgraph/jpgraph.php');
  55: require_once('jpgraph/jpgraph_line.php');
  56: require_once('jpgraph/jpgraph_log.php');
  57: 

国内ではアシアル株式会社がサポートしている PHPで様々なグラフを生成するライブラリ「JpGraph」を準備する。JpGraph コミュニティ版は、QPL (Qt Free License) 1.0 ライセンスの下で、非営利目的で使用する場合に無償利用できる。
JpGraph は、サーバ側(PHP側)でグラフを画像データとして生成し、クライアントへ配信する。このため、GDライブラリが有効化されていることが前提である。
また、使用する機能によってインクルードする PHPファイルの種類が異なることに留意してほしい。

公式サイトからダウンロードしたファイルを解凍したら、include_path が通ったディレクトリに配置してほしい。

凡例やラベルに日本語を使う場合は、日本語フォント(TrueTypeファイル)を用意しておく。これについても、アシアル社のサイトに詳しい説明がある。
具体的には、フォント・ファイルを格納したフォルダを "jpg-config.inc.php" の定数 TTF_DIR および MBTTF_DIR に設定してやる。
実際に使うフォントは "jpgraph_ttf.inc.php" の定数 MINCHO_TTF_FONTPMINCHO_TTF_FONTGOTHIC_TTF_FONTPGOTHIC_TTF_FONT に設定されている。デフォルトで設定されているのは IPA の無償フォントで、文字情報技術促進協議会の IPAフォントから入手できる。

準備:各種定数など

MontyHall.php

  49: // 各種定数(START) ===========================================================
  50: 
  51: // エラー表示用フォント・ファイル名;各自の環境に合わせて
  52: define('FONT', '../../../../common/font/ipagp.ttf');
  53: 
  54: // JpGraph(各自の環境に合わせて)
  55: require_once('jpgraph/jpgraph.php');
  56: require_once('jpgraph/jpgraph_log.php');
  57: require_once('jpgraph/jpgraph_line.php');
  58: 
  59: // ドア識別子
  60: define('DOOR_LABEL', 'ABC');
  61: 
  62: // 試行回数
  63: $Counter = 10000;
  64: 
  65: // 各種定数(END) ===============================================================

3つのドアA・B・Cは定数 DOOR_LABEL に、試行回数は変数 $Counter にあらかじめ用意しておく。これらは変更可能である。
ドア識別子をA・B・C・D‥‥のように増やすことも可能で、増やしたとしてもドアを変更した方がアタリ確率がアップすることが分かる。

解説:モンティ・ホール問題

MontyHall.php

 142: /**
 143:  * モンティ・ホール問題(1回)
 144:  * @param   なし
 145:  * @return  array(介入なし,介入あり)
 146: */
 147: function MontyHall_once() {
 148:     $doors = array();
 149:     makeDoors($doors);
 150: 
 151:     // プレイヤーがドアを1つ選ぶ
 152:     $user = getRandomDoor($doors);
 153:     $doors[$user]['status'] = TRUE;
 154: 
 155:     // モンティがドアを1つ開ける
 156:     make_seed();
 157:     do {
 158:         $monty = getRandomDoor($doors);
 159:     } while ($doors[$monty]['hit'] == TRUE);
 160:     $doors[$monty]['status'] = TRUE;
 161: 
 162:     // プレイヤーはドアを変えない
 163:     if ($doors[$user]['hit'])   $res0 = TRUE;
 164:     else                        $res0 = FALSE;
 165: 
 166:     // プレイヤーはドアを変える
 167:     $user = getRandomDoor($doors);
 168:     if ($doors[$user]['hit'])   $res1 = TRUE;
 169:     else                        $res1 = FALSE;
 170: 
 171:     return array($res0, $res1);
 172: }

ドアを開ける作業は、ユーザー関数 MontyHall_once で行う。
冒頭に記したゲームのルールを、そのままプログラムに落とし込んである。

MontyHall.php

 175: /**
 176:  * モンティ・ホール問題
 177:  * @param   int   $n = 試行回数
 178:  * @return  array 確率配列
 179: */
 180: function MontyHall($n) {
 181:     $prob = array();
 182:     $cnt0 = 0;
 183:     $cnt1 = 0;
 184: 
 185:     for ($i = 0$i < $n$i++) {
 186:         list($res0, $res1) = MontyHall_once();
 187:         if ($res0)      $cnt0++;
 188:         if ($res1)      $cnt1++;
 189:         $prob[$i][0] = (float)$cnt0 / ($i + 1);
 190:         $prob[$i][1] = (float)$cnt1 / ($i + 1);
 191:     }
 192: 
 193:     return $prob;
 194: }

試行回数だけ繰り返し、そのときの確率を配列に格納していくのがユーザー関数 MontyHall である。

解説:グラフを描く

MontyHall.php

 196: /**
 197:  * グラフ描画
 198:  * @param   array  $items データ
 199:  * @param   int    $year_min 最小年
 200:  * @param   int    $year_max 最大年
 201:  * @return  object graphオブジェクト
 202: */
 203: function drawGraph($items, $n) {
 204:     $plotdata = array();
 205: 
 206:     // プロット配列を作成
 207:     for ($i = 0$i < $n$i++) {
 208:         $data_x[$i]  = $i;                      // 横軸
 209:         $data_y0[$i] = $items[$i][0];
 210:         $data_y1[$i] = $items[$i][1];
 211:     }
 212: 
 213:     // グラフ描画処理開始
 214:     $graph = new Graph(600, 500, 'auto');
 215:     $graph->SetScale('loglin', 0.0, 1.0);       // X軸:対数,Y軸:小数
 216:     $graph->SetBackgroundGradient('#FFFFCC','#FFFFCC', GRAD_HOR, BGRAD_MARGIN);
 217: 
 218:     // タイトル、凡例などの設定
 219:     $graph->img->SetImgFormat('png');
 220:     $graph->img->SetMargin(60, 60, 20, 40);
 221:     $graph->title->SetFont(FF_PGOTHIC, FS_NORMAL, 16);
 222:     $graph->title->Set('モンティ・ホール問題');
 223:     $graph->xaxis->title->SetFont(FF_PGOTHIC);
 224:     $graph->xaxis->SetTitle('試行回数','center');
 225:     $graph->yaxis->title->SetFont(FF_PGOTHIC);
 226:     $graph->yaxis->title->Set('当たり確率');
 227:     $graph->yscale->ticks->Set(0.1);
 228:     $graph->yaxis->SetTitleMargin(40);
 229:     // フッタを設定する.
 230:     $graph->footer->right->SetFont(FF_PGOTHIC, FS_NORMAL, 10);
 231:     $graph->footer->right->Set('produced by JpGraph');
 232: 
 233:     // ドアを変更しない場合
 234:     $plotdata[0] = new LinePlot($data_y0);
 235:     $plotdata[0]->SetLegend('ドアを変更しない');    // 凡例
 236:     $graph->Add($plotdata[0]);
 237: 
 238:     // ドアを変更する場合
 239:     $plotdata[1] = new LinePlot($data_y1);
 240:     $plotdata[1]->SetLegend('ドアを変更する');      // 凡例
 241:     $graph->Add($plotdata[1]);
 242: 
 243:     // 色指定は最後に
 244:     $plotdata[0]->SetColor('#0000FF');          // 
 245:     $plotdata[0]->SetWeight(2);             // 太さ
 246:     $plotdata[1]->SetColor('#FF0000');          // 
 247:     $plotdata[1]->SetWeight(2);             // 太さ
 248: 
 249:     // 凡例
 250:     $graph->legend->SetFont(FF_PGOTHIC);                    // フォント
 251:     $graph->legend->SetColumns(1);                          // 表示列数
 252:     $graph->legend->SetPos(0.15, 0.85, 'right', 'bottom');  // 位置
 253: 
 254:     return $graph;
 255: }

目的のグラフは、「PHPで対数グラフ(ムーアの法則)を描く」とは逆で、X軸が対数で、Y軸が確率という形である。ユーザー関数 drawGraph で描画する。

参考サイト

(この項おわり)
header