メルセンヌ・ツイスタを使った乱数ライブラリ(version 2.09)

coded by isaku@pb4.so-net.ne.jp

目次

アルゴリズム

アルゴリズムは

http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt.html
 を使った。
このページの mt19937ar.c を基にしている。

ztmtrand は高速化のために工夫が多数してある。 
16ビットのコンパイラ、かつ、ビッグエンディアンの場合は、
マクロ BIG_E をあらかじめ定義しておくこと。
しかし、手元に該当するビッグエンディアン・マシンがないため、
確認はしていない。 

NextMt() や NextUnif() などデフォルトの系列は初期化せずに
呼び出すと1を種として初期化してある。

各種の分布に従う乱数の参考文献:奥村晴彦著「C言語による最新アルゴリズム事典」1991 技術評論社

ファイル

zmtrand.htmlこのファイル
zmtrand.c ソースファイル
zmtrand.h ヘッダファイル
zmtsub.c インクルード専用のC/C++ソース(4箇所から読み込まれる)
test.c C言語用のテストファイル

ウィンドウズ版のファイル

$Cmd コマンドプロンプトを起動する
test.rc バージョン情報等。(セキュリティソフトの誤削除防止のため)
M.bat test.c をコンパイルするバッチファイル
Ms.bat test.c を分割コンパイルするバッチファイル
zmtrand.cpp C++言語用のソースファイル
test.cpp C++言語用のテストファイル
tools\ M.bat / Ms.bat が呼び出すバッチファイル群

デフォルト系列を使用する関数の説明

InitMt(S) 整数の種Sによる初期化
InitMtEx(K,L) 長さLの配列Kによる初期化
NextMt() 32ビット符号なし整数の乱数
NextMt64() 64ビット符号なし整数の乱数(16ビットコンパイラでは不可)
NextUnif() 0以上1未満の乱数(53bit精度)
NextInt(N) 0以上N未満の整数乱数
NextIntEx(N) 丸め誤差のない0以上N未満の整数乱数
NextBit() 0か1を返す
NextByte() 0から255の値を返す
NextChisq(N) 自由度νのカイ2乗分布
NextGamma(A) パラメータAのガンマ分布
NextGeometric(P) 確率Pの幾何分布
NextTriangle() 三角分布
NextExp() 平均1の指数分布
NextNormal() 標準正規分布(最大8.57σ)
NextUnitVect(V,N) N次元のランダム単位ベクトル
NextBinomial(N,P) パラメータN,Pの2項分布
NextBinormal(R,&X,&Y)相関係数Rの2変量正規分布
NextBeta(A,B) パラメータA,Bのベータ分布
NextPower(N) パラメータNの累乗分布
NextLogistic() ロジスティック分布
NextCauchy() コーシー分布
NextFDist(A,B) 自由度A,BのF分布
NextPoisson(L) 平均λのポアソン分布
NextTDist(N) 自由度Nのt分布
NextWeibull(A) パラメータαのワイブル分布

複数系列(0〜MSEQ-1までの系列番号を指定して使用する)

マクロ MSEQ を1以上に設定し、〜_m 関数を使うことにより、
大規模シミュレーションで、互いに影響を受けない系列を得ること
ができる。 系列の数を設定する MSEQ はメモリの許す限り、
いくらでも大きくできる。
16ビットコンパイラでは MSEQ の既定値は4、それ以外の場合は64である。
NextMt_m(m) 等の引数 m は 0 〜 MSEQ-1 である。
NextMt_m(m) 等の初期状態は -m を種として初期化されている。
複数系列の場合、6千桁以上ある全周期のうちの部分列が返る。
実用上、部分列同士に相関関係が生じる可能性は
ほとんどないと思われる。
MSEQ を 0 に定義するとプログラムが小さくなる。

OLD_MSEQ を定義すると以前のバージョンと同じになる。
この場合 NextMt_m(0) 等と NextMt() 等は同じ領域を共有する。
NextMt_m(0) 等の初期状態の種は 0 ではなく 1 で初期化されている。
従って以前のバージョンと新しいもは 0 番目の系列のみが違った値を返す。
一方 1 〜 MSEQ-1 番目の系列は以前のものと新しいものは同じ値を返す。

