« 機能追加によるUIの悪化 | トップページ | PostgreSQL サーチパスの設定方法 »

2014.03.19

VBAのランダム関数

超久しぶりにVBAのお仕事。 1d20を作ったw

VBA内にはランダム関数として"Rnd"関数が準備されている。

そこで20面サイコロを振って1~20の乱数を生成する関数を作ってみた。

Function getVal1d20()
 getVal1d20 = Int((20 - 1) * Rnd() + 1)
End Function

ただ、このままだと乱数表としては致命的な欠陥を抱えることになる。
一回のセッションの中では乱数を得られるのだが、使うたびに同じ乱数配列になってしまうのだ。
たとえば14,11,6,6,8,20,18.....と出力されててこれはちゃんと乱数と言える。しかしこのVBAを保存したExcelを開き直して再度乱数を得ると、やっぱり14,11,6,6,8,20,18.....という順番で出力されるのである。

なお、これは不具合ではなく仕様である。

[MSDNより抜粋]
初期シード値が変わらない限り、一連の Rnd 関数が返す乱数系列は同じになります。これは、連続する Rnd 関数が乱数系列の中の直前の乱数をシード値として、次の乱数をそれぞれ生成するためです。

たいていの乱数シミュレータは「シード値」というものを元に、ランダムっぽい値を計算して出力している。 疑似乱数とも呼ばれるゆえんである。
シードが同じであれば得られる乱数配列は常に同じとなるし、それが狙いである。
紙の乱数表を知っている人なら、ランダム関数はこの乱数表をシードによって作成して、右上から順番に使っていく、というイメージになる。
ドラクエでいうところのホイミテーブルであるw

これを避けるためには初期シード値に乱数ないし変動要素を入れる必要がある。
ゲームなどでは、ロードするたびに同じ結果になってしまっては面白くないので、シードは変動させないといけない。(一方でホイミテーブルのように固定した複数のシードしか持たないのは、セーブ&ロードを繰り返すタイプのチート行為を防ぐためである)
一番簡単なのは現在時刻を取得し、それをシードにする方法だろう。
特に頑張る気がなければ、randomizeステートメントを宣言することで、VBAであればシステムタイマーをシードに持ち込める。

Function getVal1d20()
 Randomize
 getVal1d20 = Int((20 - 1) * Rnd() + 1)
End Function

これだけでOK。
たぶんこれが一番簡単な方法だろう。
もっとシビアな乱数が必要であれば、シード生成ルーチンとかを加えればいいのだろうが、そこまでやる必要のないものだったので、今回はここまで。

|

« 機能追加によるUIの悪化 | トップページ | PostgreSQL サーチパスの設定方法 »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/2022/59314945

この記事へのトラックバック一覧です: VBAのランダム関数:

« 機能追加によるUIの悪化 | トップページ | PostgreSQL サーチパスの設定方法 »