例えば、x = 4
とy = 8
とすると、4, 5, 6, 7, 8
のいずれかが出力されるような、2つの指定された変数間のランダムな整数を、JavaScriptで生成するにはどうしたらよいでしょうか?
Mozilla Developer Network]1のページにいくつかの例があります。
/**
* Returns a random number between min (inclusive) and max (exclusive)
*/
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
/**
* Returns a random integer between min (inclusive) and max (inclusive).
* The value is no lower than min (or the next integer greater than min
* if min isn't an integer) and no greater than max (or the next integer
* lower than max if max isn't an integer).
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
となっています。
ここでは、そのロジックをご紹介します。それは単純な3の法則です。
Math.random()は0(含む)から1(含む)までの
数字`を返します。つまり、次のような区間があります。
[0 .................................... 1)
次に、min
(含む)とmax
(含まない)の間の数値が欲しいとします。
[0 .................................... 1)
[min .................................. max)
Math.randomを使って,[min, max]区間に対応する数値を得ることができます.しかし,最初に,2番目の区間から
min` を引くことによって,問題を少しだけ因数分解しなければなりません.
[0 .................................... 1)
[min - min ............................ max - min)
これは次のようになります。
[0 .................................... 1)
[0 .................................... max - min)
次に,Math.random
を適用して,対応するものを計算してみましょう。乱数を選んでみましょう。
Math.random()
|
[0 .................................... 1)
[0 .................................... max - min)
|
x (what we need)
そこで、x
を求めるためには、次のようにします。
x = Math.random() * (max - min);
min,max)区間の数値を得るために、min
を戻すのを忘れないでください。
x = Math.random() * (max - min) + min;
これがMDNの最初の関数です。2番目の関数は、min
とmax
の間の整数を返します(両方を含む)。
整数を得るためには、round
、ceil
、floor
を使うことができます。
Math.round(Math.random() * (max - min)) + minを使うこともできます。+ min
を使うこともできますが、これは不均等な分布となります。minと
max`の両方とも、ロールするチャンスはおよそ半分しかありません。
min...min+0.5...min+1...min+1.5 ... max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘ ← Math.round()
min min+1 max
区間からmax
を除外すると、min
よりも出る確率がさらに低くなります。
Math.floor(Math.random() * (max - min +1)) とすると、完全に均等な分布となります。+ min` では完全に均等な分布となります。
min.... min+1... min+2 ... max-1... max.... max+1 (is excluded from interval)
| | | | | |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘ ← Math.floor()
min min+1 max-1 max
この式では ceil()
と -1
を使うことはできません。なぜなら max
が出る確率が少し低くなったからです。
function getRandomizer(bottom, top) {
return function() {
return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;
}
}
を使用しています。
var rollDie = getRandomizer( 1, 6 );
var results = ""
for ( var i = 0; i<1000; i++ ) {
results += rollDie() + " "; //make a string filled with 1000 random numbers in the range 1-6.
}
の内訳です。
私たちは、関数型プログラミングからの借用で、関数が呼び出されたときに、bottom
とtop
の間のランダムな整数を返す関数を返します。inclusive'と言ったのは、返すことのできる数字の範囲にbottomとtopの両方を含めたいからです。これにより、getRandomizer( 1, 6 )
は、1、2、3、4、5、6のいずれかを返すことになります。
(下が小さい数字、上が大きい数字)
Math.random() * ( 1 + top - bottom )
Math.random()
は 0 と 1 の間のランダムな倍数を返します。これに 1 と top
と bottom
の差を掛けると、0
と 1+b-a
の間の倍数が得られます。
Math.floor( Math.random() * ( 1 + top - bottom ) )
Math.floorは数値を最も近い整数に丸めます。これで
0と
top-bottomの間にあるすべての整数を得ることができました。1 は紛らわしいですが、常に切り捨てているので、これがないと実際には一番上の数字に到達しないので、必要なのです。生成するランダムな小数は、切り捨てて
0から
(1+top-bottom)の範囲の整数を得ることができるように、
0から
top-bottom` の範囲でなければなりません。
Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom
先ほどのコードでは 0
と top-bottom
の範囲の整数が得られたので、あとはその結果に bottom
を加えて bottom
と top
の範囲の整数を得るだけです。
注意: もし非整数の値や大きい方を先に渡すと望ましくない動作をしますが、誰かが要求しない限り、元の質問の意図とはかなり離れているので、引数チェックのコードを掘り下げるつもりはありません。
function getRandomInt(lower, upper)
{
//to create an even sample distribution
return Math.floor(lower + (Math.random() * (upper - lower + 1)));
//to produce an uneven sample distribution
//return Math.round(lower + (Math.random() * (upper - lower)));
//to exclude the max value from the possible values
//return Math.floor(lower + (Math.random() * (upper - lower)));
}
この関数やそのバリエーションをテストするには、以下のHTML/JavaScriptをファイルに保存し、ブラウザで開いてください。 このコードは、100万回の関数呼び出しの分布を示すグラフを作成します。 このコードはエッジケースも記録するので、関数が最大値よりも大きい値を生成したり、最小値よりも小さい値を生成したりした場合には、それについて知ることができます。
<html>
<head>
<script type="text/javascript">
function getRandomInt(lower, upper)
{
//to create an even sample distribution
return Math.floor(lower + (Math.random() * (upper - lower + 1)));
//to produce an uneven sample distribution
//return Math.round(lower + (Math.random() * (upper - lower)));
//to exclude the max value from the possible values
//return Math.floor(lower + (Math.random() * (upper - lower)));
}
var min = -5;
var max = 5;
var array = new Array();
for(var i = 0; i <= (max - min) + 2; i++) {
array.push(0);
}
for(var i = 0; i < 1000000; i++) {
var random = getRandomInt(min, max);
array[random - min + 1]++;
}
var maxSample = 0;
for(var i = 0; i < max - min; i++) {
maxSample = Math.max(maxSample, array[i]);
}
//create a bar graph to show the sample distribution
var maxHeight = 500;
for(var i = 0; i <= (max - min) + 2; i++) {
var sampleHeight = (array[i]/maxSample) * maxHeight;
document.write('<span style="display:inline-block;color:'+(sampleHeight == 0 ? 'black' : 'white')+';background-color:black;height:'+sampleHeight+'px"> [' + (i + min - 1) + ']: '+array[i]+'</span> ');
}
document.write('<hr/>');
</script>
</head>
<body>
</body>
</html>