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

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

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

3囚人問題

こちらはモンティ・ホール問題より有名だと思う。

ある監獄にA、B、Cという3人の囚人がいて、それぞれ独房に入れられている。罪状はいずれも似たりよったりで、近々3人まとめて処刑される予定になっている。ところが恩赦が出て3人のうち1人だけ助かることになったという。誰が恩赦になるかは明かされておらず、それぞれの囚人が「私は助かるのか?」と聞いても看守は答えない。
囚人Aは一計を案じ、看守に向かってこう頼んだ。「私以外の2人のうち少なくとも1人は死刑になるはずだ。その者の名前が知りたい。私のことじゃないんだから教えてくれてもよいだろう?」すると看守は「Bは死刑になる」と教えてくれた。それを聞いた囚人Aは「これで助かる確率が1/3から1/2に上がった」とひそかに喜んだ。果たして囚人Aが喜んだのは正しいか?

3囚人問題

まず「Bが死刑になる確率」と「Bが死刑になることを知る確率」が別の物だと気がつくことが大事。通常は「Cが恩赦のとき、Bが死刑になることを知る確率」と「Aが恩赦のとき、Bが死刑になることを知る確率」は同じと考えるべき。しかし、この問題文のような質問の仕方をしてしまうと、「Cが恩赦のとき、Bが死刑になることを知る確率」は1であり、「Aが恩赦のとき、Bが死刑になることを知る確率」は1/2であって、ズレが起こってしまう。

この説明でも怪しいと思う人は、やっぱりシミュレーションして試すとよい。

import scala.util.Random._

val A = 0
val B = 1
val C = 2

def playRole(keeperDecision: Array[Boolean] => Boolean): (Boolean, Boolean) = {
  val whoAlives = nextInt(3)
  val isDeath: Array[Boolean] = (0 to 2).map((x: Int) => x != whoAlives).toArray
  val hearBsDeath: Boolean = keeperDecision(isDeath)
  (hearBsDeath, isDeath(A))
}

def whichIsDeath(isDeath: Array[Boolean]) = {
  if (! isDeath(C)) true
  else if (! isDeath(B)) false
  else nextBoolean()
}

val leakRate: Double = 0.8
def withNoAssumption(isDeath: Array[Boolean]) =
  if (isDeath(B) && nextDouble() < leakRate) true else false

def statistics(keeperDecision: Array[Boolean] => Boolean): Double = {
  val results = (1 to 10000).map(_ => playRole(keeperDecision)).toList
  val resultsKnowOfB = results.filter(_._1)
  val numOfDead = resultsKnowOfB.count(_._2)
  numOfDead.toDouble / resultsKnowOfB.length.toDouble * 100
}

println("normal situation: %.2f %%" format statistics(withNoAssumption))
println("ask which is death: %.2f %%" format statistics(whichIsDeath))

結果はほぼ理論値通りで、何も仮定しないと死刑となる確率は1/2になるが、この問題の仮定の場合は死刑となる確率は2/3のまま変化しない。

normal situation: 49.51 %
ask which is death: 66.85 %