JavaScript で再現性のある乱数を作る

JavaScript で乱数というと、Math.random() を使って生成できる。

しかし、今回はフロントエンド (JavaScript) の世界で、再現性のある乱数が生成したいという要件があり、やり方を調べた。


調べてみると、「シード値」という値を与えてあげることで乱数を生成できるプログラムがれば、それを実現できるらしい。

機械学習の分野でもよく使われる Python では、再現性のあるランダム値を必要とする機会が多いのか、ググると真っ先に情報が出てきた。

乱数生成には色んなやり方があるが、超絶平たくいうと、通常の乱数生成ではシード値にタイムスタンプを使うことで、「擬似ランダム」を実現している場合が多いそうだ。真のランダムな値というのはかなりパフォーマンスを使うため、擬似ランダムが一般に使われるらしい。

逆にいえば、シード値を自分で指定できるような乱数生成器を作れれば、「決まったシード値に対しては決まった値を返す」ワケである。


そして調べていくと、ドンズバな記事があった。

XorShift というアルゴリズムを使うと、シード値を指定した疑似乱数が容易に生成できるそうだ。

以下にコードを整形のうえ転記させていただいた。

class Random {
  constructor(seed = 88675123) {
    this.x = 123456789;
    this.y = 362436069;
    this.z = 521288629;
    this.w = seed;
  }
  
  // XorShift
  next() {
    const t = this.x ^ (this.x << 11);
    this.x = this.y;
    this.y = this.z;
    this.z = this.w;
    return this.w = (this.w ^ (this.w >>> 19)) ^ (t ^ (t >>> 8));
  }
  
  // min 以上 max 以下の乱数を生成する
  nextInt(min, max) {
    const r = Math.abs(this.next());
    return min + (r % (max + 1 - min));
  }
}

コンストラクタ内の xyz の値は、ゼロより大きい任意の値で良いらしい。next() メソッド内の処理は Wikipedia にある C 言語での実装例と同じで、XorShift アルゴリズムの実装なようだ。

使う時はこんな感じで使う。

const seed = 1;  // 任意のシード値を決める
const random = new Random(seed);

// 乱数を10個生成して表示する
for(let i = 0; i < 10; i++) {
  const value = random.next();
  console.log(value);
}

// 2 から 10 の間で乱数を10個生成して表示する
for(let i = 0; i < 10; i++) {
  const value = random.nextInt(2, 10);
  console.log(value);
}

このコードは何回実行しても、seed1 なので、10個とも同じ乱数が表示される。

かなり短いコードなのに、コレで実現できるってスゴいな。スバラシキ。