プログラミングすれば一目瞭然、にわかに信じがたい「モンティ・ホール問題」をシミュレーションしてみよう!

アメリカのTV番組で生まれたという興味深い「モンティ・ホール問題」。

「3つのうち1つが当たりのくじ引き」で、あなたが1つを選んだ後に司会者がハズレを1つ減らしてくれます。この段階でチェンジ可能な場合「あなたはチェンジしますか?しませんか?」という問題。

パッと聞くと、もはや 2つ となった目の前のくじ、変えようが変えまいが当たる確率は 1/ 2 な気がします。

ではコンピュータでシミュレーション、今回は「こどもOS IchigoLatte JS」で。

まずは、

var hit = rnd(3);

当たりをセット、このプログラムの意味は以下。

  • var
    • メモるための箱を準備
  • hit
    • そのメモ箱の名前は “hit”
  • = rnd(3)
    • そのメモ箱の中にランダムな数 0〜2 をメモる

次は、

var chosen = rnd(3);

3つのうち1つを選択。

そしてここでハズレを一つ減らします。

var reduced = rnd(3);
while( (reduced==hit) + (reduced==chosen) ){
  reduced = rnd(3);
}

これの意味は、

  • reduced
    • メモ箱の名前は “reduced”
  • while( 〇〇 ){ □□ }
    • 〇〇〇 の間 □□□ をくり返す
  • (reduced==hit) + (reduced==chosen)
    • (reducedがhitと等しい) か (reducedがchosenと等しい)

つまり、

「reduced」をランダムに1つ選ぶんだけど「hit」や「chosen」と同じだったらそれは減らしたらいけないので、違うやつになるまで rnd(3) をしまくる。

という感じ。

では今回はチェンジしないバージョンということにして、

if(chosen==hit){
log("HIT!\n");
}else{
log("blank…\n");
}

結果を表示しちゃいましょう。

  • if( 〇〇〇 ){ □□□ }
    • もし 〇〇〇 なら □□□ する
  • chosen==hit
    • chosenがhitと等しい
  • log( “HIT!\n” )
    • “HIT!\n” と画面に出す(”\n”は”改行”の意味)
  • else{ △△△ }
    • でないなら △△△ する

こんな感じ。ここまでをまとめると最終的にシミュレーションプログラムは、

var hit = rnd(3);
var chosen = rnd(3);

// reduce one
var reduced = rnd(3);
while( (reduced==hit) + (reduced==chosen) ){
  reduced = rnd(3);
}

// show result
if(chosen==hit){
  log("HIT!\n");
}else{
  log("blank…\n");
}

こんな具合、”vi” でこれを打ち込んで ESCキー でセーブ&Exitしよう。

実行は

lash> ms .

こんな具合、「HIT!」が出れば当たり、「blank…」が出ればハズレ。何度も “ms .” すれば当たったりハズレたりを確認できるね。

さて、1,000回ほどシミュレーションしてみよう。1,000回も “ms .” するのはしんどいので、これもプログラミング。

var count=1000;

while(0 < count){
  var hit = rnd(3);
  var chosen = rnd(3);

  // reduce one
  var reduced = rnd(3);
  while( (reduced==hit) + (reduced==chosen) ){
    reduced = rnd(3);
  }

  // show result
  if(chosen==hit){
    log("HIT!\n");
  }else{
    log("blank...\n");
  }

  count = count-1;
}

太字の部分を加えました。はじめ “count” を 1,000 にして置いて、”0 < count” な間くり返します。”count” は下の方で “count = count-1″ しているので1ずつ減っていき、”0” になった時点で終了します。

lash> ms .
HIT!
HIT!
blank…
blank…
HIT!
blank…
blank…
:
:
:

実行するとこんな具合、1,000回シミュレーションされ、結果が大量に表示されます。あとはこれを集計すれば、、、でも手ではたいへん。なので集計もプログラミング。

var count=1000;
var hits=0;

while(0 < count){
  var hit = rnd(3);
  var chosen = rnd(3);

  // reduce one
  var reduced = rnd(3);
  while( (reduced==hit) + (reduced==chosen) ){
    reduced = rnd(3);
  }

  // show result
  if(chosen==hit){
    hits = hits+1;
  }else{
    // none
  }

  count = count-1;
}