系列番号 M を指定して使用する関数の説明

InitMt_m(M,S) 整数の種Sによる初期化
InitMtEx_m(M,K,L) 長さLの配列Kによる初期化
NextMt_m(M) 32ビット符号なし整数の乱数
NextMt64_m(M) 64ビット符号なし整数の乱数(16ビットコンパイラでは不可)
NextUnif_m(M) 0以上1未満の乱数(53bit精度)
NextInt_m(M,N) 0以上N未満の整数乱数
NextIntEx_m(M,N) 丸め誤差のない0以上N未満の整数乱数
NextBit_m(M) 0か1を返す
NextByte_m(M) 0から255の値を返す
NextChisq_m(M,N) 自由度νのカイ2乗分布
NextGamma_m(M,A) パラメータAのガンマ分布
NextGeometric_m(M,P) 確率Pの幾何分布
NextTriangle_m(M) 三角分布
NextExp_m(M) 平均1の指数分布
NextNormal_m(M) 標準正規分布(最大8.57σ)
NextUnitVect_m(M,V,N) N次元のランダム単位ベクトル
NextBinomial_m(M,N,P) パラメータN,Pの2項分布
NextBinormal_m(M,R,&X,&Y)相関係数Rの2変量正規分布
NextBeta_m(M,A,B) パラメータA,Bのベータ分布
NextPower_m(M,N) パラメータNの累乗分布
NextLogistic_m(M) ロジスティック分布
NextCauchy_m(M) コーシー分布
NextFDist_m(M,A,B) 自由度A,BのF分布
NextPoisson_m(M,L) 平均λのポアソン分布
NextTDist_m(M,N) 自由度Nのt分布
NextWeibull_m(M,A) パラメータαのワイブル分布

複数系列(状態構造体を宣言して使用する)

状態構造体 state_t と、〜_r 関数を を使っても
複数系列を扱うことができる。マルチスレッドなどで使う。

state_t seq と宣言して InitMt_r() で初期化せずに NextMt_r(seq) 等を
呼び出すと、順に種を -MAX_MSEQ (= -1024) から減算させながら初期化される。 
MSEQ が MAX_MSEQ を超える場合は -MSEQ から減算させながら初期化される。
以前のバージョンで分割コンパイルした場合、NextMt_m() に悪影響を
与える不具合があったが現在は解消されている。
また、以前のバージョンでマルチスレッドの場合は排他処理をしていなかったので、
極まれに同じ種で初期化される不具合があったが現在は排他処理を行っている。

マルチスレッド等でスタック領域に状態構造体を確保するための
マクロ DeclMt(N) を用意した。
DeclMt は zmtrand.h で以下のように定義されている。

    #ifdef __cplusplus
     #define DeclMt(N) state_t N
    #else
     #define DeclMt(N) state_t N={{0}}
    #endif

このマクロは、Cでは index を 0 に初期化する。
C++では宣言するだけだが、コンストラクタが初期化する。
そして、この構造体を 〜_r 関数に、渡して使用する。

使用例:

#include <stdio.h>
#include "zmtrand.c"
int main(void) {
    int i; DeclMt(seq);

    for (i=0;i<10;i++) printf("%10.6f\n",NextUnif_r(seq));
    return 0;
}

また、マルチスレッドのためC++でクラスの中に状態構造体を
宣言する必要がある場合は、

class Test {
    state_t seq;
    ...
}

のようにしておく。そして、〜_r 関数に構造体を渡して使用する。

状態構造体 Z を宣言して使用する関数の説明

