Hoe kan ik willekeurige gehele getallen genereren tussen twee gespecificeerde variabelen in JavaScript, bijv. x = 4
en y = 8
zou een willekeurig van 4, 5, 6, 7, 8
opleveren?
Er staan enkele voorbeelden op de Mozilla Developer Network pagina:
/**
* 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;
}
Hier is de logica erachter. Het is een simpele regel van drie:
Math.random()
geeft een getal
tussen 0 (inclusief) en 1 (exclusief). Dus we hebben een interval als dit:
[0 .................................... 1)
Nu, we willen een getal tussen min
(inclusief) en max
(exclusief):
[0 .................................... 1)
[min .................................. max)
We kunnen Math.random
gebruiken om de overeenkomstige waarde in het [min, max] interval te krijgen. Maar, eerst moeten we het probleem een beetje ontbinden door min
van het tweede interval af te trekken:
[0 .................................... 1)
[min - min ............................ max - min)
Dit geeft:
[0 .................................... 1)
[0 .................................... max - min)
We kunnen nu Math.random
toepassen en dan het overeenkomstige uitrekenen. Laten we een willekeurig getal kiezen:
Math.random()
|
[0 .................................... 1)
[0 .................................... max - min)
|
x (what we need)
Dus, om x
te vinden, zouden we doen:
x = Math.random() * (max - min);
Vergeet niet min
weer toe te voegen, zodat we een getal in het [min, max] interval krijgen:
x = Math.random() * (max - min) + min;
Dat was de eerste functie van MDN. De tweede, geeft een geheel getal tussen min
en max
, beide inclusief.
Om gehele getallen te krijgen, kun je round
, ceil
of floor
gebruiken.
Je zou Math.round(Math.random() * (max - min)) + min
, dit geeft echter een ongelijke verdeling. Zowel min
als max
hebben maar ongeveer de helft van de kans om te rollen:
min...min+0.5...min+1...min+1.5 ... max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘ ← Math.round()
min min+1 max
Als max
van het interval wordt uitgesloten, heeft deze nog minder kans om te rollen dan min
.
Met Math.floor(Math.random() * (max - min +1)) + min
heb je een perfect gelijke verdeling.
min.... min+1... min+2 ... max-1... max.... max+1 (is excluded from interval)
| | | | | |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘ ← Math.floor()
min min+1 max-1 max
Je kunt ceil()
en -1
niet gebruiken in die vergelijking omdat max
nu iets minder kans had om te rollen, maar je kunt het (ongewenste) min-1
resultaat ook rollen.
function getRandomizer(bottom, top) {
return function() {
return Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom;
}
}
gebruik:
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.
}
uitsplitsing:
We geven een functie terug (ontleend aan functioneel programmeren) die, wanneer aangeroepen, een willekeurig geheel getal zal teruggeven tussen de waarden bottom
en top
, inclusief. We zeggen 'inclusive' omdat we zowel bottom als top willen opnemen in het bereik van getallen die kunnen worden teruggegeven. Op deze manier, getRandomizer( 1, 6 )
zal ofwel 1, 2, 3, 4, 5, of 6 teruggeven.
(onder is het laagste getal, boven is het hoogste getal)
Math.random() * ( 1 + top - bottom )
Math.random()
geeft een willekeurig getal tussen 0 en 1, en als we dat vermenigvuldigen met één plus het verschil tussen top
en onder
, krijgen we een getal ergens tussen 0
en 1+b-a
.
Math.floor( Math.random() * ( 1 + top - bottom ) )
Math.floor
rondt het getal naar beneden af op het dichtstbijzijnde gehele getal. We hebben nu dus alle gehele getallen tussen 0
en boven-onder
. De 1 ziet er verwarrend uit, maar hij moet er zijn omdat we altijd naar beneden afronden, dus het bovenste getal zal nooit bereikt worden zonder die 1. De willekeurige decimaal die we genereren moet in het bereik 0
tot (1+boven-onder)
liggen, zodat we naar beneden kunnen afronden en een getal in het bereik 0
tot boven-onder
krijgen
Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom
De code in het vorige voorbeeld gaf ons een geheel getal in het bereik 0
en top-bottom
, dus alles wat we nu nog moeten doen is bottom
toevoegen aan dat resultaat om een geheel getal te krijgen in het bereik bottom
en top
inclusief. :D
OPMERKING: Als je eerst een niet-integer getal of een groter getal opgeeft, krijg je ongewenst gedrag, maar tenzij iemand daarom vraagt ga ik me niet verdiepen in de argument controle code omdat het nogal ver af staat van de bedoeling van de oorspronkelijke vraag.
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)));
}
Om deze functie, en variaties op deze functie, te testen, sla de onderstaande HTML/JavaScript op in een bestand en open het met een browser. De code zal een grafiek maken met de verdeling van een miljoen functie-aanroepen. De code zal ook de randgevallen registreren, dus als de functie een waarde produceert die groter is dan de max, of kleiner dan de min, dan.zul.je.dat.weten.
<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>