疑似乱数生成器を自作する

Pineスクリプトにはビルトイン関数 rand() が用意されてないので、自分で疑似乱数生成器を実装してみる。実装するのは、C++11の minstd_rand0 とその改良版の minstd_rand

minstd_rand0 は、1988年にStephen K. ParkとKeith Millerが発表[1]した乱数生成器の実装を元にしたもの。論文の中で下記のPascalコードが示されている。

function Random : real;
const
  a = 16807;
  m = 2147483647;
begin
  seed := (a * seed) mod m;
  Random := seed / m
end;

変数 seed1 から 2147483646 までの整数の中から一つを割り当てて初期化する。

これをPineスクリプトで実装すると下記のようになる。seedtimenow の値で初期化している。


//@version=4
study("minstd_rand0")

var seed = timenow

a = 16807
m = 2147483647
seed := (a * seed) % m
//random = seed / float(m)

plot(seed)

変数 seed を参照すれば、C++11の minstd_rand0 のように整数の乱数を得ることができる。float の乱数が欲しい場合は、コメントアウトした random の値を参照すれば良い。

実装が正しいかどうかは、変数 seed1 で初期化し10000番目に生成された seed の値が 1043618065 であることを確認すれば良い。

下記のコードを使って、乱数生成器の実装の正しさを確認した。


//@version=4
study("minstd_rand0 test")

var seed = 1

if barstate.isfirst
    for i = 1 to 10000
        a = 16807
        m = 2147483647
        seed := (a * seed) % m
        //random = seed / float(m)

plot(seed, color=seed == 1043618065 ? color.green : color.red)

次に改良版の minstd_rand を実装する。これは1993年にStephen K. Park、Keith W. Miller、Paul K. Stockmeyerが発表[2]した改良版で、a = 16807 の代わりに a = 48271 を使用することを推奨したものである。

よって、Pineスクリプトの実装は単に a の値を 48271 に変更するだけで済む。


//@version=4
study("minstd_rand")

var seed = timenow

a = 4827
m = 2147483647
seed := (a * seed) % m
//random = seed / float(m)

plot(seed)
References
  1. S. K. Park and K. W. Miller. 1988. Random number generators: good ones are hard to find. Commun. ACM 31, 10 (Oct. 1988), 1192–1201. DOI:https://doi.org/10.1145/63039.63042
  2. Diane Crawford. 1993. Technical correspondence. Commun. ACM 36, 7 (July 1993), 105. DOI:https://doi.org/10.1145/159544.376068