¿Cómo puedo generar números enteros aleatorios entre dos variables especificadas en JavaScript, por ejemplo, x = 4
y y = 8
daría como resultado cualquiera de 4, 5, 6, 7, 8
?
Hay algunos ejemplos en la página Mozilla Developer Network:
/**
* 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;
}
Aquí'está la lógica detrás de esto. Es una simple regla de tres:
Math.random()
devuelve un Número
entre 0 (inclusivo) y 1 (exclusivo). Así que tenemos un intervalo como este:
[0 .................................... 1)
Ahora, queremos un número entre min
(inclusivo) y max
(exclusivo):
[0 .................................... 1)
[min .................................. max)
Podemos utilizar el Math.random
para obtener el correspondiente en el intervalo [min, max). Pero, primero debemos factorizar un poco el problema restando min
del segundo intervalo:
[0 .................................... 1)
[min - min ............................ max - min)
Esto da:
[0 .................................... 1)
[0 .................................... max - min)
Ahora podemos aplicar Math.random
y calcular el correspondiente. Elijamos un número aleatorio:
Math.random()
|
[0 .................................... 1)
[0 .................................... max - min)
|
x (what we need)
Así, para encontrar x
, haríamos
x = Math.random() * (max - min);
No te olvides de añadir min
de nuevo, para que obtengamos un número en el intervalo [min, max):
x = Math.random() * (max - min) + min;
Esa fue la primera función de MDN. La segunda, devuelve un número entero entre min
y max
, ambos inclusive.
Ahora, para obtener enteros, podrías usar round
, ceil
o floor
.
Puedes usar Math.round(Math.random() * (max - min)) + min
, pero esto da una distribución no uniforme. Tanto mín
como máx
sólo tienen aproximadamente la mitad de posibilidades de salir:
min...min+0.5...min+1...min+1.5 ... max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘ ← Math.round()
min min+1 max
Si se excluye a "max" del intervalo, tiene aún menos posibilidades de salir que "min".
Con Math.floor(Math.random() * (max - min +1)) + min
tienes una distribución perfectamente uniforme.
min.... min+1... min+2 ... max-1... max.... max+1 (is excluded from interval)
| | | | | |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘ ← Math.floor()
min min+1 max-1 max
No puedes usar ceil()
y -1
en esa ecuación porque max
ahora tiene un poco menos de posibilidades de salir, pero puedes sacar el resultado (no deseado) de min-1
también.
function getRandomizer(bottom, top) {
return function() {
return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;
}
}
uso:
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.
}
desglose:
Estamos devolviendo una función (tomada de la programación funcional) que al ser llamada, devolverá un entero aleatorio entre los valores inferior
y superior
, ambos inclusive. Decimos 'inclusivo' porque queremos incluir tanto abajo como arriba en el rango de números que pueden ser devueltos. De esta manera, getRandomizer( 1, 6 )
devolverá 1, 2, 3, 4, 5 o 6.
(abajo es el número menor, arriba es el mayor)
Math.random() * ( 1 + top - bottom )
Math.random()
devuelve un doble aleatorio entre 0 y 1, y si lo multiplicamos por uno más la diferencia entre top
y bottom
, obtendremos un doble entre 0
y 1+b-a
.
Math.floor( Math.random() * ( 1 + top - bottom ) )
Math.floor
redondea el número hacia abajo al entero más cercano. Así que ahora tenemos todos los enteros entre 0
y top-bottom
. El 1 parece confuso, pero tiene que estar ahí porque siempre estamos redondeando hacia abajo, así que el número superior nunca se alcanzará sin él. El decimal aleatorio que generamos tiene que estar en el rango de 0
a (1+superior-inferior)
para que podamos redondear hacia abajo y obtener un int en el rango de 0
a superior-inferior
.
Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom
El código del ejemplo anterior nos dio un entero en el rango 0
y top-bottom
, así que todo lo que tenemos que hacer ahora es añadir bottom
a ese resultado para obtener un entero en el rango bottom
y top
inclusive. :D
NOTA: Si pasas un valor no entero o el número mayor primero obtendrás un comportamiento no deseado, pero a menos que alguien lo solicite no voy a profundizar en el código de comprobación de argumentos ya que está bastante lejos de la intención de la pregunta original.
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)));
}
Para probar esta función, y las variaciones de esta función, guarde el siguiente HTML/JavaScript en un archivo y ábralo con un navegador. El código producirá un gráfico que muestra la distribución de un millón de llamadas a la función. El código también registrará los casos de borde, así que si la función produce un valor mayor que el máximo, o menor que el mínimo, usted lo sabrá.
<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>