InitMt_r(Z,S) 整数の種Sによる初期化
InitMtEx_r(Z,K,L) 長さLの配列Kによる初期化
NextMt_r(Z) 32ビット符号なし整数の乱数
NextMt64_r(Z) 64ビット符号なし整数の乱数(16ビットコンパイラでは不可)
NextUnif_r(Z) 0以上1未満の乱数(53bit精度)
NextInt_r(Z,N) 0以上N未満の整数乱数
NextIntEx_r(Z,N) 丸め誤差のない0以上N未満の整数乱数
NextBit_r(Z) 0か1を返す
NextByte_r(Z) 0から255の値を返す
NextChisq_r(Z,N) 自由度νのカイ2乗分布
NextGamma_r(Z,A) パラメータAのガンマ分布
NextGeometric_r(Z,P) 確率Pの幾何分布
NextTriangle_r(Z) 三角分布
NextExp_r(Z) 平均1の指数分布
NextNormal_r(Z) 標準正規分布(最大8.57σ)
NextUnitVect_r(Z,V,N) N次元のランダム単位ベクトル
NextBinomial_r(Z,N,P) パラメータN,Pの2項分布
NextBinormal_r(Z,R,&X,&Y)相関係数Rの2変量正規分布
NextBeta_r(Z,A,B) パラメータA,Bのベータ分布
NextPower_r(Z,N) パラメータNの累乗分布
NextLogistic_r(Z) ロジスティック分布
NextCauchy_r(Z) コーシー分布
NextFDist_r(Z,A,B) 自由度A,BのF分布
NextPoisson_r(Z,L) 平均λのポアソン分布
NextTDist_r(Z,N) 自由度Nのt分布
NextWeibull_r(Z,A) パラメータαのワイブル分布

MEXP

マクロ MEXP は乱数の周期の長さを決めている。既定値は 19937 である。
メモリを節約したい場合は MEXP を 2281 か 1279 に定義する。
または MEXP2281 か MEXP1279 を定義する。
この場合遅くなることが多い。これを変更すると関数の結果が大きく変わる。

FAST_UNIF

マクロ FAST_UNIF を定義してコンパイルすると、NextUnif 等の
精度が53/52ビットから32ビットに下がるが高速になる。
これを定義すると浮動小数点数を使用している関数の結果が大きく変わる。

UNIF_53 / UNIF_52

16ビットコンパイラの場合は FAST_UNIF と ORIGINAL を定義しなければ
NextUnif 等の精度は52ビットになる。具体的には確率2分の1で53ビットの結果と
比較して約1京分の1(1.1102e-16)小さな結果を返す。
16ビットコンパイラでも UNIF_53 を定義すると53ビットになるが同時に遅くなる。
32/64ビットコンパイラの場合は FAST_UNIF を定義しなければ NextUnif 等の
精度は53ビットである。しかし UNIF_52 を定義すると52ビットになる。
ただし32/64ビットコンパイラで UNIF_52 定義した場合、
精度が落ちるばかりでなく速度も遅くなることが多い。

STABLE_SPEED / STABLE

通常は数列をまとめて 624 個発生させるので、乱数生成時間は 624 回に1回は
非常に長くなり、それ以外の場合は瞬間的に短くなる。
マクロ STABLE_SPEED または STABLE を定義すると、平均速度は遅くなるが、
一つずつ発生させるので一つの応答速度の最悪値を飛躍的に短くできる。
乱数の応答時間を一定に抑えたい場合に STABLE_SPEED または STABLE を定義する。
STABLE_SPEED と STABLE は ORIGINAL や HAVE_SSE2 等に比べて優先順位が高い。

ORIGINAL

マクロ ORIGINAL を定義すると mt19937ar.c に忠実になる。
mt19937ar.c との違いは、状態生成ループを関数にしていることである。
理由は NextMt() NextMt_r()にループを書くと、古いCコンパイラで
インライン展開ができなくなるからである。
なお、状態生成部分を関数にして少し速くなった。
zmtrand の version 2.01 〜 2.04 の場合、
ORIGINAL を定義すると既定の種を mt19937ar.c に従って5489にしていたが、
version 2.05 以降は、ORIGINAL を定義しても既定の種は1になる。

Unif() は mt19937ar.c の genrand_res53() と同じになる。
ただし、FAST_UNIF を定義すると、genrand_real2() と同じになる。

ORIGINAL は zmtrand.c との実行時間の比較のために用意した。
例えば TurboC 1.5 で ORIGINAL を定義して test.c をコンパイル・実行すると、
NextMt()は約 8.9 倍遅くなり NextUnif()は約 8.6 倍遅くなり、
プログラムは 22 バイト小さくなる。
また、コンパイル時の消費メモリは 188 バイト減る。

