1つのサイコロを振って出た目の数の得点がもらえるゲームがあります。
(1)出た目が気に入らなければ1回だけ振り直すことを許すとする。
このゲームでもらえる得点の期待値が最大となるようにふるまったとき、
その期待値はいくらか・
(2)最高2回まで降り直すことができるとすると、
このゲームの得点の期待値は最大いくらか。
(1)の解答
サイコロを1回振って出る目の期待値は、
1回だけ振り直すことが許される時、最初に振って出た目が3以下のならばもう1度振り直して、
そのときの得点は上の計算結果よりを期待する。
最初に振って出た目が、4,5,6ならばその数を得点とする。
したがって、1回だけ振り直すことが許される場合の、期待値の最大値は、
どうしてこのような式が出るかいまひとつ意味が理解できません。(なぜなのか?)
詳しく教えていただけないでしょうか?宜しくお願いします。
ちなみに(2)も同じような解答で答えはでした
★希望★完全解答★
どうしてこのような式が出るかいまひとつ意味が理解できません。
僕もですね(笑)。理解できません(笑)。
ええと初めに言っておきますが、僕もこれ解くの挫折しました(笑)。と言うか
正攻法では解けないんです。
実は数日間この問題かかりっきりだったんですが、どうにも上手く行かない。かと言ってここである程度纏めて書いておかないと、またもや数日間浪費しちゃいそうです(笑)。それが怖い(笑)。
んで、まあ、もっと数学が得意な人の目に止まるやもしれん、と言う期待を込めて、ある程度背景にある考え方だけ解説しておきます。まあ、1番中心に、と言う事で。
僕もその解法見て確かに「アレ?」とは思ったんですよ。恐らくなおひさんも同じトコで詰まったんじゃないかと。それは次の一文です。
最初に振って出た目が3以下のならばもう1度振り直して
ここですね。
確かにサイコロを一回だけ振るなら期待値は
ですね。と言う事は3以下の目が出た場合、これは期待値以下です。ただし、3以下が出たからと言って、それが二回目にサイコロを振った場合も合わせて、最善の「1回目でこの目以下だったら振りなおす基準」として考えてイイのか・・・・?ここが問題なんですよ。
まあ、このテの問題ってのは数理経済学者とかが熱中している「最適化戦略問題」の一種なんでしょうが、いきなり上記の回答方法で「3以下だったら・・・・・・」ってのはあまりにも「決め打ち」に見えちゃうんですよね。だから分かります。
「何で3以下って決めつけんやねん!!!もう一回振りなおしてもエエんやったら2とか4の可能性もあるんやないか!?」
と言う意見はもっともです。僕も全く同じ事を感じたのです。
んで、結論から言うと上の方式以外じゃ(少なくとも僕が何種類か試した限りは)解けなかったんですよ。ハイ。んで、確かに回答の解は最大の期待値でした。
まずはちょっと状況を整理してみます。
例えば、
どんな状況であろうと2回必ずサイコロを振る、としてみます。ここで一体最終的に1の目が出る確率は何だ?と考えてみましょう。多分これは簡単ですし、暗算で出るとは思いますが、敢えてちょっと表にしてみますか。
通常では、一回目にサイコロを振る、と言う行為と二回目にサイコロを振る、と言う行為は独立だと仮定して、結局二回目に振ったサイコロは一回目の結果に影響無く転がるハズなんで、1が出る確率は1/6と計算します。
まあ、それはそれで構わないんですが、ここで上の表の意味をちょっと説明します。
一回目にサイコロを振って出た目をここでは
と表現して、二回目にサイコロを振って出た目を
としましょう。また、
が特定の目(つまり1~6)が出る確率を
と表現します。んで、上の表を見て欲しいんですが
と言うのは表の中には存在しません。代わりにあるのは
と言うステレンキョウな表記です。一体これは何なのか・・・・・・?
のような表記で表される確率を
条件付確率と呼びます。意味は日本語で言うと、
が特定の値を持った場合のが特定の値を持つ確率、って事ですね。つまり、ここでは
の目の出方がに左右されている、と考えているんです。
ちょっと注目して欲しいんですが、表内の
を全て足しても1にはなりません。その代わり、特定の
に付いての
の和は1となっています。あくまで
内で確率の公理が成り立っている、って事ですね。
さて、では
は一体どうやって求めるのか・・・・・・?
一般に、こう言う場合の
は次の式で求められます。
人によっては上記の式を
エビデンスと呼んだりするようですが、要するに全てのケースでの
の総和を求めます。
上の例では
となって、確かに独立事象として計算した解に一致するのが分かりますね。
とまあ、基本はここまで、です。では
一回振りなおす、と言う事柄をどうやって表現すればイイのでしょうか?例えば1回目に6が出たとします。すると、2回目に振りたくないのですが・・・・・・。これはまた表を作れば上手い事表現出来るのに気づくハズです。
そうですね。
としてしまえばイイんですね。そうすると、
の時必ず
なんでこれが
振りなおさない事、として表現している事となります。
では、
を求めてみましょう。
となりますね。
では他の
はどうなんでしょうか・・・・・・?とここでハタ、と立ち止まる。全部計算するのメンド臭えぞ(笑)。
以上は基本ではあるんですが、数学者も同じ事考えたんでしょうね(笑)。一気に計算してしまえる上手い方法が無いものか・・・・・・?極端な話、こう言う計算を行うジャンルを
確率過程と呼ぶんです(ママ、やったよ!!!とうとう確率過程に到達したよ・笑!!!)。
一般に、上の表を用いたエビデンスの計算は、行列を用いて
と表現します。ここで、
、
はそれぞれ全ての
、
を成分に持つベクトルです。また、行列Aは
を要素に持つ行列で、特に
推移確率行列と呼びます。
例えば上記の
の時だけ振りなおさないケースを、推移確率行列を用いた表現で記述すると次のようになります。
これを計算する事で、各々の
が求められるんですね。
まあ、これは良し、としましょう。そして、結局のトコ・・・・・・推移行列Aのカタチを色々変えて、しらみつぶしに調べていけばどの道期待値の最大値は求められるのです。
いつもだったらMaxima導入してここで実際計算してみるんですが・・・・・まあ、イイでしょう(笑)。
ココでも参考にしてご自分で試してみて下さい。
なお、期待値自体はベクトルを使って記述すれば、結局コレは
との内積
の事です。
すなわち、Maxima上で計算するんだったら、
とでもして入力すれば良いでしょう。
さて、ここまで見てきても、結構複雑ですよね。問題は、
を何らかの単一の関数で書き表せないのか、と言うのが焦点だとは思ったのですが・・・・・・。
平たく言うと、
の範囲ですと、
、
の範囲ですと
となるようです。
つまり、適当な関数、
を用いれば
として表現できて、つまり、
なんじゃないか、と踏んだのですが・・・・・・。
ここで、uは二値関数で、
の時1、
の時0を取る、と言う大変都合のイイ関数として設定して、最大値を求めよ、と言う題意に従って微分すれば何とかなるんじゃないか、と思ったんですね
(しかも二値関数であるuは
であるのは自明)。
ところがこの計算が上手く行かなかったのです。まず一つの理由として、問題の確率分布は離散分布だ、と言う事があげられます。連続関数用である微分はそもそも適さないようなのです。
では微分の代わりに差分ならどうだ?とも考えたのですが、差分が0の時離散関数が極大値を取るか、と言うとそれは分かりませんでした。ネットで色々検索かけてはみたんですが、そう言った情報は特に見つけられなかったのです。
他にも内積に着目して6次元ベクトル同士の内積の最大値を探す、と言うような手段も試みたのですが、同様に計算が複雑になり過ぎて上手く行きませんでした。
従って、今のトコロは、背後にある考え方はともかくとして、回答の解のようにして解くしか道はなさそうだな、と思います。
以前、
モンテカルロ法を紹介した事がありましたが、この問題の場合もSchemeで書いたモンテカルロ法のソースコードを紹介しておこうと思います。
Schemeでモンテカルロ法を行うにあたって、色々なちょっとしたテクニックを投入してるので、少々複雑になってます。んで、ココで全てを説明するのが困難なので(しかしながら、基本は
以前と同じです)、とりあえず下のソースをDrScheme上段のテキストエディタにコピペして、適当に名前を付けてファイルを[保存]して下さい。
(define Q3590
(lambda (n)
(Q3590-engine n 0 0 0 0 0 0 0 0 0 0 0 0)))
(define Q3590-engine
(lambda (n a b c d e f g h i j k l)
(if (zero? n)
(list (/ a g) (/ b h) (/ c i) (/ d j) (/ e k) (/ f l))
(let ((dice (+ (random 6) 1)) (threshold (+ (random 6) 1)))
(if (>= dice threshold)
(cond ((= threshold 1) (Q3590-engine (- n 1) (+ a dice) b c d e f (+ g 1) h i j k l))
((= threshold 2) (Q3590-engine (- n 1) a (+ b dice) c d e f g (+ h 1) i j k l))
((= threshold 3) (Q3590-engine (- n 1) a b (+ c dice) d e f g h (+ i 1) j k l))
((= threshold 4) (Q3590-engine (- n 1) a b c (+ d dice) e f g h i (+ j 1) k l))
((= threshold 5) (Q3590-engine (- n 1) a b c d (+ e dice) f g h i j (+ k 1) l))
(else (Q3590-engine (- n 1) a b c d e (+ f dice) g h i j k (+ l 1))))
(cond ((= threshold 1) (Q3590-engine (- n 1) (+ a (random 6) 1) b c d e f (+ g 1) h i j k l))
((= threshold 2) (Q3590-engine (- n 1) a (+ b (random 6) 1) c d e f g (+ h 1) i j k l))
((= threshold 3) (Q3590-engine (- n 1) a b (+ c (random 6) 1) d e f g h (+ i 1) j k l))
((= threshold 4) (Q3590-engine (- n 1) a b c (+ d (random 6) 1) e f g h i (+ j 1) k l))
((= threshold 5) (Q3590-engine (- n 1) a b c d (+ e (random 6) 1) f g h i j (+ k 1) l))
(else (Q3590-engine (- n 1) a b c d e (+ f (random 6) 1) g h i j k (+ l 1)))))))))
そして、[実行]ボタンを押した後、下段のインタプリタに次のように入力した後、[Enter]キーを叩いてプログラムを走らせます。
(Q3590 実行させたい回数)
例えば亀田のパソコンで100万回の試行をさせたら次のようになりました。
これは閾値(threshold:この場合は
)が1〜6までのそれぞれに於いて、モンテカルロシミュレーションで計算された期待値をリスト(カッコで括られた数字のセット)で返してくれているんです。
パッと目を引くのは、やはり閾値が
(左側から4番目)の場合ですね。この時、モンテカルロ法による100万回のシミュレーションでは
なんで、大体のトコ、理論値と合っているようです。
当然乱数ベースなんで、常に同じ値が出るとは限りませんし、また、あくまで近似値であって正確な値ではないのですが、それでも何度かプログラムを走らせてみて、他の閾値に期待値の最大値が移る事があるのかどうか試してみてください。
註:
- Schemeの基本に関しては3567参照。
- プログラムQ3590はプログラムQ3590-engineを呼び出し、各引数の初期値を与える為だけに存在している。nは外部入力だが、その他は全て0と言う初期値としてプログラムQ3590-engineに渡される。
- プログラムQ3590-engineは13個の引数を持つ。nはモンテカルロ法に於ける試行回数でプログラムを走らせる側が入力する数値、そしてそれはプログラムQ3590から渡される。引数a~fはそれぞれ、閾値に従ったサイコロの目の総和、引数g~lは、モンテカルロ法により、閾値が乱数で変動した場合、何回その閾値内でサイコロが振られたかの総数をカウントしている。
- (zero? n)は「nが0となったら~」の意。if節との組み合わせ方に付いては3567参照。
- Schemeでは(list 数値1 数値2 ・・・)と言うスタイルでリストを作る。nが0になった時(つまり計算が終了した時点で)リストを返すように設定している。なお、中身の(/ a g)、(/ b h)・・・・・・と言うのは設定したn以外の引数の意味を考えれば自明。と言うのも、例えばaは閾値が1の場合のサイコロの目の総和、gはそこでサイコロを振った総数なので、中置記法で書くと、これはa/gなので、まさしく平均(=期待値)を表している。つまりここで期待値のリストを返すように設定しているワケだ。
- 局所変数let。このletはこれが記述された場所からプログラムQ3590-engineの最後までの範囲で有効な変数を定義できる。Q3590-engineが一回起動し終わればあとはその変数は消去される仕組みになっている。ここではdice(サイコロの意)とthreshold(閾値の意)の2つを局所変数として定義している。なお、書式は(let ((変数名1 変数の中身1) (変数名2 変数の中身2)・・・) 計算の手順)となる。そして、今回はdice、thresholdの二つの値は乱数で決定している。
- Schemeでの乱数の書式は(random 上限値)。これで0以上上限値未満の整数を一様乱数として返す。すなわち、(random 6)で0~5の6個の整数を乱数として返す。これに1加える事によって、サイコロの目と閾値を与える事に成功しているわけだ。
- 以下の部分は再帰と言う方法で書かれている。再帰に付いても3567参照。ここでは末尾再帰と言われるちょっと特殊な形式でプログラムを記述してある。このお陰で、ハードウェアが許せば1億回の試行の計算も行える。ただし、プログラミングの文脈で考える必要は全く無くって、数学的には単に漸化式とか級数と同義である、と言うことを忘れない事。モンテカルロ法は数列で記述できるのだ。形式的にはn回目の操作はn-1の結果に何かした、と考えれば良い。
- 原理的にはSchemeではlet以下は1行で書く事も可能だった。が、見た目はともかく、目で追うには少々複雑に見える形の方が初心者には分かりやすいんじゃないか、と思い直し、そのままにした。何せ、基本的にはやってる計算は全て同じである為、どう言う操作をしているのか、目で追いやすいとは思う。
- 基本的な考え方は閾値とサイコロを乱数で設定して、サイコロの値が閾値以上だったらそのままサイコロの値を採用、そうじゃなかったら、また新しく乱数を生成してその値を採用するだけ、だ。ルーティンとしては大した内容じゃないと思う。
- 条件節はif節が有名、かつ基本だが、条件がたくさんになると、入れ子で記述する事も可能だが、それなりの表記を好んで使う場合が多い。普通のプログラミング言語だと、elseifで記述したり、ないしはcaseで記述する場合が多い。これで複数の条件を並べまくるのだが、Schemeの場合、これに相当するのがcondと言う命令。condはそのままcondition(条件)の意。Schemeでは(cond ((条件1) (結果1)) ((条件2) (結果2))・・・・・・(else (最後の結果)))と記述する。