脱N00Bしたいエンジニア

N00Bなエンジニアの奮闘記録

擬似乱数生成器 rand() と random() のおはなし

はじめに

たとえ名前が変わらなくてもプログラミング言語だってアップデートされる。
安全なソフトウェアをつくろうとしたとき、例えば医療機器向けのシステムとか、安全がある程度保証されているものを使いましょうというのがある。

大学の講義で非推奨関数の例としてはじめてきいたのはC言語のgets関数である。
ほかにもいろいろあるので、調べてみると将来だれも殺さずに済むかも知れない。
参考:

乱数生成について

非推奨関数ではないとおもうが、あまり使わない方がいいよってよくきくのが乱数生成器のrand関数だ。
お手軽に乱数がつくれて、とても重宝する。
しかしながら、研究とかちゃんとした乱数がほしいときにはあまり使わない方がいいらしい。

そこで、POSIX的にはrandom関数を用いることを推奨しているらしい。

旧版の rand() の実装や、他のシステムの現在の実装では、下位のビットが上位のビットほど ランダムになっていない。移植性を高める場合でも、精度の高い乱数が必要な アプリケーションではこの関数は使用してはいけない (代わりに random() を使うこと)。

random関数の使い方は特にかわったものはなく、シード関数がsrand()からsrandom()へ、生成の関数がrand()からrandom()へかわっただけである。
具体的な実装は下記のような感じになる。

参考:

RANDOM使用の注意点

randomをつかわないといけない使命感からさっそく使おうとしたときに問題が発生する。
srandとrandomが暗黙的な宣言といわれてしまったのである。

-bash-4.2$ gcc rangen.c -o rangen -std=c11
rangen.c: 関数 ‘test_random’ 内: 
rangen.c:23:3: 警告: 関数 ‘srandom’ の暗黙的な宣言です [-Wimplicit-function-declaration] 
srandom(10); ^ 
rangen.c:26:5: 警告: 関数 ‘random’ の暗黙的な宣言です [-Wimplicit-function-declaration] 
r = random(); ^ 

-bash-4.2$ gcc rangen.c -o rangen -std=c99 
rangen.c: 関数 ‘test_random’ 内: 
rangen.c:23:3: 警告: 関数 ‘srandom’ の暗黙的な宣言です [-Wimplicit-function-declaration] 
srandom(10); ^ 
rangen.c:26:5: 警告: 関数 ‘random’ の暗黙的な宣言です [-Wimplicit-function-declaration] 
r = random(); ^

原因はコンパイルオプションで-std=c99-std=c11を指定していることである。
Man page によると、

  • rand()はSVr4, 4.3BSD, C89, C99, POSIX.1-2001 に準拠
  • random()は4.3BSD, POSIX.1-2001. に準拠 とある。

random()はC11とC99準拠になっていないのでダメっぽい。
オプションなしでコンパイルするとこの警告はでない。

使用しているコンパイラ
-bash-4.2$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

stdlib.h ヘッダーの中身

ヘッダーファイルがどうなっているのか気になるので、中身をみた。
まずはヘッダーの場所を探す。

$ find /usr/ -name stdlib.h
/usr/include/bits/stdlib.h
/usr/include/stdlib.h
/usr/include/c++/4.8.2/tr1/stdlib.h

ちなみにMacだと

$ find /Applications/Xcode.app -name stdlib.h 
/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/include/stdlib.h
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/stdlib.h
/Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/usr/include/stdlib.h
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdlib.h
/Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform/Developer/SDKs/WatchSimulator.sdk/usr/include/stdlib.h
/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk/usr/include/stdlib.h
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/stdlib.h
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h
[4]+  Done                    /Applications/Atom.app/Contents/MacOS/Atom /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdlib.h

参考:

ヘッダーの中身を読んでいるとそこには

These are the functions that actually do things. The random',srandom', initstate' andsetstate' functions are those from BSD Unices. The rand' andsrand' functions are required by the ANSI standard. We provide both interfaces to the same random number generator. / / Return a random long integer between 0 and RAND_MAX inclusive. */

randとrandomの実装は同じらしい!!!
古いシステムへの移植を考えないのなら普通にrandomじゃなくてrandでいいみたい。

ちなみにMacの場合、

void  srand(unsigned) __swift_unavailable("Use arc4random instead.");
int  rand(void) __swift_unavailable("Use arc4random instead.");
void     srandom(unsigned);
long     random(void) __swift_unavailable("Use arc4random instead.");

arc4randomを使ってください、とのこと。
そもそもrand()もrandom()も使うなと言うことだろう。
arc4randomについては、下記のサイトを読んでみたので比較表だけのせて、あとは紹介だけとする。

関数名 返り値 範囲
rand() int 0~2147483647
random() long 0~2147483647
arc4random() uint32_t 0~4294967295

参考:

最後に

使ってる関数がどう言うものなのか知っておくと何か問題が起こった時に原因がわかるのでいいかなと。 古いものを使う機会はないので、サクッと実装するときはrandで全然構わないと言う結論。 ちなみに最近はMersenne Twisterを使ってる。