GNUC の場合、後に説明する既定の MAG 以外の NEG / SLR / MUL / CND を
定義して -O4 で最適化すると SSE2 を使用したプログラムが作られる。

ORIGINAL は STABLE_SPEED や STABLE の次に優先順位が高い。

NO_INLINE

マクロ NO_INLINE を定義すると NextMt_r や NextUnif_r 等を
インライン関数にしていたのを通常の関数にする。

zmtrand version 2.05 以前では NO_INLINE を定義しても、
VC++とGNUCで /O2 や -O4 とするとインライン展開していたが、
version 2.06 以降ではインライン展開しないようにした。

HAVE_SSE2 / SSE2 / HAVE_SSE3 / SSE3 / HAVE_AVX2 / AVX2

マクロ HAVE_SSE2 または SSE2 を定義すると、
SSE2 を使った高速なプログラムができる。 

マクロ HAVE_SSE3 または SSE3 を定義すると、
SSE2 の _mm_loadu_si128() を SSE3 の
_mm_lddqu_si128 に変更したプログラムができる。 
CPU とコンパイラの組み合わせにより HAVE_SSE2 より
速くなったり遅くなったりするので推奨しない。

マクロ HAVE_AVX2 または AVX2 を定義すると、
AVX2 を使ったより高速なプログラムができる。 
ただし、GNUC では SSE2 に対して AVX2 にしてもあまり速くならない。
また、2013 以降の VC++ で MEXP が 1279,2281 の場合。
・Core i3 4005U の場合 AVX2 より SSE2 の方が速かった
・Core i5 10210U の場合 AVX2 の方が SSE2 より速かった。

関数 CpuType() は実行するCPUが SSE3 に対応していると2を返し、
SSE2 に対応していると1を返し、SSE2 に対応していないと0を返す。
GNUC または VisualC++2008SP1 以降ならば AVX2 に対応していると
3を返す。

DUAL

HAVE_SSE2, SSE2, HAVE_SSE3, SSE3, HAVE_AVX2, AVX2 のどれかと
DUAL を定義してコンパイルした場合、SSE2, SSE3, AVX2 のプログラムと
i386 のプログラムの両方が組み込まれる。実行時に CPU が SSE2, SSE3, AVX2 に
対応していればそれを使い、対応していなければ i386 の命令で計算する。

EXTEND / SMALL / COMPACT

マクロ EXTEND を定義すると Tempering 後の結果を保持するメモリ領域を追加する。
2010 以降の VC++ で HAVE_SSE2, SSE2, HAVE_SSE3, SSE3, HAVE_AVX2, AVX2 の
いずれかの時は EXTEND が定義される。これを取り消すには SMALL または COMPACT を
定義する。
これら以外の場合で EXTEND, SMALL, COMPACT をいずれも定義しない時は既定で SMALL となる。
STABLE_SPEED, STABLE, ORIGINAL が定義されている時は EXTEND, SMALL, COMPACT を
定義しても無視される。

COMPACT を定義する時は必ず HAVE_SSE2, SSE2, HAVE_SSE3, SSE3, HAVE_AVX2, AVX2
のどれかを定義すること。
COMPACT を定義した場合は Tempering で SMALL と違い SSE2, AVX2 を使う。
Core i5 10210U では EXTEND の方が COMPACT よりも高速だった。
Core i5 10500T では COMPACT の方が EXTEND よりも高速だった。

HAVE_SSE2, SSE2, HAVE_SSE3, SSE3, HAVE_AVX2, AVX2 ではなく、かつ、
EXTEND を定義して64ビットコンパイルするときは Tempering を64ビットで行う。

version 2.08 以前に指定できた EXTEND32 , EXTEND64 は常に遅くなるので
version 2.09 で削除した。

SIMPLE / UNROLL

zmtrand.c にはループ展開をして高速化している部分が幾つかある。