log(hits, "\n");

はじめ “hits” を0にして置いて、当たったら “hits = hits+1” で1増やす。1,000回終わったら hits を表示、”\n”は改行の意味。

lash> ms .
319

319回当たりました。

lash> ms .
326

2回目は326。

lash> ms .
350

3回目は350、3回を平均すると331.7回。モンティ・ホール問題で当たる確率は、チェンジしないと33%ほどのようです。つまり、66%ほどはハズレる、言い換えると「チェンジしていれば66%ほどで当たる」という事になります。不思議ですね。

ここで、よくよくソースコードを見ると、

var reduced = rnd(3);
while( (reduced==hit) + (reduced==chosen) ){
  reduced = rnd(3);
}

この部分、せっかく while まで使って reduced(減らすやつ) を選んでいるのに、

if(chosen==hit){
  hits = hits+1;
}else{
  // none
}

結果に reduced は利用されていません。つまりこのプログラムで “reduced” は無くても結果に影響しない、あるだけでCPUやメモリを消費する無駄なモノです。

var count=1000;
var hits=0;

while(0 < count){
  var hit = rnd(3);
  var chosen = rnd(3);

  // show result
  if(chosen==hit){
    hits = hits+1;
  }else{
    // none
  }

  count = count-1;
}

log(hits, "\n");

つまりこうですね。このように無駄なモノを取り除くことを「最適化」と呼んだりします。C/C++などの言語ではコンパイラがこれを自動でやってくれますが、今回の IchigoLatte JS はやってくれないのでこのように手動で最適化しました。

lash> ms .
313

実行しても結果は最適化前と同じです。

つまりこの「モンティ・ホール問題」、チェンジしないのであれば「司会者が1つ減らす」という行為は必要ありません。単に3つの中から1つ選ぶので、確率は1/3。

「チェンジしていれば2/3で当たり」をあえてプログラミングしてみましょう。

if(reduced==0){
  if(chosen==1) chosen=2;
  else          chosen=1;
}else{
if(reduced==1){
  if(chosen==0) chosen=2;
  else          chosen=0;
}else{
  if(chosen==0) chosen=1;
  else          chosen=0;
}}

チェンジするプログラムはこんな感じ。

var count=1000;
var hits=0;

while(0 < count){
  var hit = rnd(3);
  var chosen = rnd(3);

  // reduce one
  var reduced = rnd(3);
  while( (reduced==hit) + (reduced==chosen) ){
    reduced = rnd(3);
  }

  // change chosen
  if(reduced==0){
    if(chosen==1) chosen=2;
    else chosen=1;
  }else{
  if(reduced==1){
    if(chosen==0) chosen=2;
    else chosen=0;
  }else{
    if(chosen==0) chosen=1;
    else chosen=0;
  }}

  // show result
  if(chosen==hit){
    hits = hits+1;
  }else{
    // none
  }

  count = count-1;
}

log(hits, "\n");

これを結果表示の前に入れ込んで、全体はこんな具合。前回と違って reduced が役に立っているので消すことはできませんね。

lash> ms .
693

実行してみるとだいたい 2/3 になっています。ただし、さっきの最適化したプログラムで 2.5秒 だったシミュレーションが、今回は 7.5秒 になりました。元のシミュレーションの方が良いですね。

ひとまず「モンティ・ホール問題」において、「当てたいのであればチェンジはすべき」というのが結論になりそうです。


この問題を知った時、「だったら最初から2つにして選ばせればいいじゃん」って思ってたわけですが、それだと当たる確率は1/2。あえて「3つの時に1つ選ばせて、減らしたあとでチェンジする」という手段を取ることで当たる確率が2/3に上昇するという、にわかに信じ難い くじ引きゲーム。

米村でんじろうサイエンスプロダクションチャンネル でも詳しく説明されています。(でんじろうさん、福井に引っ越してもっと広い実験場で楽しい動画を撮られてはいかがでしょう?)

それでも「えー、ほうとうに?」「いやー、ちがうでしょー」と思う人がいるかも。今回のシミュレーションとは違う意見がある方はご意見聞かせてください。

最初の くじの数 が 1億個 だったら?










.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s