Hvordan kan jeg generere tilfeldige heltall mellom to spesifiserte variabler i JavaScript, f.eks. x = 4
og y = 8
vil gi et av 4, 5, 6, 7, 8
?
Det finnes noen eksempler på siden 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;
}
Her er logikken bak. Det er en enkel treregel:
Math.random()
returnerer et Tall
mellom 0 (inkludert) og 1 (ekskludert). Så vi har et intervall som dette:
[0 .................................... 1)
Nå ønsker vi et tall mellom min
(inkludert) og max
(ekskludert):
[0 .................................... 1)
[min .................................. max)
Vi kan bruke Math.random
for å få korrespondenten i intervallet [min, max]. Men først bør vi faktorisere problemet litt ved å trekke min
fra det andre intervallet:
[0 .................................... 1)
[min - min ............................ max - min)
Dette gir:
[0 .................................... 1)
[0 .................................... max - min)
Vi kan nå bruke Math.random
og deretter beregne korrespondenten. La oss velge et tilfeldig tall:
Math.random()
|
[0 .................................... 1)
[0 .................................... max - min)
|
x (what we need)
Så, for å finne x
, ville vi gjøre:
x = Math.random() * (max - min);
Ikke glem å legge til min
tilbake, slik at vi får et tall i intervallet [min, max]:
x = Math.random() * (max - min) + min;
Det var den første funksjonen fra MDN. Den andre returnerer et heltall mellom min
og max
, begge inkludert.
Nå for å få heltall, kan du bruke round
, ceil
eller floor
.
Du kan bruke Math.round(Math.random() * (max - min)) + min
, dette gir imidlertid en ikke-jevn fordeling. Både min
og max
har bare omtrent halvparten av sjansen til å rulle:
min...min+0.5...min+1...min+1.5 ... max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘ ← Math.round()
min min+1 max
Med max
ekskludert fra intervallet har den enda mindre sjanse for å kaste enn min
.
Med Math.floor(Math.random() * (max - min +1)) + min
har du en perfekt jevn fordeling.
min.... min+1... min+2 ... max-1... max.... max+1 (is excluded from interval)
| | | | | |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘ ← Math.floor()
min min+1 max-1 max
Du kan ikke bruke ceil()
og -1
i denne ligningen fordi max
nå hadde en litt mindre sjanse til å rulle, men du kan rulle det (uønskede) min-1
resultatet også.
function getRandomizer(bottom, top) {
return function() {
return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;
}
}
bruk:
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.
}
sammenbrudd:
Vi returnerer en funksjon (låner fra funksjonell programmering) som når den kalles, vil returnere et tilfeldig heltall mellom verdiene bunn
og topp
, inkludert. Vi sier "inkludert" fordi vi ønsker å inkludere både bunn og topp i tallområdet som kan returneres. På denne måten vil getRandomizer( 1, 6 )
returnere enten 1, 2, 3, 4, 5 eller 6.
(nederst er det laveste tallet, øverst er det høyeste tallet).
Math.random() * ( 1 + top - bottom )
Math.random()
returnerer et tilfeldig dobbelttall mellom 0 og 1, og hvis vi multipliserer det med én pluss differansen mellom top
og bottom
, får vi et dobbelttall et sted mellom 0
og 1+b-a
.
Math.floor( Math.random() * ( 1 + top - bottom ) )
Math.floor
runder tallet ned til nærmeste heltall. Så vi har nå alle heltallene mellom 0
og top-bottom
. 1-tallet ser forvirrende ut, men det må være der fordi vi alltid avrunder nedover, så det øverste tallet vil faktisk aldri nås uten det. Det tilfeldige desimaltallet vi genererer må være i området 0
til (1+top-bottom)
slik at vi kan runde ned og få en int i området 0
til top-bottom
.
Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom
Koden i forrige eksempel ga oss et heltall i området 0
og top-bottom
, så alt vi trenger å gjøre nå er å legge bottom
til det resultatet for å få et heltall i området bottom
og top
inklusive :D
MERK: Hvis du sender inn en ikke-heltalverdi eller det største tallet først, får du uønsket oppførsel, men med mindre noen ber om det, kommer jeg ikke til å fordype meg i argumentkontrollkoden, da det er ganske langt fra hensikten med det opprinnelige spørsmålet.
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)));
}
For å teste denne funksjonen, og varianter av denne funksjonen, lagre HTML/JavaScript-koden nedenfor i en fil og åpne den med en nettleser. Koden vil produsere en graf som viser fordelingen av en million funksjonskall. Koden vil også registrere randtilfellene, så hvis funksjonen produserer en verdi som er større enn maks, eller mindre enn min, vil du vite om det.
<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>