ループ展開しないで 1 〜 6 の和を単純な for ループで求める例
int i,sum,x[6]={1,2,3,4,5,6};
for (sum=i=0;i<6;i++) sum+=x[i];

展開例1:ループを全て展開
int i,sum,x[6]={1,2,3,4,5,6};
sum=x[0]; sum+=x[1]; sum+=x[2]; sum+=x[3]; sum+=x[4]; sum+=x[5];

展開例2:三つずつ展開
int sum,x[6]={1,2,3,4,5,6},*p,*z=&x[6];
for (sum=0,p=x;p<z;p+=3) { sum+=p[0]; sum+=p[1]; sum+=p[2]; }

マクロ SIMPLE を定義するとループの展開をしてる部分の全てを
単純な for ループに置き換える。(C言語での Loop unroll の抑止)。
この場合プログラムが小さく遅くなる場合が多い。SIMPLE を定義すると、
SIMPLE16GEN,  SIMPLE16TMP,  SIMPLE32GEN,  SIMPLE32TMP,
SIMPLE128GEN, SIMPLE128TMP, SIMPLE256GEN, SIMPLE256TMP
の八つが定義される。
16は16ビット、32は32ビット、128は SSE2, SSE3、256は AVX2 の演算が対象である。

16ビットのコンパイラではプログラムを小さくするため
既定で SIMPLE16 が定義される。

〜GEN は数列の生成のループ、〜TMP は EXTEND を定義したときの
Tempering のループが対象である。
SIMPLE を定義せずにこれらを個別に定義することによりきめ細かく最適化できる。
SIMPLE〜 の定義を取り消したい場合には UNROLL〜 を定義する。

GNUC の場合は既定で SIMPLE32GEN が定義される。
理由は GNUC で -O4 で最適化した場合、単純な for ループにした方が GNUC の
オプティマイザが SSE2 を使用したより高速なコードを吐き出すからである。
また GNUC で -mavx2 -O4 を指定した場合 AVX2 を使用した高速なコードを吐き出す。

16 ビットコンパイラでは既定で SIMPLE16GEN と SIMPLE16TMP が定義される。
理由は 16 ビットコンパイラはプログラムサイズの制約が厳しいからである。
例えば TurboC 1.5 で UNROLL を定義して実行すると、
NextMt() は約 8%、NextUnif() は約 5 %高速になるが、
プログラムは992バイト大きくなる。
また、コンパイル時の消費メモリは15,420バイト増える。

SIMPLE32GEN または SIMPLE16GEN が定義されていない場合は、
マクロ MT_U が数列生成ループ展開を何回分まとめるかを決めている。
MEXP が 19937 の時 MT_U は 1,2,3,4,6,12 が可能。既定値 16bit:6。それ以外 12。
MEXP が  2281 の時 MT_U は 1,2,4,6,9,12 が可能。既定値 16bit:6。それ以外 12。
MEXP が  1279 の時 MT_U は 1,2,4,5,10 が可能。  既定値 16bit:5。それ以外 10。
16ビットコンパイラで小さいのはプログラムサイズを小さくするためである。
また、LSI_C の場合 12 や 10 であるとコンパイルすらできない。

DIRECT / MACRO

デフォルト系列の乱数 NextMt(), NextUnif(), NextMt64() は通常
 #define NextMt() NextMt_r(DefaultMt) 
 #define NextUnif() NextUnif_r(DefaultMt) 
 #define NextMt64() NextMt64_r(DefaultMt) 
とマクロ定義されている。
DIRECT を定義すると
uint32_t NextMt() { 〜 DefaultMt->index 〜 DefaultMt->u.x 〜 }
double NextUnif() { 〜 NextMt() 〜 NextMt() 〜 }
uint64_t NextMt64() { 〜 NextMt() 〜 NextMt() 〜 }
のように引数を使わず直接 DefaultMt を参照するようになる。

32/64ビットコンパイラの場合は最適化すると引数はレジスタになるので、
DefaultMt を直接参照してもしなくても速度に変化はない。

一方 16ビットコンパイラ等では最適化しても引数はレジスタではなく常に
スタックに積まれていくので、引数なしにすると高速なる。
従って16ビットのコンパイラの場合、既定で DefaultMt を直接参照する。
16ビットのコンパイラで NextMt() 等をマクロにする場合は MACRO を定義する。

