PurePerlでメルセンヌ・ツイスタな話。Ver.0.1

レン鯖とかでの移植性を考えてPurePerlでメルセンヌ・ツイスターを実装してみようと言う話。

追記

404 Blog Not Found:perl - Math - Mersenne Twister を Pure Perlででバグ取りも含めて作りなおされたので、そちらを参照してください。

旧テキスト

☆Perlでメルセンヌ・ツイスタ | 徒然日記と言う先達がいらっしゃるのだが、実はここのソースはそのままコピペしただけでは動かないと言うミス。
これはご本人のせいではなくて、ソース中のビットシフト演算を意味する">>"がHTMLとして解釈されてしまって消えていて、それがコピーできずにそのまま実行するとSyntax errorを引き起こすのである。
それとまあ、ソースそのものが、C言語で書かれたhttp://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.cを直訳しただけで、Perl的なアレになってないと言うのもある。まさに機械翻訳。そりゃ、読むのは機械なんだけどさ。
なので、自分でhttp://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.cを読みながら(俺みたいな腐れPerlerでも、簡単なC言語くらいは読めるんだぜ?)、Perlに翻訳してみた。

MTpp.pm

文字コードはutf8Nだ!

package MTpp;
use utf8;
use strict;
use integer;
use bigint;
# 日本語じゃよー。ぎゃわー。

sub N { return 624; }
sub M { return 397; }
sub MATRIX_A { return 0x9908b0df; }
sub UPPER_MASK { return 0x80000000; }
sub LOWER_MASK { return 0x7fffffff; }

our @mt = ();
our $mti = &N+1;

sub init_genrand {
	my $s = shift;
	$mt[0] = $s & 0xffffffff;
	foreach my $mti ( 1..(&N-1) ) {
		$mt[$mti] = ( 1812433253 * ( $mt[$mti-1] ^ ( $mt[$mti-1] >> 30 ) ) + $mti );
		$mt[$mti] &= 0xffffffff;
	}
}

sub genrand_int32 {
	my $y = undef;
	my @mag01 = ( 0, MATRIX_A );
	unless ( $mti < &N ) {
		init_genrand(5489) if ( $mti == &N+1 );
		
		foreach my $kk ( 0..( &N-&M ) ) {
			$y = ( $mt[$kk] & UPPER_MASK ) | ( $mt[$kk+1] & LOWER_MASK );
			$mt[$kk] = $mt[$kk+&M] ^ ( $y >> 1 ) ^ $mag01[$y & 0x1];
		}
		
		foreach my $kk ( ( &N-&M )..( &N-1 ) ) {
			$y = ( $mt[$kk] & UPPER_MASK ) | ( $mt[$kk+1] & LOWER_MASK );
			$mt[$kk] = $mt[$kk+(&M-&N)] ^ ( $y >> 1 ) ^ $mag01[$y & 0x1];
		}
		my $kk = &N - 1;
		$y = ( $mt[$kk] & UPPER_MASK ) | ( $mt[$kk+1] & LOWER_MASK );
		$mt[&N-1] = $mt[&M-1] ^ ( $y >> 1 ) ^ $mag01[$y & 0x1];
		
		$mti = 0;
	}
	$y = $mt[$mti++];
	
	$y ^= ( $y >> 11 );
	$y ^= ( $y << 7  ) & 0x9d2c5680;
	$y ^= ( $y << 15 ) & 0xefc60000;
	$y ^= ( $y >> 18 );
	
	return $y;
}
1;

使い方

&MTpp::init_genrand($seed);で初期化して、
&MTpp::genrand_int32();を呼びだす。
返り値として、0から2^32-1の範囲の整数が戻ってくるので、それを適当に料理して乱数として扱って!

テスト

あー、なんかそれっぽくなってるから大丈夫じゃないかな。
十万個の乱数を発生させて、65536で割って表にしてみたけど、まあ、適当に散らばって出たと思うんだ。

キモ

☆Perlでメルセンヌ・ツイスタ | 徒然日記にも書いてあるんだけど。

Perlは32ビット符号なし整数が扱えないのかっ!

これに対する解決として、use bigintプラグマの導入と言う激烈馬鹿な手段を選択してしまい、MTの利点である「高速性」を大きく損なうことに成功。ダメじゃん!

これからの展開

  • bigintプラグマを使わない方向で頭を働かせる。
  • OO化する。