二封筒問題のシミュレーション その1 基礎編
■ |
概要
|
最初は誤解して、「求めているのは、単に、相手を見たとき相手の額が自分の何倍かを調べると平均で 1.25 倍になるということ。交換したとしてもしなかったとしてもそうなる。じゃあ、交換すれば結果が変わるかというと変わらない。開封も未開封も関係ない。」と考えた。
しかし、その後、開封バージョンを実際にシミュレーションをすれば、確かに交換したら 1.25 倍になるということが(予想外にも)示せた。この「その1」では、その「驚き」までを示す。
■ |
はじめに
|
> |
あなたはゲームのプレイヤーです。胴元が、二つの見分けのつかない封筒を提示して、こう言います。「どちらか一方を取ってください。それぞれ金額の書かれた小切手が入っていて、取った方の金額を差し上げますよ。ちなみに、一方は他方の2倍の金額なんですけどね」
あなたは左の封筒を取りました。そして、開封しないまま、胴元が最初に言った言葉を思い出します。「ファイナルアンサーを決める前に、一度だけ、選択を変えるチャンスが与えられている」と。
あなたは考えます。いま手に取った封筒内金額をA円としよう。あっちの封筒の中にあるのは、2A円かA/2円か、どちらかだ。確率は五分五分。では、交換した場合の期待値を求めてみよう。2A/2+(A/2)/2=5A/4。交換しなかった場合の期待値はもちろんA。
そうか、Aがいくらであるかにかかわらず、交換すると期待値はA/4だけ多くなるぞ。交換しよう!
さあどうでしょう。この計算は正しいでしょうか。そして、交換すると実際に得なのでしょうか。(p.270-271)
|
もし、得だとすれば、交換したあと、また同じように考えるとまた交換すべきとなり、交換によって 5(5A/4)/4 に期待値が大きくなる。そして続けると交換するだけで無限に期待値が大きくなる。…
そんなことは起こるはずはない。三浦もここでは、交換しても得をすることはないと答えている。
ただし、封筒が未開封の場合と限る。…と。
> |
自分が選んだ封筒をあなたは交換しないまま開封しました。小切手に1万円と書かれていました。ここであなたは胴元が最初に言った言葉を思い出します。「ファイナルアンサーを決める前に、一度だけ、選択を変えるチャンスが与えられている。選択変更は、最初に選んだ封筒を開封して金額を見てからでもけっこうである」と。(p.274)
|
こうすると、確かに交換すると金額の期待値は A/4 だけ大きくなるというのだ。
■ |
私の誤解
|
何かがおかしい。1万円というのは仮の値で、そこはいくらでもいいはずである。元の封筒の額を A、もう一つの封筒の額を B としよう。もし、交換が得なら B/A を集計すれば、その値が 1 ではなく 1.25 などになるはずだ。…と私は考えた。
そこで作ってみたのが下の two_envelopes_1.py である。プログラム上では、元の封筒の額を a、もう一つの封筒の額を b、基準となる額を x、コインの表裏で a と b どちらに二倍入れるかを r (r == 0 で a に二倍、r == 1 で b に二倍)とした。
import random import numpy as np import argparse ARGS = argparse.Namespace() ARGS.trials = 10000 ARGS.x_max = 200 def main (): s = 0 for trial in range(ARGS.trials): r = random.randrange(2) x = random.randrange(1, ARGS.x_max + 1) if r == 0: a = x * 2 b = x else: a = x b = x * 2 s += np.array([a, b, x, b/a, a/b]) av = s / ARGS.trials print("E(A) =", av[0]) print("E(B) =", av[1]) print("E(X) =", av[2]) print("E(B/A) =", av[3], "(== 1.25)") print("E(A/B) =", av[4], "(== 1.25)") print("E(B)/E(A) =", av[1] / av[0], "(== 1.00)") if __name__ == '__main__': main()
実行結果は次のようになる。
$ python two_envelopes_1.py E(A) = 151.2315 E(B) = 149.3442 E(X) = 100.1919 E(B/A) = 1.2425 (== 1.25) E(A/B) = 1.2575 (== 1.25) E(B)/E(A) = 0.9875204570476388 (== 1.00)
B/A の平均は 1.25 近くになっているが、B の平均を A の平均で割ったものはほぼ 1.0 である。B にかならず交換したとしても得をしないことがわかる。
にもかかわらず E(B/A) は 1.0 ではない。これは少し不思議だが、E(X)/E(Y) が 1 になっても、E(X/Y) が 1 になるとは限らないのはよくあることだ。例えば、サイコロを二個振ってその目(X と Y)どうしを割ると、E(X)/E(Y) = 1 だが、E(X/Y) = 1.428166... となる (div_of_dice.py で答えを求めている)。
つまり、1.25倍といって求めているのは、単に、相手を見たとき相手の額が自分の何倍かを調べると平均で 1.25 倍(2倍と1/2倍の中間)になるということなのである。交換したとしてもしなかったとしてもそうなる。もちろん、交換すれば結果が変わるかというと変わらない…となる。
開封か未開封かは疑似問題に過ぎない。「隣の芝生は青い」というのの数学的根拠にはなるかもしれないが。…とひとまず私は結論したのだった。
ここで一応確認のため「1万円と固定した場合は異る」のだから、固定した場合のシミュレーションもやってみておくか…と作ったのが次の two_envelopes_2.py である。なお、少ない試行で答えを得るために 1万円 ではなく 50 円で試している。
import random import numpy as np import argparse ARGS = argparse.Namespace() ARGS.trials = 100000 ARGS.x_max = 200 ARGS.q = 50 def main (): s = 0 valid = 0 for trial in range(ARGS.trials): r = random.randrange(2) x = random.randrange(1, ARGS.x_max + 1) if r == 0: a = x * 2 b = x else: a = x b = x * 2 if ARGS.q != a: continue valid += 1 s += np.array([a, b, x, b/a, a/b]) av = s / valid print("valid: ", valid, "/", ARGS.trials) print("E(A) =", av[0]) print("E(B) =", av[1]) print("E(X) =", av[2]) print("E(B/A) =", av[3]) print("E(A/B) =", av[4]) print("E(B)/E(A) =", av[1] / av[0]) if __name__ == '__main__': main()
実行結果は次のようになる。
$ python two_envelopes_2.py valid: 531 / 100000 E(A) = 50.0 E(B) = 61.440677966101696 E(X) = 37.14689265536723 E(B/A) = 1.228813559322034 E(A/B) = 1.271186440677966 E(B)/E(A) = 1.228813559322034
なんと、E(B/A) はもちろん、E(B)/E(A) もほぼ 1.25 になっている! 交換は確かに得になるのだ!
なぜ、こんなことが起こるのだろう。それを調べるために…
if ARGS.q != a: continue
…の部分で、 ARGS.q != a すなわち 50 != a でなく 50 != a and 51 != a と変えてみると、E(B)/E(A) は 1.5 ぐらいになる。50 != a and 52 != a にすると、E(B)/E(A) は 1.25 に戻る。210 != a にすると、E(B)/E(A) は 0.5 になる。
どうも上限 ARGS.x_max が結果に関係しているようだ。また、a が偶数か奇数かも問題になっているようだ。まず、上限に関して調べるためにこの問題の額を実数を取るものに拡張してみる。それが「その2 実数拡張編」になる。
■ |
参考
|
まず、参考文献として最初に挙げるべきは…
一連の記事を書いたあと、ググったりして関連を調べた。二封筒問題は有名な問題で、私と同様の解答をしているものがすでにあるだろうと思ったが、まったく同じものは見つけられなかった。ただ、私に理解できない記述も多かったため、私のやったことを包含してさらに難しく論じたものもあるものと思われる。そもそも私が問題を理解できていないという可能性も認めねばならない。そういった中で私の結論に近そうなものを一つだけ挙げておくと以下になるか。
三浦俊彦の紹介する問題の関連も挙げておく。
|
■ |
著者
|
■ |
ライセンス
|
私が作った部分に関してはパブリックドメイン。 (数式のような小さなプログラムなので。)
自由に改変・公開してください。
■ |
配布物
|
今回の配布物は以下の zip ファイル。更新があれば下のリンクの中身は最新のものに置き変わっているはず。
GitHub にも登録してあるが、更新は、ここの更新のあと1日から3日遅れるかもしれない。
更新: | 2020-11-05--2020-11-12 |
初公開: | 2020年11月05日 22:51:23 |
最新版: | 2020年11月12日 23:01:28 |
2020-11-05 22:51:24 (JST) in シミュレーション Python | 固定リンク | コメント (4)
コメント
初公開: two_envelopes-20201105.zip。バージョン 0.0.1。
two_envelopes-20201105.zip に相当するものは↓からどうぞ。
https://github.com/JRF-2018/two_envelopes/releases/tag/v0.0.1
[cocolog:92328204](↓)に感想を書いたのでよければそちらもお読みください。
http://jrf.cocolog-nifty.com/statuses/2020/11/post-0c9790.html
>「二封筒問題のシミュレーション」という記事(のシリーズ)を書いた。開封後だと交換するほうが得というこの問題、最初に決める額の上限額より開封して見た額が小さく、かつ、偶数の額であるということ…それが 1.25 倍という結果を導いているというのが私の解答。<
投稿: JRF | 2020-11-06 00:12:42 (JST)
更新: 記事すなわち 00_README.txt の更新のみ。
「その3」の「結論」と「今後に向けて」それぞれに>領域 c に属すことと、1万円とわかることには違いがある。<からはじまる段落を足した。
アーカイブは更新してないが、GitHub は更新してあるので、ドキュメントのアーカイブが欲しい方は GitHub から master ブランチをダウンロードしていただきたい。
投稿: JRF | 2020-11-06 09:00:36 (JST)
更新: two_envelopes-20201107.zip。バージョン 0.0.2。
two_envelopes-20201107.zip に相当するものは↓からどうぞ。
https://github.com/JRF-2018/two_envelopes/releases/tag/v0.0.2
two_envelopes_5.py を足して、その3に説明を少し書いた。差分を取るところ、偶奇に関してどうするのかと悩んでいたが、とりあえずやってみたところ、そういうの以前に、期待値の差分という発想がまったくのあてはずれであることがわかったのだった。
投稿: JRF | 2020-11-07 03:17:51 (JST)
更新: two_envelopes-20201112.zip。バージョン 0.0.3。
two_envelopes-20201112.zip に相当するものは↓からどうぞ。
https://github.com/JRF-2018/two_envelopes/releases/tag/v0.0.3
誤字修正などちょこちょこと修正。
投稿: JRF | 2020-11-12 23:10:09 (JST)