短時間に繰り返す時の乱数の種
標準関数timeの戻り値は1秒単位なので、繰り返して実行する間隔が短くなると、
同じ値が乱数の種として与えられるようになり、同じ乱数列が出現することとなる。
この問題はtimeの戻り値の分解能が原因なので、
簡単な解決策としてはもっと分解能の高い値を得ればよいことになる。
get_rnd.c
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> int main(void) { struct timeval tv; gettimeofday(&tv, NULL); srand((unsigned int)tv.tv_sec * ((unsigned int)tv.tv_usec + 1)); printf("%d\n", rand()); return 0; }
gettimeofday関数を使ってみた。
この関数は実際に得られる精度は別にして名目上マイクロ秒単位の現在時刻を取得できる。
time関数と同じく秒単位のおおまかな時刻がtv_secメンバで得られ、
それより細かい時刻の情報がマイクロ秒単位でtv_usecメンバから得られる。
二つの情報をsrandに渡すunsigned int型の情報一つにまとめるのに、
二つの積を計算してこれをsrandの引数としている。
tv_usecの方に1を加えているのは、得られたtv_usecが0の時にsrandに渡す値が0となるのを防ぐためである。
もし、rand関数の実装が線形合同法に基づくもので、かつ、増分が0であるようなものの場合、
引数に0が与えられてそれがそのまま次の乱数生成器の状態への種とされてしまうとrandは0を返し続けることになる。
そのような乱数生成器が0を出力できるのは状態中の全ビットが出力に使われるとは限らないからで、
状態中の出力に関わる部分ビットパターンが0と等価になることは問題ないからである。
まあ、そんな心配は無駄なのかもしれないが、1を加える不利益もないと思うので念のため。
C:\foo> FOR /L %i IN (1,1,5) DO @get_rnd 21591 18043 14495 10947 7399
1秒以内での繰り返しでも問題ない。
残念ながらBCC32(5.5.1)ではgettimeofday関数は定義されていないようである。