[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; }
やっていること(ライブラリの使い方)は次の通り。
sfmt_init_gen_rand(&sfmt, (unsigned)time(NULL))
で乱数のシードを設定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関数の問題として次の二つをあげている。
- 生成される乱数の上限が(環境によっては)小さいこと
- 同じシード値を与えても環境によって生成される乱数が異なること(再現性の問題)
また次のページでは、rand関数の持つ問題を「C の標準関数 rand() には、生成する乱数列の特性についてなんの保証もない。rand() の実装によっては、生成される数値列の周期が比較的短くなってしまい、どのような値が生成されるか予測される恐れがある。」と説明している。
このように、rand関数にはいくつかの問題があるため、基本的には別の関数を使った方が良いだろう。
参考ページ
乱数生成アルゴリズムについて詳しい解説がある。
omitakahiro.github.io
SFMTライブラリの使い方の解説がある。www.albow.net
更新履歴
2018-11-17 作成
2020-07-05 全体的に修正。特にC言語標準のrand関数の問題について修正・加筆。