LAB
研究室
C言語プログラムTIPS
乱数
ゲーム制作において、敵の行動の選択、アイテムの出現確率など、乱数を使用する場面は多いと思います。
現実世界でのサイの目の出現確率などとは違い、コンピューターで扱う乱数は、疑似乱数です。
つまり、乱数のようにみえるけれど、実際は論理式があり次に発生する数が求められます。
この疑似乱数であるということに、注意が必要な場合があります!!
C言語標準ライブラリにはrand()という関数があります。
この関数を用いて乱数を発生させている方も多いのではないでしょうか?
この関数の内部は線形合同法というアルゴリズムが用いられています。
このアルゴリズムですが、下位ビットの精度が悪いことで有名です。
最下位ビットの周期は2で、0と1が交互に現れてしまいます。
その後上位ビットになるつれ、周期が4,8,16、・・・と上がっていきます。
if (rand() & 1) { if (rand() & 1) { printf("A"); } else { printf("B"); } } else { if (rand() & 1) { printf("C"); } else { printf("D"); } }
↑このプログラムではBとCしか出力されず、AとDは一切出力されません。
(※処理系によりrand()の実装が異なるため、結果が異なる場合があります)
さらに、線形合同法の問題点として、ある乱数が得られたら次に得られる乱数が限られることが挙げられます!
例えば、2次元のベクトルの各要素を rand() で求めるような場合、得られる組み合わせが限られてしまうことになります。
(※ 処理系によっては、上位16ビットを右シフトし、下位の15ビットを返すものもあり、上記の限りではありませんが、乱数の返す値が0?32767となり、精度が足りなくなる問題があります。単に精度の問題なのであれば rand()*rand() とするなどで対処可能な場合もあります)
では精度が高く、周期の長い乱数を得るにはどうすれば良いのでしょうか!?
ここはメルセンヌツイスターの出番ですよね。
623次均等分布かつ、10進数で6000桁以上の長周期性と、自然乱数近似を得たいのであれば、真っ先に挙げられるソリューションではないかと思います。
(内部のワーキングメモリが32bit版で2496バイト必要になるので、使用の際は注意してください)
さて、rand()の使用上の注意や、メルセンヌツイスターの簡単な紹介をしてきましたが、ゲームで乱数を使う場合は、その用途に応じて適切なアルゴリズムを選択することが最も重要だと思います。
処理速度やメモリが切迫していて、精度をそこまで求めない場合は、rand() も十分候補に挙がるでしょうし、単純なタイマーでも代替可能かもしれません。
また、乱数の偏りが一概に悪いとは言いきれないときもあるのではと思います。
それがそのゲームの良い味になっていたりするんですよね。
ゲームで乱数を使用する時は、意識してみてください。
CATEGORY
- 製品事例 (7)
- デモプログラム (18)
- プログラムTIPS (41)
- C言語 (5)
- C++ (6)
- C++cording (4)
- Ruby (8)
- VisualStudioの使い方 (3)
- 最適化Tips (5)
- ゲーム開発テクニック (2)
- その他 (7)