Como posso gerar números inteiros aleatórios entre duas variáveis especificadas em JavaScript, por exemplo x = 4
e y = 8
, que sairiam qualquer um de 4, 5, 6, 7, 8
?
Há alguns exemplos na 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;
}
Aqui está a lógica por trás disso. É uma regra simples de três:
Math.random()
retorna um Número
entre 0 (inclusive) e 1 (exclusivo). Então nós temos um intervalo como este:
[0 .................................... 1)
Agora, gostaríamos de um número entre min
(inclusive) e max
(exclusivo):
[0 .................................... 1)
[min .................................. max)
Podemos utilizar o Math.random
para obter o correspondente no intervalo [min, max). Mas, primeiro devemos considerar um pouco o problema subtraindo o min
do segundo intervalo:
[0 .................................... 1)
[min - min ............................ max - min)
Isto dá:
[0 .................................... 1)
[0 .................................... max - min)
Podemos agora aplicar Math.random
e depois calcular o correspondente. Vamos escolher um número aleatório:
Math.random()
|
[0 .................................... 1)
[0 .................................... max - min)
|
x (what we need)
Então, para encontrar o x
, nós faríamos:
x = Math.random() * (max - min);
Não se esqueça de adicionar min
de volta, para que tenhamos um número no intervalo [min, max]:
x = Math.random() * (max - min) + min;
Essa foi a primeira função da MDN. A segunda, retorna um número inteiro entre min
e max
, ambas inclusivas.
Agora para obter números inteiros, você poderia usar "redondo", "tecto" ou "chão".
Você poderia utilizar Math.round(Math.random() * (max - min)) + min
, isto no entanto dá uma distribuição não uniforme. Ambos, min
e max
só têm aproximadamente metade da chance de rolar:
min...min+0.5...min+1...min+1.5 ... max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘ ← Math.round()
min min+1 max
Com o max
excluído do intervalo, tem ainda menos chance de rolar do que o min
.
Com Math.floor(Math.random() * (max - min +1)) + min
você tem uma distribuição perfeitamente uniforme.
min.... min+1... min+2 ... max-1... max.... max+1 (is excluded from interval)
| | | | | |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘ ← Math.floor()
min min+1 max-1 max
Você não pode utilizar ceil()
e -1
nessa equação porque max
agora tinha um pouco menos de chance de rolar, mas você pode rolar o resultado (indesejado) min-1
também.
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.
}
avaria:
Estamos retornando uma função (empréstimo de programação funcional) que quando chamada, retornará um inteiro aleatório entre os valores bottom
e top
, inclusive. Dizemos 'inclusive' porque queremos incluir tanto o inferior como o superior no intervalo de números que podem ser retornados. Desta forma, getRandomizer( 1, 6 )
irá retornar 1, 2, 3, 4, 5, ou 6.
(inferior é menor número, superior é maior número)
Math.random() * ( 1 + top - bottom )
Math.random()
retorna um duplo aleatório entre 0 e 1, e se multiplicarmos por um mais a diferença entre top
e bottom
, teremos um duplo em algum lugar entre 0
e 1+b-a
.
Math.floor( Math.random() * ( 1 + top - bottom ) )
"Matemática.piso" arredonda o número para o número inteiro mais próximo. Então agora temos todos os números inteiros entre "0" e "top-bottom". O 1 parece confuso, mas precisa de estar lá porque estamos sempre a arredondar para baixo, por isso o número superior nunca será realmente alcançado sem ele. O decimal aleatório que geramos precisa estar no intervalo 0
a (1+top-bottom)
para que possamos arredondar para baixo e obter um int no intervalo 0
a top-bottom
.
Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom
O código no exemplo anterior nos deu um número inteiro no intervalo 0
e top-bottom
, então tudo que precisamos fazer agora é adicionar bottom
a esse resultado para obter um número inteiro no intervalo bottom
e top
inclusive. :D
NOTA: Se você passar em um valor não-inteiro ou o maior número primeiro você terá um comportamento indesejável, mas a menos que alguém o solicite, eu não vou mergulhar no código de verificação de argumentos como estando bastante longe da intenção da pergunta 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 testar esta função, e variações desta função, salve o HTML/JavaScript abaixo em um arquivo e abra com um navegador. O código produzirá um gráfico mostrando a distribuição de um milhão de chamadas de função. O código também registrará os casos de borda, então se a função produzir um valor maior que o máximo, ou menor que o mínimo, você.saberá.sobre.ele.
<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>