[BlueLeaf1336]> PROGRAM>

メルセンヌ・ツイスタ〜

historyTOP

2005/12/28:作成

overviewTOP

最初に。このページの内容に独自性はありません。完全無欠にパクリです。

翻訳もそのまま、本当にそのままやってるんでコメントなどもそのままです。おまけに思いっきりDelphi版の実装も見つけてしまったりして...

しかもそのソースを覗き見して

{$OVERFLOWCHECKS OFF}
{$RANGECHECKS ON}

をソースに追加してます。やっぱりオーバーフローするよなぁというのもコレを見たことで胸をなでおろしてます。

コードTOP

(*
    ===========================================================================
    mt19937ar.c
    mt19937ar.cの使い方 (2002/1, 2004/2改訂)
    (略)
    ===========================================================================
*)

unit mt19937ar;
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS ON}
interface

function GenRandInt32(): Cardinal;
function GenRandInt31(): Integer;
function GenRandReal1(): Double;
function GenRandReal2(): Double;
function GenRandReal3(): Double;
function GenRandRes53(): Double;

implementation

//  Period parameters
const
    MERSENNE_TWISTER_N = 624;
    MERSENNE_TWISTER_M = 397;
    MERSENNE_TWISTER_MATRIX_A   = $9908b0df;    //  constant vector a
    MERSENNE_TWISTER_UPPER_MASK = $80000000;    //  most significant w-r bits
    MERSENNE_TWISTER_LOWER_MASK = $7fffffff;    //  least significant r bits
    F_MASK = $FFFFFFFF;

var
    //  the array for the state vector (unsigned long)
    Mt: array[0..MERSENNE_TWISTER_N-1] of Cardinal;

    //  mti==N+1 means mt[N] is not initialized
    Mti: Cardinal = MERSENNE_TWISTER_N + 1;

//-----------------------------------------------------------------------------
//  initializes mt[N] with a seed (unsigned long)
procedure InitGenRand(const Seed: Cardinal);
begin
    Mt[0] := Seed and F_MASK;

    Mti := 1;
    while (Mti < MERSENNE_TWISTER_N) do
    begin
        Mt[Mti] := (1812433253 * (Mt[Mti-1] xor (Mt[Mti-1] shr 30)) + Mti);
        //  See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
        //  In the previous versions, MSBs of the seed affect
        //  only MSBs of the array mt[].
        //  2002/01/09 modified by Makoto Matsumoto

        Mt[Mti] := Mt[Mti] and F_MASK;
        //  for >32 bit machines

        Inc(Mti);
    end;
end;

//-----------------------------------------------------------------------------
function Max(const A, B: Integer): Integer;
begin
    if (A >= B) then Result := A else Result := B;
end;

//-----------------------------------------------------------------------------
//  initialize by an array with array-length
//  init_key is the array for initializing keys
//  key_length is its length
//  slight change for C++, 2004/2/26
procedure InitByArray(const Seeds: array of Cardinal);
var
    i, j, k: Cardinal;
    SeedsLen: Cardinal;
begin
    InitGenRand(19650218);

    i := 1;
    j := 0;

    SeedsLen := Length(Seeds);
    k := Max(SeedsLen, MERSENNE_TWISTER_N);

    while (k > 0) do
    begin
        Mt[i] := (Mt[i] xor ((Mt[i-1] xor (Mt[i-1] shr 30)) * 1664525))
               + Seeds[j] + j;          //  non linear
        Mt[i] := Mt[i] and F_MASK;      //  for WORDSIZE > 32 machines

        Inc(i);
        Inc(j);

        if (i >= MERSENNE_TWISTER_N) then
        begin
            Mt[0] := Mt[MERSENNE_TWISTER_N-1];
            i := 1;
        end;

        if (j >= SeedsLen) then
        begin
            j := 0;
        end;

        Dec(k);
    end;

    for k := MERSENNE_TWISTER_N - 1 downto 1 do
    begin
        Mt[i] := (Mt[i] xor ((Mt[i-1] xor (Mt[i-1] shr 30)) * 1566083941))
               - i;                     //  non linear
        Mt[i] := Mt[i] and F_MASK;      //  for WORDSIZE > 32 machines
        Inc(i);

        if (i >= MERSENNE_TWISTER_N) then
        begin
            Mt[0] := Mt[MERSENNE_TWISTER_N-1];
            i := 1;
        end;
    end;

    //  MSB is 1; assuring non-zero initial array
    Mt[0] := $80000000;
end;

//-----------------------------------------------------------------------------
//  generates a random number on [0,0xffffffff]-interval
function GenRandInt32(): Cardinal;
const
    //  mag01[x] = x * MATRIX_A  for x=0,1
    MAG01: array[0..1] of Cardinal = (0, MERSENNE_TWISTER_MATRIX_A);
var
    kk: Integer;
