JavaScriptでのrandomに関するまとめ

TL;DR

JavaScriptのランダムは非常に使い勝手が悪いので選択肢とか、よく使う関数などのまとめです。

Randomの基礎

Math.random

とりあえず一番簡易的なのはMath.randomを使う手法です。この関数は[0, 1]の範囲の乱数を生成します。ただ、これはseed値を指定できないので嬉しくないです。

const rand = Math.random();
console.log(rand);
// 0.5294271038323526

ちょっと便利にする関数

// [0, max]
function randInt(max) {
  return Math.floor(Math.random() * (max + 1));
}

// [min, max]
function randFromRange(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

XorShift

seed値が使え、簡単で比較的良い乱数生成アルゴリズムとしてXorShiftがあります。

class XorShift {
  constructor(seed = Date.now()) {
    this.x = 123456789;
    this.y = 984328975;
    this.z = 839047104;
    this.seed = seed;
  }

  gen() {
    let t;

    t = this.x ^ (this.x << 11);
    this.x = this.y;
    this.y = this.z;
    this.z = this.seed;
    return (this.seed = this.seed ^ (this.seed >>> 19) ^ (t ^ (t >>> 8)));
  }

  genFromRange(min, max) {
    const r = Math.abs(this.gen());
    return min + (r % (max + 1 - min));
  }
}
const random = new XorShift();

console.log(random.gen());
// -638953871
console.log(random.genFromRange(0, 10));
// 7

メルセンヌツイスタ

外部ライブラリのmt.jsを使えばメルセンヌツイスタも使えます。このような感じです。setSeedメソッドでseedの指定もできます。

var mt = new MersenneTwister();
var integer1 = mt.nextInt(0, 5); // 0 以上 5 未満の整数
var decimal1 = mt.next(); // 0 以上 1 未満の実数

配列のシャッフル

上のアルゴリズムを使えば、ただの乱数生成なら問題ないんですが、重複なく乱数生成したい、みたいなことがあります。その場合は単純に配列をシャッフルすればいいです。シャッフルにはFisher-Yates shuffleやDurstenfeldの手法などが使われるようです。Durstenfeldの手法のほうが高速らしいです。

randomはmaxだけ指定して持ってくる感じのものなら何でも使えるようにしました。簡易的には

const randomizer = function(max) {
  return Math.floor(Math.random() * (max + 1));
};

でいいです。

Fisher-Yates shuffle

function fisherYatesShuffle(array, randomizer) {
  let newArray = [];
  while (array.length > 0) {
    const n = array.length;
    const k = randomizer(n - 1);

    newArray.push(array[k]);
    array.splice(k, 1);
  }

  return newArray;
}

const randomizer = function(max) {
  const random = new XorShift();
  return random.genFromRange(0, max);
};

let array = [0, 1, 2, 3, 4];
array = fisherYatesShuffle(array, randomizer);

console.log(array);
// [ 3, 1, 2, 4, 0 ]

ダステンフィルドの手法

ほぼ同じですが、

function durstenfeldShuffle(array, randomizer) {
  for (let i = array.length; i > 1; i--) {
    let j = randomizer(max = i - 1);
    [array[i], array[j]] = [array[j], array[i]];
  }

  return array;
}

const randomizer = function(max) {
  const random = new XorShift();
  return random.genFromRange(0, max);
};

let array = [0, 1, 2, 3, 4];
array = durstenfeldShuffle(array, randomizer);

console.log(array);

// [ 0, 3, 4, 2, 1 ]

参考

この記事に関するIssueをGithubで作成する

Read Next