乱数をつくる

サイコロの目

サイコロを振って出る目は 1 から 6 まで、続いて同じ目が出ても回数を繰り返すとほぼ均等に出ますね。 連続して作為なく振って出した目の値は乱数といえます。


PICで乱数を出す

PIC でモールス符号の受信練習器を作ったとき ABCD・・・や イロハ・・・の順では後続の符号が分かってしまって学習になりませんから乱数を発生させてその数値に相当したモールス符号をテーブルから取り出して発信させるようにしました。
一生懸命(でもないか?)勉強して PICでは 「M系列」といわれる方法が一番簡単な乱数発生方法と知りました。


M 系列の乱数発生

PIC の命令セットの中にレジスタの内容を1ビットづつ左回ししたり右回しするものがあります。これを使って乱数がつくれます。
RRF 命令はレジスタ "Resister" の内容を Status レジスタのキャリービット"C"を通して1ビットづつ右に回転します。
Status レジスタのキャリービット"C"とレジスタ "Resister" の内容がすべて "1" か "0" であると何回右回ししてもレジスタの値は変りません。 "Resister" に "1" と "0" が入り組んで並んでいても右回しの9回目には最初と同じ値が出てきます。
そこでいくつかのビットの値を取り出して排他的論理和 XOR をとり、bit7 へ戻すとレジスタの内容はいろいろな数値になります。 

次に示す方法は bit4 と bit2 の XOR をとり、さらにその結果と bit0 とのXOR を取って bit7 へ入れて乱数をつくります。 実際には XOR の結果はキャリービットに入れ RRF 命令を実行して bit7 へ移します。 この操作で乱数をつくることができます。
XOR の位置は上の図に限ったものではなく任意の箇所に接続して使えます。 乱数の数値が少ない値の場合は XOR は bit2 と bit0 の間に 1個入れただけでもよいでしょう。

乱数発生プログラム例

16F タイプのPICはRRF命令(右回り)でもRLF命令(左回り)でも回転ループの中に Status レジスタのキャリービット "C" が介在しますから上のロジックは次のようなプログラムを実行することになります。

RANDM MOVF RANDMNB,W ;RANDMNB の値を W レジスタへ
ANDLW B'00010000' ;bit 4 以外をマスクする
MOVWF RTEMP ;その値を RTEMP レジスタへ
RRF RTEMP,F ;右回しして bit4 の値を bit3へ移す
RRF RTEMP,F ;右回しして bit3 の値を bit2へ移す
;
MOVF RANDMNB,W ;RANDMNB の値を W レジスタへ
ANDLW B'00000100' ;bit2 以外をマスクする
;
XORWF RTEMP,F ;W の bit2 と RTEMP の bit2 との
;XOR の結果を RTEMP へ入れる
RRF RTEMP,F ;右回しして bit2 の値を bit1へ移す
RRF RTEMP,F ;右回しして bit1 の値を bit0へ移す
;
MOVF RANDMNB,W ;RANDMNB の値を W レジスタへ
ANDLW B'00000001' ;bit0 以外をマスクする
XORWF RTEMP,F ;W の bit0 と RTEMP の bit0 との
;XOR の結果を RTEMP へ入れる
RRF RTEMP,F ;RTEMP を右回し bit0 の値を Cへ
RRF RANDMNB,F ;RANDMNB を右回し Cはbit7 へ
;この時の RANDMNB の値が ”乱数”
RETURN 呼出し元へ戻る


擬乱数

上のプログラムで発生させる乱数は 次、その次と出現する数値の予測ができますからサイコロを振って出てくる数値(乱数)とは異なり Pseudo Random Number (擬乱数)と言うそうです。

乱数発生を行うレジスタの初期値(種、ネタ)が決っているとプログラムを起動するたびに前回起動したときと同じ数値、順序の乱数が出てきます。 この乱数をつかってモールス符号を送信していると何回か起動を繰り返すうちに出てくるモールス符号を憶えてしまって面白くありません。


乱数発生初期値(種)

乱数発生のための種をプログラム起動ごとに変えれば、常に起動するたびに違った値の乱数を発生させることができます。
ではどうしたらよいでしょうか? パソコンなら時計を持っているのでその日、時の数値を乱数の種にできそうですが PIC はそんなうまいものがありません。

次の方法を試してみました。

1.モールス符号練習器やエレキー付属の機器の場合、スピード調節に A/D コンバータを使っています。 電源をいれて立ち上がりに A/D のデータを1回取りその値を「種」にする。

2.モールス符号がランダムに送信されているときの文字か語の乱数値を EEPROM に書き込んでおき、次回起動時最初に EEPROM の値を読み出して「種」にする。

上の1.の方法は短時間で再起動させると同じ並びの乱数が出てきます。 A/D 基準電圧を変える可変抵抗器(スピード調節 VR )を回したり、室温が変化した翌日とか電池電圧が若干変れば「種」として使えるかもしれません。

次の2.は毎度、まいど EEPROM に書いては消して、書き込むのが煩わしい感じですが、操作はPIC がやってくれるので手間がかかることはありません。 現在この方法を愛用しています。
EEPROM への書込み消去の繰り返しは PIC のデータシートによれば 百万回可能なようなので問題ないでしょう。 起動するたびに異なる「種」を得て前回とは異なる乱数がスタートします。
 
2007.6.22 一部修正
PIC目次へ     トップページへ