TurboC 1.5 で MACRO を定義してコンパイル・実行すると、
NextMt() は約 14% 遅くなり、NextUnif() は約 19% 遅くなる。
プログラムは 288 バイト小さくなる。
また、コンパイラの消費メモリは 172 バイト減る。

BB_MACRO

これを定義すると NextBit(), NextBit_m(), NextBit_r(), NextByte(),
NextByte_m(), NextByte_r() を関数ではなくマクロにする。

mag01[y&1] 演算とその代替演算

MAG / NEG / SLR / MUL / CND

これらは SSE2 / SSE3 / AVX2  HAVE_SSE2 / HAVE_SSE3 / HAVE_AVX2 でコンパイルしない時に意味を持つする。

unsigned mag01[2]={0,A}; として

MAG: mag01[(unsigned)y&1]   配列参照   mt19937ar.c の方法
NEG: (-((int)y&1))&A        2の補数   two's complement NEGation
SLR: (((int)(y<<31))>>31)&A シフト演算 Shift Left and shift Right
MUL: ((unsigned)y&1)*A      掛け算     MULtiply
CND: ((unsigned)y&1)?A:0    条件演算   CoNDitional operator


MAG / NEG / SLR / MUL / CND の演算は全て同じ値を返す。

コンパイル時に MAG / NEG / SLR / MUL / CND のどれかを定義することにより
どの演算を使うか選ぶことができる。
ORIGINAL が定義されているときの既定演算は[配列参照MAG]で、
ORIGINAL が定義されていないときの既定演算は[2の補数NEG]である。
速さの順序はコンパイラ/ビット数/CPUにより変わる。

[配列参照MAG]は GNUC で SSE2 に最適化出来なくなる場合があるので使わない方がよい。

[条件演算CND]は GNUC で SSE2 に最適化出来た場合に [掛け算MUL] よりも高速になる。
一方、Borland C++ の場合[条件演算CND]は極端に遅くなる。

結論として、[配列参照MAG]と[条件演算CND]以外では性能の
変化は大差ないので既定演算の[2の補数NEG]のままにしておいて問題はない。

C/C++

C++では乱数領域を宣言と同時にコンストラクタが初期化するので、
乱数生成時に初期化が終わっているかどうかを確認しない。
一方Cでは乱数生成の呼び出し毎に初期化が終わっているかどうかを確認
する必要があるのでC++と比べて約 14% 遅くなる。

test.c

test.c をコンパイルするとき、
C:\... > bcc32 test.c
のようにすると test.c は zmtrand.c をインクルードする。
C:\... > bcc32 -DSPLIT test.c zmtrand.c
のように SPLIT を定義することにより、test.c は zmtrand.h を
インクルードして分割コンパイルすることもできる。

test.exe を作成した後、
C:\... > test.exe
のようにパラメータ無しで実行すると結果をフォルダ 'C' に書き出す。
MEXPが19937の場合ファイル名はc19937o.txt c19937x.txt c19937d.txt となる。
(c19937x.txtはSSE2 SSE3 AVX2のどれかとDUALが定義されているとき作成される)
ただし以下の3つの条件を全て満たす場合はc19937o-.txt c19937d-.txt となる。
@ORIGINAL が定義されている。
AFAST_UNIF UNIF_52 STABLE STABLE_SPEED のいずれも定義されていない。
BNEG SLR MUL CND のいずれも定義されていない。または MAG が定義されている。

C:\... > test.exe -
のようにハイフンで始まるパラメータを付けて実行するとベンチマークテストを
する。

C:\... > test.exe +
のようにプラスで始まるパラメータを付けて実行してもベンチマークテストを
する。+ にすると結果は '%[ホスト名].TXT' に追記する。
例えばホスト名が 'host' なら '%HOST.TXT' に追記する。

C:\... > test.exe +$
のようにプラスの後に '%' 以外の文字を指定するとベンチマークテストの
結果を 'C/[指定した文字][ホスト名].TXT に追記する。
例えばホスト名が 'host' で指定した文字が '$' なら 'C/$HOST.TXT' に追記する。

