Prolog 写経記 その 60 random/3

(ほぼ) 毎日淡々と Prolog を写経します.元ネタはこちら.

Prologユーティリティライブラリ

Prologユーティリティライブラリ

今日は random/3 を写経します.

解説

random(Min, Max, Num)MinMax の間からランダムに選んだ数を Num に具体化する.

ふむ.

モード

random(+, +, -).

ふむふむ.

定義

では,こいつの定義を写経しませう.

random(Min, Max, Num) :-
	integer(Min),
	integer(Max),
	Min =< Max,
	retract(seed(S)),
	Num is S mod (Max - Min + 1) + Min,
	NewSeed is (125 * S +1) mod 4096,
	asserta(seed(NewSeed)), !.
seed(7).

うーみゅ...
ここでポイントになるのは以下のとこ.

	retract(seed(S)),

retract/1 は引数で指定した述語を Prolog の実行環境 (Prolog ではこれをデータベースと呼ぶらしい) から取り除きます.Java でいうとクラスのアンロードみたいなものでしょうか.
その際に,乱数の種 S を具体化しています.
その種から引数で指定された範囲の乱数 Num を求めた後,新しい種 NewSeed を求めて

	asserta(seed(NewSeed)), !.

asserta/1 により新しい種を Prolog の実行環境に追加します.
つまり,実行環境 (データベース) をグローバル変数として利用しているわけです.
し・か・も
破壊的代入をしているのと同等.
なんか,とってもいやーんなやり方ですね...


なお,SWI-Prolog (ってか ISO 標準?) では random/3seed/1 を定義する前に

:- dynamic seed/1.

としておく必要があります.
こうしておかないと retract/1 等で seed/1 を扱えません.

注記

MinMax は整数である.16 ビットコンピュータでは NewSeed に関する整数オーバーフローを防ぐために,定義中の 4096 を 256 に置き換えること.
上記の定義ではランダム数生成の元となる数 (シード (seed) と呼ばれる) の初期値は定数としてある.しかし,たとえばシステム時間のような変数を用いれば,random が呼ばれるたびに同じランダム数の列が作り出されるのを防げる.

ふむふむふむふむ.
定義中の

seed(7).

がシードの初期値を定義した定数になるわけですね.

では使用例を写経しませう.

2 ?- nl, repeat, random(1, 100, X), write(X), tab(2), X == 77.

8  77  

X = 77 

Yes

うーみゅ.
repeat はバックトラックで制御が戻ってくる度にまた後続の節を実行する述語なので,random/3 の生成する乱数 X が 77 になるまで延々繰り返すという事ですね.
そして最初に生成された乱数が 8.その次に生成された乱数が 77.終了.
あっけなさすぎるのでもう一度.

3 ?- nl, repeat, random(1, 100, X), write(X), tab(2), X == 77.

6  91  4  73  54  43  92  93  30  3  48  13  10  47  48  5  70  55  60  45  82  
99  68  9  30  51  44  73  78  95  60  9  6  95  96  93  90  31  24  1  2  79  
16  9  22  15  52  89  18  7  4  17  70  39  48  69  54  79  64  21  38  7  16  
45  6  95  88  17  26  23  52  9  78  55  84  5  34  79  56  73  66  59  48  89  
58  79  32  29  78  7  80  65  2  23  72  77  

X = 77 

Yes

お,それっぽい感じ.
さらにもう一度.

4 ?- nl, repeat, random(1, 100, X), write(X), tab(2), X == 77.

6  99  80  37  62  11  84  21  50  35  52  1  46  43  64  57  18  11  92  61  
42  19  12  85  2  35  100  13  62  39  28  9  2  3  76  57  98  99  12  29  
26  11  20  97  54  7  68  41  66  63  32  29  30  59  92  45  26  63  16  57  
26  51  84  45  2  3  68  77  

X = 77 

Yes

いい感じ♪