これは何?
go の乱数の実装を見て気づいたことを書く。
扱うのは、数値シミュレーションとかで使う方の乱数。
ハードウェア乱数の話も暗号用途の話もしない。
気づいたこと
生成
seed が 64bit しかない。64bit では足りない場面を見たことがあるので、えー64bit しかないのー、みたいな気分。
スキップしたい
複数のプロセスや goroutine で大量の乱数を発生させるとき、みんな同じ種で作っておいて、「n番目のプロセスは、rng.Uint64
を $n×2^{128}$ 回呼んだ後と同じ状態にしてからスタート」みたいなことができると便利だと思うんだけど、そういう機能はない模様。残念。
Source64
math/rand に Source64
というインターフェイスがあるけど、これの使い方が難しい。
ソースを見ると。
rng := rand.New(s)
みたいにするとき、s
は math/rand の Source
である必要があるわけだけど、 s
が Source64
を実装しているとそれも使ってくれる模様。
ソースを見たから気づいたけど、 math/rand の func New(略) の説明 で言及されてないから気づかないよ。
同期
グローバルな乱数は、mutex で保護されている。なので、その分遅い。手元で Uint32()
の時間を適当に測ったら、グローバルな方はグローバルでない方の 3倍以上の時間を使っていた。
グローバルでない乱数は mutex で保護されていない。その分速いんだけど、複数の goroutine で使うと死ぬかもしれないんじゃないかな。
アルゴリズム
C++・Java なんかは各種アルゴリズムを取り揃えていて用途によって使い分けることができるけど、 go の標準では一種類しか提供してない模様。
中身を見るとわりと無名なアルゴリズムを使っている印象。専門家からすると、あああの乱数ね、となるのかもしれないけど、私は他で使われているのを見たことがない。
ソースコードを見ると
/*
* Uniform distribution
*
* algorithm by
* DP Mitchell and JA Reeds
*/
とあるんだけど「メルセンヌ・ツイスタ」とか「Permuted congruential generator (PCG) 」みたいな名前は見当たらない。
Go の疑似乱数生成器は Goroutine-Safe ではないらしい(追記あり) によると
math/rand パッケージに実装されている擬似乱数生成器はラグ付フィボナッチ法(Lagged Fibonacci Generator)のバリエーションらしい。
とのこと。
中身を見ると、 64bit 整数 607個、つまり 4856バイトもの状態を持っている。メルセンヌ・ツイスタ(MT19937)の 2496バイトより多い。
初期化にあたっては 4856バイトを埋めなければならないのでわりと時間がかかる。
まあ初期化に時間がかかっても困らないけどね。
ソースコードを見る限り、周期はわからない。
「1万種類のシードで1万個発生させる」みたいなことが安心してできるのかどうか(つまり、あるシードからできた系列が、別のシードからできた系列の5千番以降と同じだったりすると困る)がよくわからない。
計算
計算は加減5回ぐらいと比較しかないけど、なにせ状態が 4856 バイトもあるのでメモリアクセスが遅くなりがちだと思う。
手元で Xoshiro256++ と比較してみたけど、大差ない感じだった。キャッシュに乗るかどうかの勝負なので条件によって変わるんじゃないかな。
まとめ
グーグル様監修なので変なことはないと思うけど、周期がわからないのは不安。
状態が 5kバイトぐらいあるのはやだなぁと思ったけど、そんな非力なコンピュータは相手にしてないのかなとも思った。
あと。名前知りたいなぁ。