16ビットで MS-DOS の場合は MSDOS を定義する。
__TURBOC__ _MSC_VER LSI_C が定義されているなら MSDOS の定義は省略できる。

ウィンドウズで NO_PERIOD を定義してコンパイルすると timeBeginPeriod(1); と
timeEndPeriod(1); を呼び出さない。NO_PERIOD 定義した場合は時間計測の精度が
15ミリ秒になる。ただし、音楽・動画プレーヤーを起動している場合に、
時間計測の精度が1ミリ秒になる場合がある。

NO_PERIOD を定義しない場合 timeBeginPeriod(1) を呼び出す。
この場合、音楽・動画プレーヤーを起動しているかどうかに関わりなく、
時間計測の精度が1ミリ秒になる。

TEST_TIME を定義すると一回の計測時間の目安を秒数で指定できる。
既定値は 1.5。

ベンチマークの結果の見方を説明する。

@    A       B  C   D   E      F     G     H I   J        K
19937 VC++2019 C++ ONLY AVX2 SMALL   UNROLL MACRO  64 53   0.4176    0.9004

@MEXP の値
Aコンパイラ名
BC++ なら 'C++'、C なら 'C'
CONLY, DUAL, 8086, ORIG, STBL のいずれか
DMAG, NEG, SLR, MUL, CND, SSE2, SSE3, AVX2 のいずれか
EEXTEND, SMALL, COMPACT  のいずれか
FUNROLL, SIMPLE のいずれか。GEN ルーチンが展開されているかどうか。
GMACRO, DIRECT のいずれか。NextMt() NextUnif() NextMt64() がマクロかどうか。
Hポインタのビット数。16 32 64 のいずれか
INextUnif() の精度。 32 52 53 のいずれか
JNextMt()   を1億回呼び出した場合の推定実行時間(秒)
KNextUnif() を1億回呼び出した場合の推定実行時間(秒)

M.bat / Ms.bat

M.bat / Ms.bat はウィンドウズ版のみで使用可能。
TDM-GCC-64 がインストールされているならば
M.bat というバッチファイルをダブルクリックするだけで
test.exe を作成できる。

他のコンパイラを使ったりコンパイラオプションを指定したい場合は、
フォルダにある $Cmd というショートカットをダブルクリックして、
コマンドプロンプトを起動する。そして、

C:\...\zmtrand>m 10 /D SSE2

などとする。
この場合 Visual C++ 2010 でC++としてコンパイルしSSE2用の
64ビットプログラムが作成される。

C:\...\zmtrand>m 22c 32 /D AVX2

とすると Visual C++ 2022 でC++ではなくC言語としてAVX2用の
32ビットプログラムが作成される。

もし test2.c というソースファイルをコンパイルしたい場合は、

C:\...\zmtrand>m test2

とする。
M.bat を複製するなどして、それを編集すると便利である。M.bat は、

@echo off
echo off
set SELF=m
set ARG0=%0
set COMPILER=Gcc
set PG=test
set EXE=
set SUBSRCS=
set OPTIONS=
set HIT_OPTIONS=-DHIT_ENTER
set OPTIONSVC=/EHsc /O2
set OPTIONSGC=-O4
set OPTIONSBC=-O2
set OPTIONSTC=
set OPTIONSLC=
set LIBSVC=
set LIBSGC=
set LIBSBC=
set LIBSTC=
set LIBSLC=
set UTF8=0
set BIT=64
set WINDOWS=0
set OPENMP=0
set CPP=1
set HIT=1
set ANLZ=0
set RUN=0
set PARAM=
set TOOLS=.\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\..\..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\..\..\..\..\tools
if not exist %TOOLS%\AddVer.bat echo Pocom tools folder is not found.
if not exist %TOOLS%\AddVer.bat pause
if not exist %TOOLS%\AddVer.bat goto EndOfBat
set ARG=
:DoLoop
if '%1' == '' goto ArgDone
set ARG=%ARG% %1
shift
goto DoLoop
:ArgDone
call %TOOLS%\Mk^$Base %ARG%
:EndOfBat
set PG=

