読者です 読者をやめる 読者になる 読者になる

北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

モンティ・ホール問題

math scala

3囚人問題は有名だけど、こちらを見かけたのは恥ずかしながら初めて。

「プレイヤーの前に3つのドアがあって、1つのドアの後ろには景品の新車が、2つのドアの後ろにはヤギ(はずれを意味する)がいる。プレイヤーは新車のドアを当てると新車がもらえる。プレイヤーが1つのドアを選択した後、モンティが残りのドアの内ヤギがいるドアを開けてヤギを見せる。
ここでプレイヤーは最初に選んだドアを、残っている開けられていないドアに変更しても良いと言われる。プレイヤーはドアを変更すべきだろうか?」

モンティ・ホール問題

答えは、変更した方が勝率が高くなる。ベイズの定理できちんと考えれば答えは出るのだけど、非常に嘘っぽく感じてしまうのがこの問題の特徴*1。嘘っぽいなら実際やらせて確かめてみればいい。

import scala.util.Random._

def didWinGame(isChange: Boolean): Boolean = {
  val doors: Array[Boolean] = Array(false, false, false)
  doors(nextInt(3)) = true

  val playerChoice = nextInt(3)

  def tryChoosing(): Int = {
    val choice = nextInt(3)
    if (choice != playerChoice && ! doors(choice))
      choice
    else
      tryChoosing()
  }
  val montyChoice = tryChoosing()

  val finalAnswer =
    if (isChange) List(0, 1, 2).filter(
      (x: Int) => x != playerChoice && x != montyChoice
    )(0)
    else playerChoice

  return doors(finalAnswer)
}

def winningRate(doGame: () => Boolean) = {
  val total = 10000
  val winCount = (1 to total).map(_ => doGame()).count((x: Boolean) => x)

  winCount.toDouble / total.toDouble * 100
}

println("not change: %.2f %%".format(winningRate(() => didWinGame(false))))
println("change: %.2f %%".format(winningRate(() => didWinGame(true))))

実行するとほぼ理論値となる。

not change: 33.18 %
change: 66.38 %

*1:この嘘っぽさは、引用した問題文だけだと前提条件が説明不足だってのもあるけど