擬似乱数生成器 rand() と random() のおはなし
はじめに
たとえ名前が変わらなくてもプログラミング言語だってアップデートされる。
安全なソフトウェアをつくろうとしたとき、例えば医療機器向けのシステムとか、安全がある程度保証されているものを使いましょうというのがある。
大学の講義で非推奨関数の例としてはじめてきいたのはC言語のgets関数である。
ほかにもいろいろあるので、調べてみると将来だれも殺さずに済むかも知れない。
参考:
乱数生成について
非推奨関数ではないとおもうが、あまり使わない方がいいよってよくきくのが乱数生成器のrand関数だ。
お手軽に乱数がつくれて、とても重宝する。
しかしながら、研究とかちゃんとした乱数がほしいときにはあまり使わない方がいいらしい。
そこで、POSIX的にはrandom関数を用いることを推奨しているらしい。
旧版の rand() の実装や、他のシステムの現在の実装では、下位のビットが上位のビットほど ランダムになっていない。移植性を高める場合でも、精度の高い乱数が必要な アプリケーションではこの関数は使用してはいけない (代わりに random() を使うこと)。
random関数の使い方は特にかわったものはなく、シード関数がsrand()からsrandom()へ、生成の関数がrand()からrandom()へかわっただけである。
具体的な実装は下記のような感じになる。
参考:
- MSC30-C. 疑似乱数の生成に rand() 関数を使用しない
- Linux Programmer's Manual (3) RAND
- Linux Programmer's Manual (3) 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 によると、
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' and
setstate' functions are those from BSD Unices. Therand' and
srand' 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を使ってる。