数学の思い出
典型的な文系人間で、数学全般にアレルギーがあった。まぁ、小学一年生のころに算数のテスト一発目で 0 点。「繰り上げ」の意味がわからなくて一人居残りさせられ、母真っ青という思い出がある。算数の素質に問題あり。 ^^;
数学の中でも特に「確率」なんてチンプンカンプンだった。大学受験の二次試験のとき、出題されないと思い勉強せずに受験に臨んだら、大問 3 問中の 1 問が確率の問題だと知ったときはマジで終ったと思った。 ^^; そんなこんなで大学では数学なんてやらなくてすむと思いほっとしていたが、統計を使わなくては卒論が書けないので、多少は理解するために読まざる終えなかった。そして、友人に勧められて読んだのがこの本。難しい数式が使われず、読み物のよう。なつかしいなぁ。でも、表紙が変わってる。 (@_@;) 改訂版か。
確率のはなし―基礎・応用・娯楽 (Best selected business books) 大村 平 by G-Tools |
さて、久しぶりに読むとおもしろい。必要に迫られないで自分の興味のままにわからないことを調べるというのであれば、数学の話もおもしろく感じる。今回は再度読むにあたり、折角なのでパソコンを使って実験し、確率を体感したいと思う。確率を理解する難しさは、特に一般化したときの話を具体的な問題に適用するときに、どれがどれに相当するのか訳がわからなくなるということが大きいが、それに加えて確率の実感がわきにくいということもあった。理論的に式を導いたり意味を理解するのは難しいと感じるが、ランダムを使って確率の動きを見ることくらいならできそうだ。
大数の法則
さて、確率の話となると「大数の法則」(p24) というのが最初の方にでてくる。大数の法則 – Wikipedia によると、
例えば「コイン投げ」、つまりゆがみも偏りもない"理想的なコイン"を投げて出る表裏を当てるゲームを行うとする。 (…) このとき、コイン投げの試行回数を限りなく増やせば、表が出る回数と裏が出る回数の比率はどちらも 1/2 に近づく。
シミュレーション:大数の法則 law of large numbers 中川雅央 - 滋賀大学 では、「確率 p の事象に対して n 回試行を行う」場合のシミュレーションを実際に試すことができる。コイン投げであれば、 p を 1/2 に設定した状態が相当する。
このシミュレーションでは、 確率 p を変動させて試すことができることからわかるように、「確率 p の事象」という抽象的なレベルでシミュレーションを表現してる。 JavaScript で書かれているのでソースを見ることができるが、「確率 p の事象」が起こったということをコード上で次のように表現している。
if(Math.random() < p) r++;
p は確率、 r は事象が起きた回数を表わしている。つまりこの表現では、ランダムな数値を生成し、それが設定した確率 p 以下であれば「事象が起こった」と見なすということ。
コイン投げのシミュレーション
さて、文系な脳みその自分は、いきなり抽象的な表現をされると訳がわからなくなる。そこで、はじめはもっと限定的で具体的な状況でのシミュレーションを行うことにした。つまり、上記で引用したように、「コインを投げてその裏表の回数を数える」ということをその言葉通り表現してみる。言語は Ruby を使う。
まずは、投げる対象である「コイン」を表現してみる。 コインには表と裏の二つの状態 (@omote) があり、コインを生成したときにその状態が決まる (setState) とする。シュミレーションをするためにコインを投げる (nageru) 必要があり、その結果.表か裏かの状態が変化する。 @omote を見れば、そのコインの現在の状態が表か裏か知ることができる。# コインを表わすクラス class Coin attr_reader :omote # 表かどうかの状態を表わす def initialize setState end # コインを投げる def nageru setState end private # コインの表裏の状態を設定する def setState @omote = if rand().round == 1 then true else false end end end
このコインを n 回数投げて、表が出た割合を調べてみればよい。
ところで、これだけだと先ほどのシュミレーションのより限定的な状況を表現しているるだけなので、少し変化をつけてみる。どこを変えるかと言えば、コインを n 回投げるだけではなく、「n 回投げることを x 回繰り返す」ようにしてみる。そして、それぞれの回において表が出た割合がどのくらいだったのかを記録し、適当な基準において結果を表示するようにする。イメージとしては、次のような結果が表示されるようにする。
コインを 20 回投げる試行を 100 回繰り返します。
0 - 15 % : 1
15 – 25 % : 3
25 – 35 % : 8
…
上記の場合、例えばコインを 20 回投げてすべて裏だったら 0 – 15 % の区間にカウントする。 20 回投げて 4 回表だったら 15 – 25 % の区間にカウントする。以下同じようにして、それを 100 回繰り返す。
設計
コードを書く前に、おおよその輪郭を考える。以下に示すように、コインは上記の Coin クラスで、それを投げる人 (Hantei クラス) がいるとする。コインを投げる人は、指定に従ってコインを投げることに専念し、コインを投げた結果は Result クラスに記録していく。
結果は集計され、最終的な表示に変換される。この集計を行う Counter クラスでは、Result に記録された結果を読み取り、対応したカウンターでカウントすることによって分析を行う。
これでコイン投げのシュミレーションと結果の表示までのモデル化ができた。効率的ではないけれど ^^; 、やりたいことをそのまま直接的に表現した。
実装
# コインを投げて結果を記録するクラス。 class Hantei def initialize(coin) @coin = coin @result = Result.new end # コインを n 回投げる。 def nageru(n) omote_num = 0 # 表が出た回数 for i in 0...n @coin.nageru omote_num += 1 if @coin.omote end # n 回中表が出た割合を結果として記録する。 @result.add(omote_num / n.to_f * 100) end # コインを n 回投げることを x 回繰り返す。 def repeat(x,n) puts "コインを #{n} 回投げる試行を #{x} 回繰り返します。" for i in 0...x nageru(n) end end # 結果を表示する。 def result @result.print end end # 結果を保持するクラス。 class Result def initialize @results = [] # 結果の配列 @counter = Counter.new # 集計をするためのもの end # 結果を追加する def add(result) @results << result end # 結果を設定した基準で集計する def analyze @results.each do |result| @counter.add(result) end end # 結果を表示する。 def print analyze @counter.result end end # 結果を集計するためのクラス class Counter INTERVAL = 10 include Enumerable def initialize @counterUnits = [] setCriterion end # 集計のための基準を設定する def setCriterion @counterUnits << CounterUnit.new(0,15) for i in 0..6 @counterUnits << CounterUnit.new(15+INTERVAL*i, 15+INTERVAL*i+INTERVAL) end @counterUnits << CounterUnit.new(85,100) end def each @counterUnits.each do |counterUnit| yield counterUnit end end # 結果を追加する def add(result) @counterUnits.find { |counterUnit| counterUnit.lowerLimit <= result and counterUnit.upperLimit >= result }.countUp end # 集計の結果を表示する def result @counterUnits.each do |counterUnit| printf("%2d -%3d%% : %3d\n", counterUnit.lowerLimit, counterUnit.upperLimit, counterUnit.counter) end end # 集計のための個々のカウンター class CounterUnit attr_reader :lowerLimit, :upperLimit, :counter def initialize(lowerLimit, upperLimit) @lowerLimit = lowerLimit @upperLimit = upperLimit @counter = 0 end def countUp @counter += 1 end end end
集計のための区間がハードコードされているけれど、とりあえず、まぁいいや。 ^^;
結果
上記のクラスを使うには、次のようにする。
hantei = Hantei.new(Coin.new) hantei.repeat(100,10) hantei.result
結果は、次のようになる。
コインを 10 回投げる試行を 100 回繰り返します。 0 - 15% : 1 15 - 25% : 9 25 - 35% : 14 35 - 45% : 21 45 - 55% : 23 55 - 65% : 14 65 - 75% : 11 75 - 85% : 6 85 -100% : 1
パラメータの値を変化させていくと、
コインを 20 回投げる試行を 100 回繰り返します。 0 - 15% : 0 15 - 25% : 4 25 - 35% : 10 35 - 45% : 32 45 - 55% : 13 55 - 65% : 38 65 - 75% : 3 75 - 85% : 0 85 -100% : 0 コインを 40 回投げる試行を 100 回繰り返します。 0 - 15% : 0 15 - 25% : 0 25 - 35% : 5 35 - 45% : 34 45 - 55% : 35 55 - 65% : 25 65 - 75% : 1 75 - 85% : 0 85 -100% : 0 コインを 100 回投げる試行を 100 回繰り返します。 0 - 15% : 0 15 - 25% : 0 25 - 35% : 0 35 - 45% : 27 45 - 55% : 54 55 - 65% : 19 65 - 75% : 0 75 - 85% : 0 85 -100% : 0 コインを 200 回投げる試行を 100 回繰り返します。 0 - 15% : 0 15 - 25% : 0 25 - 35% : 0 35 - 45% : 7 45 - 55% : 83 55 - 65% : 10 65 - 75% : 0 75 - 85% : 0 85 -100% : 0 コインを 500 回投げる試行を 100 回繰り返します。 0 - 15% : 0 15 - 25% : 0 25 - 35% : 0 35 - 45% : 0 45 - 55% : 96 55 - 65% : 4 65 - 75% : 0 75 - 85% : 0 85 -100% : 0
コインを投げる回数を増やすにつれて、 1/2 の確率に近付いているのがわかる。
0コメント:
コメントを投稿