begin
    //  generate N words at one time
    if (Mti >= MERSENNE_TWISTER_N) then
    begin
        //  if init_genrand() has not been called,
        if (Mti = MERSENNE_TWISTER_N + 1) then
        begin
            //  a default initial seed is used
            InitGenRand(5489);
        end;

        kk := 0;
        while (kk < MERSENNE_TWISTER_N - MERSENNE_TWISTER_M) do
        begin
            Result := (Mt[kk] and MERSENNE_TWISTER_UPPER_MASK)
                   or (Mt[kk+1] and MERSENNE_TWISTER_LOWER_MASK);

            Mt[kk] := Mt[kk + MERSENNE_TWISTER_M]
                  xor (Result shr 1)
                  xor MAG01[Result and 1];

            Inc(kk);
        end;

        while (kk < MERSENNE_TWISTER_N - 1) do
        begin
            Result := (Mt[kk] and MERSENNE_TWISTER_UPPER_MASK)
                   or (Mt[kk+1] and MERSENNE_TWISTER_LOWER_MASK);

            Mt[kk] := Mt[kk + (MERSENNE_TWISTER_M - MERSENNE_TWISTER_N)]
                  xor (Result shr 1)
                  xor MAG01[Result and 1];

            Inc(kk);
        end;

        Result := (Mt[MERSENNE_TWISTER_N-1] and MERSENNE_TWISTER_UPPER_MASK)
               or (Mt[0] and MERSENNE_TWISTER_LOWER_MASK);

        Mt[MERSENNE_TWISTER_N-1] := Mt[MERSENNE_TWISTER_M-1]
                                  xor (Result shr 1)
                                  xor MAG01[Result and 1];

        Mti := 0;
    end;

    Result := Mt[Mti];
    Inc(Mti);

    //  Tempering
    Result := Result xor  (Result shr 11);
    Result := Result xor ((Result shl  7) and $9d2c5680);
    Result := Result xor ((Result shl 15) and $efc60000);
    Result := Result xor  (Result shr 18);
end;

//-----------------------------------------------------------------------------
//  generates a random number on [0,0x7fffffff]-interval
function GenRandInt31(): Integer;
begin
    Result := (GenRandInt32() shr 1);
end;

//-----------------------------------------------------------------------------
//  generates a random number on [0,1]-real-interval
function GenRandReal1(): Double;
begin
    //  divided by 2^32-1
    Result := (GenRandInt32() * (1.0 / 4294967295.0));
end;

//-----------------------------------------------------------------------------
//  generates a random number on [0,1)-real-interval
function GenRandReal2(): Double;
begin
    //  divided by 2^32
    Result := (GenRandInt32() * (1.0 / 4294967296.0));
end;

//-----------------------------------------------------------------------------
//  generates a random number on (0,1)-real-interval
function GenRandReal3(): Double;
begin
    //  divided by 2^32
    Result := (GenRandInt32() + 0.5) * (1.0 / 4294967296.0);
end;

//-----------------------------------------------------------------------------
//  generates a random number on [0,1) with 53-bit resolution
function GenRandRes53(): Double;
var
    a, b: Cardinal;
begin
    a := GenRandInt32() shr 5;
    b := GenRandInt32() shr 6;
    Result := (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
end;

//-----------------------------------------------------------------------------
const
    DefaultSeeds: array[0..3] of Cardinal = ($123, $234, $345, $456);
initialization
    //InitGenRand(19650218);
    InitByArray(DefaultSeeds);
end.

使い方TOP

こんな感じで。これは本家が乱数出力サンプル(mt19937ar.out)を試験用に出しているんですが、それと同じ内容を出力するためのコードです。

uses
    mt19937ar;

//-----------------------------------------------------------------------------
//  These real versions are due to Isaku Wada, 2002/01/09 added
procedure TForm1.Button1Click(Sender: TObject);
var
    i: Integer;
    c: Cardinal;
    r: double;
    FileName: String;
    FileStream: TFileStream;
    Line: String;
begin

    FileName := ChangeFileExt(ParamStr(0), '.txt');
    FileStream := TFileStream.Create(
                        FileName,
                        fmCreate or fmShareDenyWrite);
    for i := 0 to 999 do
    begin
        c := GenRandInt32();
        Line := Format('%10u ', [c]);
        FileStream.Write(Line[1], Length(Line));
        if (i mod 5 = 4) then
        begin
            Line := #13#10;
            FileStream.Write(Line[1], Length(Line));
        end;
    end;
    for i := 0 to 999 do
    begin
        r := GenRandReal2();
        Line := Format('%10.8f ', [r]);
        FileStream.Write(Line[1], Length(Line));
        if (i mod 5 = 4) then
        begin
            Line := #13#10;
            FileStream.Write(Line[1], Length(Line));
        end;
    end;
    FileStream.Free;

    Application.MessageBox(PChar(FileName + 'に出力完了'), PChar('情報'),
                MB_OK or MB_ICONINFORMATION);
end;

実行結果・ダウンロードTOP

1067595299  955945823  477289528 4107218783 4228976476
(990個の整数乱数を略)
2643151863 3896204135 2416995901 1397735321 3460025646

0.76275443 0.99000644 0.98670464 0.10143112 0.27933125
(990個の実数乱数を略)
0.33163422 0.85739792 0.59908488 0.74566046 0.72157152

MersenneTwisterTest.txt

ちゃんとあってるようです。

20051227MersenneTwisterTest.zip(167,509bytes)

EOFTOP