となっている。これを編集すればデフォルトのコンパイラ(COMPILER)や
メインソースファイル(PG)やオプション
(OPTIONS, HIT_OPTIONS, OPTIONSVC, OPTIONSGC, OPTIONSBC, OPTIONSTC, OPTIONSLC)や
追加のライブラリ(LIBSVC, LIBSGC, LIBSBC, LIBSTC, LIBSLC)の設定を変えて対応できる。
末尾が VC は Visual C++、GC は gcc,g++、BC は Borland C++、
TC は Turbo C、LC は Lsic86 に対応している。
追加のソースファイル(SUBSRCS)も指定できる。
メインソースファイル(PG)には拡張子 .c や .cpp は
省略するが、追加のソースファイル(SUBSRCS)や追加のライブラリ(LIBSVC 等)は
拡張子 .c や .cpp や .lib は必要である。

Ms.bat は、

@echo off
echo off
set SELF=ms
set ARG0=%0
set COMPILER=Gcc
set PG=test
set EXE=
set SUBSRCS=zmtrand.c
if '%1' == 'test0' set SUBSRCS=zmtrand0.c
if '%1' == 'test1' set SUBSRCS=zmtrand1.c
if '%1' == 'test2' set SUBSRCS=zmtrand2.c
set OPTIONS=-DSPLIT -DNO_INLINE
set HIT_OPTIONS=-DHIT_ENTER
set OPTIONSVC=/EHsc /O2
set OPTIONSGC=-O4
set OPTIONSBC=-O2
set OPTIONSTC=
set OPTIONSLC=
set LIBSVC=
set LIBSGC=
set LIBSBC=
set LIBSTC=
set LIBSLC=
set UTF8=0
set BIT=64
set WINDOWS=0
set OPENMP=0
set CPP=1
set HIT=1
set ANLZ=0
set RUN=0
set PARAM=
set TOOLS=.\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\..\..\..\tools
if not exist %TOOLS%\AddVer.bat set TOOLS=..\..\..\..\..\..\..\tools
if not exist %TOOLS%\AddVer.bat echo Pocom tools folder is not found.
if not exist %TOOLS%\AddVer.bat pause
if not exist %TOOLS%\AddVer.bat goto EndOfBat
set ARG=
:DoLoop
if '%1' == '' goto ArgDone
set ARG=%ARG% %1
shift
goto DoLoop
:ArgDone
call %TOOLS%\Mk^$Base %ARG%
:EndOfBat
set PG=
となっている。
M.bat との違いは、分割コンパイルして、インライン展開をしないことである。

M.bat , Ms.bat で指定できるコンパイラの一覧は以下の通り。
コンパイラの一覧
C++Cコンパイラ
正式名省略名正式名省略名
50 50c Visual C++ 5.0
60 60c Visual C++ 6.0
2002022002c02cVisual C++ 2002
2003032003c03cVisual C++ 2003
2005052005c05cVisual C++ 2005
2008082008c08cVisual C++ 2008
2010102010c10cVisual C++ 2010
2013132013c13cVisual C++ 2013
2017172017c17cVisual C++ 2017
2019192019c19cVisual C++ 2019
2022222022c22cVisual C++ 2022
Bcc B Bccc Bc Borland C++ 5.5.1
Gcc G Gccc Gc C:\TDM-GCC-64
Gucc Gu Guccc Guc C:\TDM-GCC-64(UTF-8ソース)
G1cc G1 G1ccc G1c C:\mingw64,C:\mingw32
Gu1ccGu1Gu1cccGu1cC:\mingw64,C:\mingw32(UTF-8ソース)
G2cc G2 G2ccc G2c C:\msys64\ucrt64
Gu2ccGu2Gu2cccGu2cC:\msys64\ucrt64(UTF-8ソース)
Lcc L Lsic86
Tcc T TurboC 1.5
Tc2 TurboC 2.01
詳しくはポコムのマニュアル
http://isaku-wada.my.coocan.jp/pocom/pocom.html#batch
を参照すること。