課題
「コイン投げのシミュレーション」と同じく,次のように表がM%出るコインをN回投げる反復試行を考えます.
- 整数Mを定める(ただし0 < M <100).
- コインを投げる回数Nを定める(ただし N > 0).
- 確率M/100(つまりM%)で表が出るコインをN回投げる反復試行を行う.
今回は上のように「N回投げる」反復試行をT回(T≧1)繰り返す(コインを全部でN・T回投げることになる). このシミュレーションについて,次のように結果を表示することにする.
- t番目の反復試行(t=0,1,2,...,T-1)において,表が出た回数をCtとする(0≦Ct≦N)
- C0,C1,C2,...,CT-1のヒストグラムH0,H1,...,HNを生成する.
ヒストグラムのj番目の値HjはT回の反復試行のうち,「表が出た回数がj回であった」反復試行の回数である(0≦Hj≦T).シミュレーション(T回の反復試行)において, 「表が出たのが0回だった」反復試行の回数がH0回, 「表が出たのが1回だった」反復試行の回数がH1回, : : 「表が出たのがN回だった」反復試行の回数がHN回 全反復試行の回数T( = H0 + H1 + … + HN) # 注: 反復試行=コインをN回投げる
- 表が出た回数(j)とその発生頻度(Hj)の一覧を結果として表示する(0≦j≦N)
補足
今回の課題で,一つの手順として,まずシミュレーションを先に完了させて,つまり 反復試行(=N回のコイン投げ)をT回すべて実行して,結果のデータC0,C1,...,CT-1の全てを記録し終わってから,それらのデータに基づいてヒストグラムを生成することはできます. しかしこの方法ではTが大きくなるほど保存しておくべきデータが増えていきます. 今回の課題のプログラムでは,ヒストグラムを構築するために,C0,C1,...,CT-1をすべてデータとして保持しておく必要はありません. 反復試行(=N回のコイン投げ)を実行しつつ,ヒストグラムを同時に構築していくことができます.
想定実行例
シミュレーションのプログラムは次のように動作することを想定しています(プログラムのファイル名をcoin_toss2.rbとします). 実現に必要な技術的要素については,以下で説明します.
$ ruby coin_toss2.rb 75 20 1000 0 0 1 0 : : 6 0 7 1 8 0 9 4 10 7 11 25 12 68 13 121 : : 18 64 19 23 20 3(注: 結果の表示を一部省略しています)
- 実行の際,(プログラムファイル名とともに),シミュレーションのパラメタとして,表が出る割合(M[%]),反復試行でコインを投げる回数(N),反復試行の回数(T)をこの順で指定します.上の例では表が出る確率M=75%,投げる回数N=20回,反復試行の回数T=1000を指定しています.
- 「表が出た反復試行回数」のヒストグラムが得られたら,そのデータを画面に表示します. 上で示したのは一例に過ぎません. シミュレーションのパラメタ(M,N,T)が同じでも結果は毎回変わりえます.
[option] ヒストグラムの表示方法
ヒストグラムを次のように(簡易的にでも)視覚化すると結果が分かりやすくなるでしょう.
0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 # 1 8 0 9 # 4 10 ## 7 11 ####### 25 12 ##########|####### 68 13 ##########|##########|##########|# 121 14 ##########|##########|##########|######### 155 15 ##########|##########|##########|##########|##########|####### 227 16 ##########|##########|##########|##########|###### 184 17 ##########|##########|##########| 118 18 ##########|###### 64 19 ###### 23 20 # 3 Average=14.951
この例では,M=75,N=20,T=1000で表の回数が15回(15/20=0.75)が最も多くなっています(15回がモード).また表が出た回数の平均値は約14.95回でした.プログラムではこのように平均値も結果として表示してみるとよいでしょう.
グラフは適切に縮尺を変えられるとよいでしょう. 次はM=30,N=20,T=100000で実行してみたときの例です.
0 # 92 1 ## 738 2 ######## 2818 3 ##########|######### 7213 4 ##########|##########|##########|### 12934 5 ##########|##########|##########|##########|##### 17940 6 ##########|##########|##########|##########|######## 19146 7 ##########|##########|##########|##########|# 16227 8 ##########|##########|######### 11480 9 ##########|####### 6568 10 ######## 3094 11 #### 1232 12 # 380 13 # 105 14 # 30 15 # 2 16 # 1 17 0 18 0 19 0 20 0 Average=5.997
これらの実現は必須ではありません.
プログラムヘッダ
次に示すコードをプログラムの先頭にコピーして入れておくようにして下さい(必要事項は記入して下さい).
=begin
所属:
氏名:
学生番号:
難易度(5段階評価):
感想など(任意)
=end
技術要素
プログラム作成に用いる可能性のある技術要素を示します.
- コマンドラインで指定される引数の読み込み
このプログラムでは実行時にシミュレーションのパラメタ(M,N,T)を指定するようにします. プログラムではこれを次のように実現できます.# 1,2,3番目の引数をそれぞれ整数に変換してM,N,Tとする # (注: 大文字から始まる名前はRubyでは定数) M = ARGV.shift.to_i # 表が出る確率=M/100 N = ARGV.shift.to_i # コインを投げる回数N T = ARGV.shift.to_i # 試行回数T
ここではコマンドライン引数の配列(ARGV)からデータを得ています. 「shift」は配列の先頭から要素を取り出すメソッドです. 次に「shift」の利用例を示します.ary = [2, ['foo', 'bar'], true] x = ary.shift # x == 2, ary==[['foo', 'bar'], true] y = ary.shift # y == ['foo', 'bar'], ary==[true] z = ary.shift # z == true, ary==[] w = ary.shift # w == nil, ary==[]
配列に対してshiftを適用すると,先頭の要素が取り出されて,それがshiftの値となります. 要素が取り出されるため,配列の要素は1個減ります.このとき,配列の残りの要素は一つずつ前にずれることになります. なお空の配列[]に対してshiftを適用するとnil(何もないことを意味するデータ)が得られます.
コマンドライン引数の配列ARGVの要素はすべて「文字の列」です(たとえ数字が並んでいても,それは数字列であって数値ではありません). コマンドライン引数を数値として扱うには数値化のメソッド(to_iやto_f)を利用します.「to_i」は整数,「to_f」は小数点数に変換するメソッドです. - 配列の初期化(Array.new)
要素の個数nと値vを指定して,要素がN個で全ての要素の値がvであるような配列を生成できます.hist = Array.new(N,0) # 配列histを準備する(要素の個数=N,全ての要素の値を0に設定)
- ランダムに整数を発生させる(rand)
kが正の整数であるとき,rand(k)は,0,1,2,...,k-1をランダムに返す. rand(k)は0,1,2,...,k-1をそれぞれ等確率(=1/k)で発生させるものとして扱うことにします. - 条件演算子と条件式
条件が成り立つかどうかによって,二つの値のどちらかをとる演算子が利用できます.# [条件演算子]:: (条件) ? (条件が成立するときの値) : (条件が成立しないときの値) # y = xの絶対値(xが正ならxそのもの,そうでないなら-x) y = (x > 0) ? x : -x
- フォーマットを指定した数値の表示
数値データを画面に表示するときに,表示の書式(フォーマット)を指定できます. フォーマットは「%」を使って指示します.次に例を示します.# %d: 整数(桁数は指定しない) # %3d: 整数(右詰め3桁) # %04d: 整数(右詰め4桁,3桁以下なら上位に0を付加する) # %f: 小数点以下も表示(桁数は指定しない) # %.2f: 小数点以下2桁まで表示
これらを使うと,次のようにフォーマット指示子の入ったメッセージに データを当てはめて表示することができます.# count=13,N=20,rate=0.65のとき # 「RESULT: 13/20(0.650)」と表示される(最後に改行される) puts "RESULT: %d/%d(%.3f)" % [count,N,rate]
この例の場合,フォーマット指示子が3つ入っていて, メッセージの後に「%」と組合せて指定している[count,N,rate]によって, 3つのフォーマット指示子に当てはめるデータを順に定めています. - 配列のデータの取得(size,max)
ary = [4,1,2,-5,3,4,9] n = ary.size # n == 7(要素の個数) m = ary.max # m == 9(要素の最大値)
- 文字データと関連する演算
MARK = '#' # ヒストグラムで棒を表す記号 SEP = '|' # ヒストグラムで区切りを表す記号 # # 文字列の演算 # + : 文字列「s」+文字列「t」でsとtを連結した文字列を作る # * : 文字列「s」*非負整数「k」でsをk個並べた文字列を作る # BAR = MARK*10 + SEP # BAR == '##########|'
- 商と剰余(divmod)
a.divmod(b)でaをbで割ったときの商と剰余が得られます.n = 31 q,r = n.divmod(5) # q == 6, r == 1
- 整数化演算(floor,ceil,round)
x = 1.4 y = x.floor # 切り捨て y == 1 z = x.ceil # 切り上げ z == 2 w = x.round # 四捨五入 w == 1
- 常用対数(Math.log10)
x = 100 y = 200 z = Math.log10(x) # z == 2.0 w = Math.log10(y).floor # w == 2
サンプルプログラム
次にサンプルプログラムを掲載しています.
サンプルプログラムをブラウザの画面で開いたときに文字化けしてしまう場合には, ダウンロードしてEmacs等で開いてみて下さい.