[C言語] 乱数の引き方(メルセンヌツイスタで)

環境:Windows10 + gcc 6.3.0

C言語で乱数を引く

疑似乱数を引くのであれば、C言語に標準装備されているrand関数を使えば良い。しかし、C言語標準のrand関数にはいくつかの問題がある(詳しくは「補足:C言語標準のrand関数の問題」という部分を参照のこと)。

そこで今回は、C標準のrand関数の代わりに、質の良い乱数を引けるメルセンヌツイスタを使う方法を説明する。

メルセンヌツイスタのアルゴリズムについては次のページなどに説明がある。
メルセンヌ・ツイスタ

今回使うSFMT(SIMD-oriented Fast Mersenne Twister)はメルセンヌツイスタ(MT)の改良版で、MTよりも処理速度が速くなっているようだ。

ライブラリのダウンロード

下のページの「Download SFMT which supports various period from 2607-1 to 2216091-1」から最新版をダウンロードする(記事執筆時点での最新バージョンは1.5.1).
SIMD-oriented Fast Mersenne Twister (SFMT)

zipファイル(またはtar.gzファイル)を解凍し、乱数を使いたいソースコードと同じディレクトリに置けば、SFMTによる乱数生成が利用できるようになる。

乱数の引き方(ライブラリの使い方)

乱数を10個引いて表示するプログラム(test_rand.c)を書いた。#include "SFMT-src-1.5.1/SFMT.h"は、フォルダ名に合わせる。

#include <stdio.h>
#include <time.h>
#include "SFMT-src-1.5.1/SFMT.h"

sfmt_t sfmt;

int main() {
    sfmt_init_gen_rand(&sfmt, (unsigned)time(NULL));
    for (int i = 0; i < 10; i++) {
        double rand = sfmt_genrand_real2(&sfmt);
        printf("%f\n", rand);
    }
    return 0;
}

やっていること(ライブラリの使い方)は次の通り。

  1. sfmt_init_gen_rand(&sfmt, (unsigned)time(NULL))で乱数のシードを設定
  2. sfmt_genrand_real2(&sfmt)で0~1の乱数を生成(SFMTライブラリには他にもいろいろな関数が用意されている)。→C言語でのメルセンヌ・ツイスタライブラリ(SFMT)の使い方 - あるぼう研究室

gccによるコンパイルの一例(詳しい話はこちらHow to compile SFMT

$ gcc -O3 -msse2 -DSFMT_MEXP=19937 SFMT-src-1.5.1/SFMT.c -o test_rand test_rand.c

ここで-msse2は最適化オプション。

結果の一例(実行する度に数字が変わる)。

0.436512
0.513134
0.761792
0.511109
0.978063
0.794533
0.292204
0.904435
0.354460
0.896812

補足:C言語標準のrand関数の問題

次のページでは、rand関数の問題として次の二つをあげている。

  1. 生成される乱数の上限が(環境によっては)小さいこと
  2. 同じシード値を与えても環境によって生成される乱数が異なること(再現性の問題)

www.albow.net

また次のページでは、rand関数の持つ問題を「C の標準関数 rand() には、生成する乱数列の特性についてなんの保証もない。rand() の実装によっては、生成される数値列の周期が比較的短くなってしまい、どのような値が生成されるか予測される恐れがある。」と説明している。

www.jpcert.or.jp

このように、rand関数にはいくつかの問題があるため、基本的には別の関数を使った方が良いだろう。

参考ページ

乱数生成アルゴリズムについて詳しい解説がある。
omitakahiro.github.io

SFMTライブラリの使い方の解説がある。www.albow.net


更新履歴
2018-11-17 作成
2020-07-05 全体的に修正。特にC言語標準のrand関数の問題について修正・加筆。