モンティホール問題に似てるけど、もう少しだけ数学的・哲学的に複雑な確率の問題である、眠り姫問題(Sleeping Beauty Problem)があります。JavaScriptで乱数を使って頻度的な確率を計算します
眠り姫問題
Wikipediaの記事 より:
実験の参加者である眠り姫は、実験の内容を全て説明され、一日経過後、薬を投与され日曜日に眠りにつく。
眠り姫が眠っている間に一度だけコインが投げられる。
コインが表であった場合、眠り姫は月曜日に目覚めさせられ、質問されたのち、再び薬を投与され眠りにつく。
コインが裏であった場合、眠り姫は月曜日に目覚めさせられ、質問されたのち、再び薬を投与され眠りにつく。そして翌日の火曜日にも目覚めさせられ、質問されたのち、再び薬を投与され眠りにつく。
この時投与される薬は一日の記憶を完全に忘却する記憶消去薬で、次に目覚めさせられるまで絶対に目覚めないという作用がある。 眠り姫が目覚め質問を受ける際、その日が何日であるか、以前に目覚めたことがあるかどうかは決して知ることができないとする。起こされた時にされる質問とは「コインが表だった確率は幾らか?」というものである。
どちらの場合でも、水曜日になれば眠り姫は目覚めさせられる。水曜日は質問を行わず、実験はそこで終了する。
コインの表裏の確率は1/2なのに対し、姫が回答すべき内容は「1/3わよ」というもの。姫が「おもてわよ」と答えた時の正答率を計算すると確かに1/3に一致する:
let [num_toss, num_try, num_correct] = [3000, 0, 0];
for (let i=0; i<num_toss; i++) {
switch (Math.floor(Math.random()*2)) {
case 0:
num_try++; num_correct++; break;
case 1:
num_try++; num_try++; break;
}
}
console.log(`toss: ${num_toss}, question: ${num_try}, bingo ${num_correct/num_try}`);
// -> toss: 3000, question: 4491, bingo 0.33600
極限の眠り姫
コインが裏だった場合に100万回と1回、目覚め、質問、忘却、睡眠を繰り返すというバリエーション。つまり約2700年間実験が続けられる。ニック・ボストロムによって策定された。
想像のとおり「おもてわよ」と答えると大体不正解になる:
let [num_toss, num_try, num_correct] = [3000, 0, 0];
for (let i=0; i<num_toss; i++) {
switch (Math.floor(Math.random()*2)) {
case 0:
num_try++; num_correct++; break;
case 1:
num_try += 1000001 ; break;
}
}
console.log(`toss: ${num_toss}, question: ${num_try}, bingo ${num_correct/num_try}`);
// -> toss: 3000, question: 1548001452, bingo 9.379e-7
100万人の眠り姫
10回連続でコイントスを行う。 全てが裏だった場合のみ、眠り姫の記憶を含めた完全な複製が100万人作られる。 眠り姫が目覚めたとき、眠り姫(そして眠り姫の完全な複製達)は「コインはすべて裏だったか?」と尋ねられる。 正解すれば、眠り姫は幸せに暮らせる。間違って答えれば拷問されて殺される。どう答えるべきだろうか
姫は「みなうらわよ」というと大体当たる。ついでにいうと「ちんくろーんわよ」というと大体当たる:
let [num_toss, num_try, num_correct] = [30000, 0, 0];
for (let i=0; i<num_toss; i++) {
switch (Math.floor(Math.random()*1024)) {
case 0:
num_try += 1000001; num_correct += 1000001; break;
default:
num_try++; break;
}
}
console.log(`toss: ${num_toss}, question: ${num_try}, bingo ${num_correct/num_try}`);
// toss: 30000, question: 29030000, bingo 0.9989675852566311
眠り姫の有罪判決
犯行現場に偶然居合わせた眠り姫は逮捕されてしまい、裁判にかけられている。
証拠を提出した後、裁判官は眠り姫に「この国にはこのような司法制度がある」と告げる。
「あなたはこの裁判所から連れ去られ、刑務所で拘束される。その間、裁判は続き目撃者の証言を聞く。有罪でないと判断した場合、明日の午後10時に刑務所から釈放される。有罪と判断された場合、明日の午後10時に、24時間の記憶の消去を行う人物が訪問する。刑務所で100夜を過ぎるまで繰り返され、午後10時に解放される。心理学者はこれがあなたに反省し更生する効果をもたらすと私たちに助言している」
眠り姫は自身が罪を犯していないことを知っている。しかし目撃者が嘘をつく可能性があり、裁判所が不当な判決に達する可能性がある。この可能性を20%と推定する。
しばらくして、眠り姫は刑務所で目を覚ます。その夜に釈放される可能性をどのように判断するか?
80%の確率で無罪を勝ち取る彼女は「こよいでるわよ」というと約5%の正答率となる。
let [num_toss, num_try, num_correct] = [3000, 0, 0];
for (let i=0; i<num_toss; i++) {
switch (Math.floor(Math.random()*5)) {
case 0:
num_try += 100; num_correct++; break;
default:
num_try++; num_correct++; break;
}
}
console.log(`toss: ${num_toss}, question: ${num_try}, bingo ${num_correct/num_try}`);
// toss: 3000, question: 60222, bingo 0.04981568197668626
眠り姫のベッド不足
眠り姫問題の実験を行なっている病院のディレクターが、すぐに必要ではないベッドを25%削減することを決定した。その結果眠り姫実験は次のように変更された。
コインが表の場合、ディレクターはカードの山からトランプのカードを一枚引く。ダイヤの場合、実験は眠り姫が目覚めることなくすぐに終了する。それ以外の場合、元の実験の通りに進む。
コインが裏の場合、ディレクターはカードの山からトランプのカードを一枚引く。ダイヤまたはハートの場合、2回の目覚めは1回に減少する。それ以外の場合は元の実験通りに進む。
もちろん眠り姫はどのカードが引かれたかを知らない。 眠り姫が目覚めた時、コインが表の確率は幾らか?
「おもてわよ」と答えると大体33%の正答率となる。実際条件付き確率を計算すると33%なので多分あってる:
let [num_toss, num_try, num_correct] = [30000, 0, 0];
for (let i=0; i<num_toss; i++) {
switch (Math.floor(Math.random()*2)) {
case 0:
switch (Math.floor(Math.random()*4)) {
case 0: break;
default: num_try++; num_correct++; break;
}
break;
case 1:
switch (Math.floor(Math.random()*2)) {
case 0: num_try++; break;
case 1: num_try ++; num_try++; break;
}
}
}
console.log(`toss: ${num_toss}, question: ${num_try}, bingo ${num_correct/num_try}`);
// toss: 30000, question: 33629, bingo 0.334621
水兵の子供の問題
水兵の子供問題と呼ばれる幻想的ではないバリエーションが、ラドフォードM.ニールによって紹介された。定期的に港の間を航行する水兵が関係している。ある港には、彼と一緒に子供をもうけたい女性がいる。海を隔てた別の港には彼と一緒に子供をもうけたい別の女性がいる。水兵は子供を1人にすべきか2人にすべきかを自分で決められないため、コイントスに任せる。表の場合は1人、裏の場合は2人にする。しかし、コインが表であったならどちらの女性と子供を作るか? 彼はこれを港の船員用のガイドを見て最初に現れた港の女性を選ぶことにした。あなたは彼の子供である。あなたが彼の唯一の子供である可能性はどのくらいあるか?
確率1/2でひとりっこのはずなのに、「ひとりっこだよ」の正答率は33%になってしまう。
let [num_toss, num_try, num_correct] = [10000, 0, 0];
for (let i=0; i<num_toss; i++) {
switch (Math.floor(Math.random()*2)) {
case 0:
switch (Math.floor(Math.random()*2)) {
case 0: num_try++; num_correct++; break;
case 1: num_try++; num_correct++; break;
} break;
case 1:
num_try++; num_try++; break;
}
}
console.log(`toss: ${num_toss}, question: ${num_try}, bingo ${num_correct/num_try}`);
// toss: 10000, question: 14953, bingo 0.33752