乱数(MSXの)
2025.08.17
MSX BASICのソースを見れば乱数発生の仕組みがわかると思うけど、いや、昔調べたような気がする。覚えてない。

学生時代に同級生がテトリスを自作して、その時に使った乱数ルーチンはMSXのROMではなく、独自に組み込んだもの。Z80マシン語秘伝の書に載っていた乱数ルーチンをアレンジしたと思う。

当時でも議論したけど、じつは乱数ってのは意外と難しく、完全な乱数って作れないのです。

BASICに内蔵されている乱数は、算術乱数というやつだったと思う。計算で次の値を求めている。どうしても偏りがある。
ある数に別の数をかけたり足したりして、その下位桁を取るとか、そんなやり方です。だからパターンが決まってしまう。不確定な要素が含まれてないわけ。

MSX-BASICにはTIMEという変数(カウンタ)があって、1/60秒ごとにインクリメントされているから、これを利用する手がある。何か押した瞬間の値を取るとか。プログラムをRUNした時に値を取り込んで乱数の種にする。
別に、TIMEに依存しなくても入力を待っている間にカウンタを適当に回しておいて、HIT ANY KEYで押した時の値でも良い。

パチスロ機の場合は、ハードウェアカウンタを回しておき、レバーを叩いた瞬間の値をとるというやり方でした。
これは昔、体感機で同期できてしまっていたので、その後に対策がとられて、ずっと高速で回るようになっていると思われます。

完全な乱数が必要なら物理乱数になります。電気的なノイズを利用したものがよく使われると思うけど、それだって外乱を受けるようではダメです。

MSX用乱数発生カートリッジとか? 需要ないだろうな。

あと、円周率を小数点以下何万桁までテーブルに記憶しておいて、そこから値を拾ってきて使うと乱数代わりになると本で読んだ事があるけど、良いのかどうか知らない。
この場合でも、順番に読むんじゃなくて、さっきのキーを押したタイミングでどこを読むかというやり方を組み合わせたものだと良さそう。

乱数の再現性を求める場合は、あらかじめデータとして蓄えておかないといけないな。ゲーム等では再現性は求めない場合がほとんどだろうから、いかにばらつかせるか、偏らせないかということになる。
サイコロ、オモテとウラ
2025.08.17
プログラムが残っていました。中学生の頃の古いノートからです。

改良前のプログラムで、バカ正直にIF文で処理しています。




バカだなあ・・・と笑ってやってください。
一方で、動けばいいんだ、という考えの方もいらっしゃると思います。

オモテとウラ、というのはコインの裏表の確率をシミュレーションしています。

乱数RNDはゼロから指定の数値未満まででした。確か。
それにわざわざ+1して1~6にしたのでしょうけど、最初から1~6を0~5に置き換えて処理したって構わないわけです。
表記としてはわかりにくくなるかもしれませんが、計算をひとつ削って処理時間を少しでも節約できます。

冒頭のRND(-TIME)は乱数のタネでしょう。(初期値)TIME変数を元にしています。

それから、
いま何回目かを常時表示しているとその表示に時間をとられます。そこで、たとえば100回とか1,000回おきに表示したかった。
これも馬鹿正直にIF文で判定させています。そうじゃなくて、1,000で割り切れて余りが0かどうか見ればよかったのではないか。あるいは、別にカウンタを回してIFで判定するほうが早ければそっちを採用しても良かったのではないか。

当時そのことは気づいていて、実際にそのような判定を入れた記憶があります。

処理速度を上げるには、整数型変数を使うようにプログラム先頭にDEFINT A-Zとすれば改善が見込めたものと思います。
しかし32767までしか扱えないのと、相対度数の計算で小数点が出てきます。
とりあえず3万回までのシミュレーションに限定すればよいでしょう。
あとは、相対度数の計算に関係する変数だけ浮動小数点のままにすればよかったでしょう。
確率のシミュレーション
2025.08.17
中学生の頃に一時ハマったのは、確率のシミュレーション

サイコロを転がして、ある目が出る確率というもので、何の仕込みもない正常なサイコロならどの目でも同じはず。

相対度数って言ったっけ。0.1666・・・になる。(1/6)
サイコロを振る回数が増えるほど、1/6に近づいていく、ということをシミュレーションしました。

MSXの乱数で1~6を発生させ、1から6の目それぞれ何回出たか集計させる単純なプログラムです。

最初は、いまでも自分で賢くないと思うけど、
IF X=1 THEN K1=K1+1
IF X=2 THEN K2=K2+1
IF X=3 THEN K3=K3+1
IF X=4 THEN K4=K4+1
IF X=5 THEN K5=K5+1
IF X=6 THEN K6=K6+1
・・・と、バカ正直にIFを並べていました。

もしIFで処理するとしても、最後にGOTOで以後のIFをとばしてやれば少しは速くなったでしょう。K1=K1+1:GOTO 200 とか。

あとで気づいて、IF文を並べるんじゃなくて
K(X)=K(X)+1
とすれば1行で済むじゃないかと。

最初は、こんなものでした。

あとから、中間結果を表示(印刷)するようにしました。
たとえば1000回ごとの結果を見たり記録に残したいわけです。

メインループの処理にできるだけ影響しないように、単純に判定する必要がありました。
MODを使ったのか\を使ったのかもう忘れました。
カウントの変数を別に回して1000回で0に戻すついでに印刷じゃなくて、
上記のXの値を1000で割って、割り切れるかどうかで判断したと思います。どっちが速いかわからないけど、まあ、放置しておけばそのうち終わる。

いま動いているのか、いまは何回サイコロをふったか、という表示もする必要がありました。そうでないと進捗がわからない。

- CafeLog -