見出し画像

乱数あれこれ

簡単なテストをする時にも使うのですが、ゲームを作る時に必要になるのが乱数です。最初に覚えたAPPLE][の6K BASICには便利なRND関数はありましたが、できればアセンブラで乱数を使いたくなりました。BASICで使われているサブルーチンを探しても良かったのですが、ちょうど学校の授業で合同式をやったところでもあったので、自分で作ってみました。

擬似乱数

当時使われていた乱数発生ライブラリの特徴として、最初は普通にまあまあのランダムな値が出てくるのですが、途中から周期を持つループした乱数が出るようになり、ループになるまでの回数とループの周期が初期値によって大きく変わるという癖がありました。

ゲームで使うのですから、乱数の発生にあまり手間をかけてはいられません。良い乱数を得るためにゲームのパフォーマンスが下がってしまっては本末転倒です。くれぐれも割り算を使わないように、加減算とシフトくらいで実装しなければなりません。

当時のアーケードゲームやテーブルゲームでも乱数発生には苦労していたのですが、都度計算するのではなくて、予め作られた乱数列を使うことも多かったようです。ひどい例だとわざわざ乱数列など作らないで、ROMに書かれていた値を順に読み出して乱数として使っているケースも知っています。この方法の良いところは再現性のあるところで、バグが起こっても再現しやすかったです。

自分で乱数を発生させたり、ありもののコードを選択するには乱数を評価しなければなりません。難しい本の説明もあったのですが、わかりやすくて評価しやすかったのは、以下の試験です。

  • 値の発生頻度

  • 次の値との差の発生頻度

サイコロの目を例にすると、最初の発生頻度は1から6までで値がそれぞれ1/6で発生すれば期待通りということになります。ここまでは当たり前ですが、次の発生頻度は、例えば最初の値が3の時に、次の値が1または2になる確率は1/3で3になる確率が1/6、そして4~6になる確率は1/2になるはずです。人間は同じ数字が続くと規則性を感じますが、ここは正しく1/6の確率で同じ値が出てこなければ乱数ではありません。

もちろん他にもいろいろな検定方法がありますが、こういった形で調べていました。

本当は疑似乱数なんか使わないで、何らかのランダムな物理現象を使えるのが望ましいです。真にランダムな放射性崩壊なんて手近にはありませんが、どこにも接続されていないI/Oの値がうまい具合に空中線電力を拾ってくれてランダムっぽいこともあります。ただこれは電源周波数の規則性がのっていることも多く、検定してみると使えないこともあります。もっと手軽なものとして、リアルタイムクロックの末尾近くの値を使うということもありますが、こちらも注意しないと規則性が出てしまうことがあります。Z-80の場合はリフレッシュカウンターがよく使われました。

ところで、乱数を発生させる関数には謎の指定が必要となります。殆どの場合は最初にできるだけ同じにならないような値で乱数系列を指定し、その後はランダムな数列を順に使えば良いので、お約束として同じ使い方をしているとは思います。どうして他の使い方があるのかといえば、乱数を利用したコードのテストをした時に、テストが通らなかった時に、同じ乱数系列を使わないと再現することができない場合があります。さっき出たエラーが、もう出せないとなると原因を調べることが出来ません。そこで再現できるようにしたいので系列を指定して乱数を発生させる仕組みがあるのです。また、敢えてゲームにパターンを持ち込むときにも使えます。

VBA Rnd 関数:乱数を生成する

【C言語入門】乱数(rand)の使い方

結局、自作した乱数ルーチンは、線形帰還シフトレジスタの一種のようなコードを書いたのですが、あまり適切なパラメタを選ばなかったようで、出来が悪くて思ったよりも品質が出なかったので、ありものの乱数表から256バイトの乱数表を作り(上限値が決まっている場合には、それに応じた表にしておく)、ここからルックアップするインデックスを、その今一つの品質の乱数で作るという方法でお茶を濁した覚えがあります。

線形帰還シフトレジスタ


おまけ

乱数について思い出していたら、こんなニュースが流れてきました。乱数に関する追求はまだまだ続いているようです。

【PHP8.2】PHPの乱数がすごい改善される



ヘッダ画像は以下のものを使わせていただきました。
https://www.irasutoya.com/2016/06/3_26.html


この記事が気に入ったらサポートをしてみませんか?