RubyでMersenneTwister(MT19937)
最近、一度習って完全に忘れかけていたRubyを再学習し始めました。
以前疑似乱数の性能についてプチ実験をした時にも出てきたメルセンヌ・ツイスタ(MT19937、配布ページhttp://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html)をRubyで書きうつしてみました。(ただし単一の整数の種による初期化と32ビット整数生成のみ実装)
本家のC製メルセンヌ・ツイスタと出力を比較して一応出力だけはあっているはずです。
ただし、オブジェクト指向言語であるRubyの特性も考慮して以下のようにしました。
・Randomクラスとして定義。
・Random.new(seed)で種seedを与えて初期化。
・Random.new(nil)の場合で初期化(本家で未初期化の場合と同じ)
・メソッドnextIntで32bit符号なし整数を得る。
以下コード
class Random def initialize(s) @N = 624 @M = 397 @CONST = [0,0x9908b0df] @MSB = 0x80000000 @LSB = 0x7fffffff @i = 1 @mt = Array.new(@N) if s==nil then @mt[0] = 5489 & 0xffffffff else @mt[0] = s & 0xffffffff end while @i<@N @mt[@i] = 1812433253 * (@mt[@i-1]^(@mt[@i-1]>>30)) + @i @mt[@i] &= 0xffffffff @i = @i+1 end end def nextInt #Twisting if @i==@N then @twi=0 while @twi < @N-@M @n = (@mt[@twi]&@MSB) | (@mt[@twi+1]&@LSB) @mt[@twi] = @mt[@twi+@M] ^ (@n>>1) ^ @CONST[@n&1] @twi += 1 end while @twi < @N-1 @n = (@mt[@twi]&@MSB) | (@mt[@twi+1]&@LSB) @mt[@twi] = @mt[@twi+@M-@N] ^ (@n>>1) ^ @CONST[@n&1] @twi += 1 end @n = (@mt[@N-1]&@MSB) | (@mt[0]&@LSB) @mt[@N-1] = @mt[@M-1] ^ (@n>>1) ^ @CONST[@n&1] @i = 0 end @i += 1 #Tempering @n = @mt[@i-1] @n ^= (@n >> 11) @n ^= (@n << 7) & 0x9d2c5680 @n ^= (@n << 15) & 0xefc60000 @n ^= (@n >> 18) return @n&0xffffffff end end
まずメルセンヌ・ツイスタのコードをじっくり読むこと自体が初めてだったんで、そこで苦労しました。なので、基本的にはほとんど書きうつしになっちゃってます。
あと、今ではより改善されたメルセンヌ・ツイスタが開発されているそうですね。
そういやそもそもRubyの疑似乱数生成器は何のアルゴリズムを使用しているのだろうか。
それではこの辺で〜(・ω・)ノシ