レッスンに戻る

min から max のランダムな整数

重要性: 2

取りうる値として minmax を含む、min から max のランダムな 整数 値を生成する関数 randomInteger(min, max) を作りなさい。

範囲 min..max からの任意の値は、同じ確率で現れなければいけません。

動作例:

alert( random(1, 5) ); // 1
alert( random(1, 5) ); // 3
alert( random(1, 5) ); // 5

シンプルだが間違った解法

最もシンプルで、しかし間違った解法は、min から max の値を生成してから丸める方法です:

function randomInteger(min, max) {
  let rand = min + Math.random() * (max - min);
  return Math.round(rand);
}

alert( randomInteger(1, 3) );

この関数は動きますが、間違っています。境界値 minmax を得る確率は他の値の半分になります。

上の例を複数回実行してみると、2 が他より多く現れることに簡単に気付くでしょう。

これは、Math.round()1..3 のランダムな数値を取った後に、次のように丸めるためです:

1   から 1.4999999999 までの値は 1 になります
1.5 から 2.4999999999 までの値は 2 になります
2.5 から 2.9999999999 までの値は 3 になります

これで、12 の半分しか取得できないことが明白に分かりますね。3についても同様です。

正しい解法

このタスクを正しく解く方法はたくさんあります。一例は、区間の端を調整することです。同じ区間となるように、値を 0.5 から 3.5 の間で生成します。そうすると境界値の発生確率が上がります:

function randomInteger(min, max) {
  // rand は (min-0.5) から (max+0.5) になりました
  let rand = min - 0.5 + Math.random() * (max - min + 1);
  return Math.round(rand);
}

alert( randomInteger(1, 3) );

もう一つの例は、min から max+1 のランダムな数値に対して Math.floor を使う方法です。

function randomInteger(min, max) {
  // ここでは rand は min から (max+1) です
  let rand = min + Math.random() * (max + 1 - min);
  return Math.floor(rand);
}

alert( randomInteger(1, 3) );

ここでは、区間は次のように割り当てられます:

1 から 1.9999999999 の値は 1 になります
2 から 2.9999999999 の値は 2 になります
3 から 3.9999999999 の値は 3 になります

全ての区間は同じ長さを持つため、最終的な分布が等しくなります。