Come posso generare numeri interi casuali tra due variabili specificate in JavaScript, ad esempio x = 4
e y = 8
produrrebbero qualsiasi 4, 5, 6, 7, 8
?
Ci sono alcuni esempi nella pagina 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;
}
hr>
Ecco la logica che c'è dietro. È una semplice regola del tre:
Math.random()
restituisce un Numero
tra 0 (incluso) e 1 (esclusivo). Quindi abbiamo un intervallo come questo:
[0 .................................... 1)
Ora, vorremmo un numero tra min
(incluso) e max
(esclusivo):
[0 .................................... 1)
[min .................................. max)
Possiamo usare il Math.random
per ottenere il corrispondente nell'intervallo [min, max]. Ma, prima dobbiamo fattorizzare un po' il problema sottraendo min
dal secondo intervallo:
[0 .................................... 1)
[min - min ............................ max - min)
Questo dà:
[0 .................................... 1)
[0 .................................... max - min)
Possiamo ora applicare Math.random
e calcolare il corrispondente. Scegliamo un numero casuale:
Math.random()
|
[0 .................................... 1)
[0 .................................... max - min)
|
x (what we need)
Quindi, per trovare x
, dovremmo fare:
x = Math.random() * (max - min);
Non dimenticare di aggiungere min
, in modo da ottenere un numero nell'intervallo [min, max]:
x = Math.random() * (max - min) + min;
Questa era la prima funzione della MDN. La seconda, restituisce un numero intero tra min
e max
, entrambi inclusi.
Ora, per ottenere numeri interi, potresti usare round
, ceil
o floor
.
Potresti usare Math.round(Math.random() * (max - min)) + min
, questo però dà una distribuzione non uniforme. Entrambi, min
e max
hanno solo circa la metà delle possibilità di tirare:
min...min+0.5...min+1...min+1.5 ... max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘ ← Math.round()
min min+1 max
Con max
escluso dall'intervallo, ha ancora meno possibilità di tirare rispetto a min
.
Con Math.floor(Math.random() * (max - min +1)) + min
si ha una distribuzione perfettamente uniforme.
min.... min+1... min+2 ... max-1... max.... max+1 (is excluded from interval)
| | | | | |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘ ← Math.floor()
min min+1 max-1 max
Non puoi usare ceil()
e -1
in questa equazione perché max
ha ora una possibilità in meno di tirare, ma puoi tirare anche il risultato (indesiderato) min-1
.
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.
}
ripartizione:
Stiamo restituendo una funzione (prendendo in prestito dalla programmazione funzionale) che, quando chiamata, restituirà un numero intero casuale tra i valori bottom
e top
, inclusi. Diciamo 'inclusivo' perché vogliamo includere sia il fondo che il cima nella gamma di numeri che possono essere restituiti. In questo modo, getRandomizer( 1, 6 )
restituirà o 1, 2, 3, 4, 5, o 6.
(il basso è il numero più basso, l'alto è il numero più grande)
Math.random() * ( 1 + top - bottom )
Math.random()
restituisce un doppio casuale tra 0 e 1, e se lo moltiplichiamo per uno più la differenza tra top
e bottom
, otterremo un doppio da qualche parte tra 0
e 1+b-a
.
Math.floor( Math.random() * ( 1 + top - bottom ) )
La funzione Math.floor
arrotonda il numero per difetto al numero intero più vicino. Così ora abbiamo tutti i numeri interi tra 0
e top-bottom
. L'1 sembra confuso, ma deve essere lì perché stiamo sempre arrotondando per difetto, quindi il numero superiore non sarà mai raggiunto senza di esso. Il decimale casuale che generiamo deve essere nell'intervallo da 0
a (1+top-bottom)
così possiamo arrotondare per difetto e ottenere un int nell'intervallo da 0
a top-bottom
.
Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom
Il codice dell'esempio precedente ci ha dato un intero nell'intervallo 0
e top-bottom
, quindi tutto quello che dobbiamo fare ora è aggiungere bottom
a quel risultato per ottenere un intero nell'intervallo bottom
e top
compreso. :D
NOTA: Se si passa in un valore non intero o il numero maggiore prima si otterrà un comportamento indesiderato, ma a meno che qualcuno lo richieda non ho intenzione di approfondire il codice di controllo degli argomenti in quanto è piuttosto lontano dall'intento della domanda originale.
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)));
}
Per testare questa funzione, e variazioni di questa funzione, salvate il seguente HTML/JavaScript in un file e apritelo con un browser. Il codice produrrà un grafico che mostra la distribuzione di un milione di chiamate di funzione. Il codice registrerà anche i casi limite, quindi se la funzione produce un valore maggiore del massimo, o minore del minimo, voi lo saprete.
<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>