【GO】乱数生成法の確認


golangで乱数を扱うならmath/randパッケージがありますが、そのまま使っていいものかわからなかったので色々見て確認してみます。

 

ソース:math/rand/rng.go

Wikipedia:線形合同法Lagged Fibonacci 法メルセンヌ・ツイスタ

線形合同法

いきなり横道にそれますが乱数を使う時に「それって線形合同法じゃないよね?」といわれるような風潮があります。

この辺の理由も直感的に分かりにくかったので簡単に書いてみます。

A、B(C)、Mは英語の方のwikiを参考にしました。

2nの剰余は2n-1のマスクビットで得られるのでそっちを使います。

 

よく見る偶数と奇数が交互にくるってやつでしょうか。奇数*X+奇数の下位ビットをマスクしているので最下位ビットが交互に来るのはしょうがない。

実際には全てのビットを使うわけではないですが、こういった周期性が問題になるようです。

2の累乗を最大値にしてプロットしてみると周期性の問題がよくわかります。

GOの乱数

本筋に戻ってrandのソースを見ていきます。

これは線形合同法出典[4]にあったやつの変形っぽい。

231 - 1 = 48271  * 44488 + 3399Q = M / 48271R = M % 48271

 

Seed()を見ると607個のスタックに詰めつつ、273離れたインデックス2つ(tap,feed)を用意している。

そして乱数を出す部分ではtap,feedをずらしてx := rng.vec[rng.feed] + rng.vec[rng.tap]を返している。

これはラグ付フィボナッチ法のようです。

(273,607)の組み合わせは有名なものみたいなのでそこから調べればよかった。

 

ついでにシード値の設定は以下のようなものがある。

メルセンヌ・ツイスタ

math/randでは特に問題は起きなかったけど乱数作るならとりあえずメルセンヌツイスタじゃないかという風潮もある。

GitHubでライブラリを探すとcpmech/goslseehuhn/mt19937がありました。

必要があるならこれらを使ってみてもいいと思います。


コメントを残す

メールアドレスが公開されることはありません。