I understand you can generate a random number in JavaScript within a range using this function:
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Courtesy of Ionuț G. Stan here.
What I want to know is if you can generate a better random number in a range using crypto.getRandomValues() instead of Math.random(). I would like to be able to generate a number between 0 and 10 inclusive, or 0 - 1, or even 10 - 5000 inclusive.
You'll note Math.random() produces a number like: 0.8565239671015732.
The getRandomValues API might return something like:
231 with Uint8Array(1)
54328 with Uint16Array(1)
355282741 with Uint32Array(1).
So how to translate that back to a decimal number so I can keep with the same range algorithm above? Or do I need a new algorithm?
Here's the code I tried but it doesn't work too well.
function getRandomInt(min, max) {
// Create byte array and fill with 1 random number
var byteArray = new Uint8Array(1);
window.crypto.getRandomValues(byteArray);
// Convert to decimal
var randomNum = '0.' + byteArray[0].toString();
// Get number in range
randomNum = Math.floor(randomNum * (max - min + 1)) + min;
return randomNum;
}
At the low end (range 0 - 1) it returns more 0's than 1's. What's the best way to do it with getRandomValues()?
Many thanks
IMHO, the easiest way to generate a random number in a [min..max] range with window.crypto.getRandomValues() is described here.
An ECMAScript 2015-syntax code, in case the link is TL;TR:
function getRandomIntInclusive(min, max) {
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
let randomNumber = randomBuffer[0] / (0xffffffff + 1);
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(randomNumber * (max - min + 1)) + min;
}
The easiest way is probably by rejection sampling (see http://en.wikipedia.org/wiki/Rejection_sampling). For example, assuming that max - min is less than 256:
function getRandomInt(min, max) {
// Create byte array and fill with 1 random number
var byteArray = new Uint8Array(1);
window.crypto.getRandomValues(byteArray);
var range = max - min + 1;
var max_range = 256;
if (byteArray[0] >= Math.floor(max_range / range) * range)
return getRandomInt(min, max);
return min + (byteArray[0] % range);
}
Many of these answers are going to produce biased results. Here's an unbiased solution.
function random(min, max) {
const range = max - min + 1
const bytes_needed = Math.ceil(Math.log2(range) / 8)
const cutoff = Math.floor((256 ** bytes_needed) / range) * range
const bytes = new Uint8Array(bytes_needed)
let value
do {
crypto.getRandomValues(bytes)
value = bytes.reduce((acc, x, n) => acc + x * 256 ** n, 0)
} while (value >= cutoff)
return min + value % range
}
If you are using Node.js, it is safer to use the cryptographically secure pseudorandom crypto.randomInt. Don't go write this kind of sensitive methods if you don't know what you are doing and without peer review.
Official documentation
crypto.randomInt([min, ]max[, callback])
Added in: v14.10.0, v12.19.0
min <integer> Start of random range (inclusive). Default: 0.
max <integer> End of random range (exclusive).
callback <Function> function(err, n) {}.
Return a random integer n such that min <= n < max. This implementation avoids modulo bias.
The range (max - min) must be less than 2^48. min and max must be safe integers.
If the callback function is not provided, the random integer is generated synchronously.
// Asynchronous
crypto.randomInt(3, (err, n) => {
if (err) throw err;
console.log(`Random number chosen from (0, 1, 2): ${n}`);
});
// Synchronous
const n = crypto.randomInt(3);
console.log(`Random number chosen from (0, 1, 2): ${n}`);
// With `min` argument
const n = crypto.randomInt(1, 7);
console.log(`The dice rolled: ${n}`);
Necromancing.
Well, this is easy to solve.
Consider random number in ranges without crypto-random:
// 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;
}
So all you need to do is replace Math.random with a random from crypt.
So what does Math.random do ?
According to MDN, the Math.random() function returns a floating-point, pseudo-random number in the range 0 to less than 1 (inclusive of 0, but not 1)
So we need a crypto-random number >= 0 and < 1 (not <=).
So, we need a non-negative (aka. UNSIGNED) integer from getRandomValues.
How do we do this?
Simple:
Instead of getting an integer, and then doing Math.abs, we just get an UInt:
var randomBuffer = new Int8Array(4); // Int8Array = byte, 1 int = 4 byte = 32 bit
window.crypto.getRandomValues(randomBuffer);
var dataView = new DataView(array.buffer);
var uint = dataView.getUint32();
The shorthand version of which is
var randomBuffer = new Uint32Array(1);
(window.crypto || window.msCrypto).getRandomValues(randomBuffer);
var uint = randomBuffer[0];
Now all we need to do is divide uint by uint32.MaxValue (aka 0xFFFFFFFF) to get a floating-point number. And because we cannot have 1 in the result-set, we need to divide by (uint32.MaxValue+1) to ensure the result is < 1.
Dividing by (UInt32.MaxValue + 1) works because a JavaScript integer is a 64-bit floating-point number internally, so it is not limited at 32 bit.
function cryptoRand()
{
var array = new Int8Array(4);
(window.crypto || window.msCrypto).getRandomValues(array);
var dataView = new DataView(array.buffer);
var uint = dataView.getUint32();
var f = uint / (0xffffffff + 1); // 0xFFFFFFFF = uint32.MaxValue (+1 because Math.random is inclusive of 0, but not 1)
return f;
}
the shorthand of which is
function cryptoRand()
{
const randomBuffer = new Uint32Array(1);
(window.crypto || window.msCrypto).getRandomValues(randomBuffer);
return ( randomBuffer[0] / (0xffffffff + 1) );
}
Now all you need to do is replace Math.random() with cryptoRand() in the above functions.
Note that if crypto.getRandomValues uses the Windows-CryptoAPI on Windows to get the random bytes, you should not consider these values a truly cryptographically secure source of entropy.
Rando.js uses crypto.getRandomValues to basically do this for you
console.log(rando(5, 10));
<script src="https://randojs.com/2.0.0.js"></script>
This is carved out of the source code if you want to look behind the curtain:
var cryptoRandom = () => {
try {
var cryptoRandoms, cryptoRandomSlices = [],
cryptoRandom;
while ((cryptoRandom = "." + cryptoRandomSlices.join("")).length < 30) {
cryptoRandoms = (window.crypto || window.msCrypto).getRandomValues(new Uint32Array(5));
for (var i = 0; i < cryptoRandoms.length; i++) {
var cryptoRandomSlice = cryptoRandoms[i].toString().slice(1, -1);
if (cryptoRandomSlice.length > 0) cryptoRandomSlices[cryptoRandomSlices.length] = cryptoRandomSlice;
}
}
return Number(cryptoRandom);
} catch (e) {
return Math.random();
}
};
var min = 5;
var max = 10;
if (min > max) var temp = max, max = min, min = temp;
min = Math.floor(min), max = Math.floor(max);
console.log( Math.floor(cryptoRandom() * (max - min + 1) + min) );
Read this if you're concerned about the randomness of your number:
If you use a 6 sided dice to generate a random number 1 thru 5, what do you do when you land on 6? There's two strategies:
Re-roll until you get a 1 thru 5. This maintains the randomness, but creates extra work.
Map the 6 to one of the numbers you do want, like 5. This is less work, but now you skewed your distribution and are going to get extra 5s.
Strategy 1 is the "rejection sampling" mentioned by #arghbleargh and used in their answer and a few other answers.
Strategy 2 is what #Chris_F is referring to as producing biased results.
So understand that all solutions to the original post's question require mapping from one pseudo-random distribution of numbers to another distribution with a different number of 'buckets'.
Strategy 2 is probably fine because:
With strategy 2, as long as you are taking the modulo then no resulting number will be more than 2x as likely as any other number. So it is not significantly easier to guess than strategy 1.
And as long as your source distribution is MUCH bigger than your target distribution, the skew in randomness will be negligible unless you're running a Monte Carlo simulation or something (which you probably wouldn't be doing in JavaScript to begin with, or at least you wouldn't be using the crypto library for that).
Math.random() uses strategy 2, maps from a ~52 bit number (2^52 unique numbers), though some environments use less precision (see here).
Is there a way to generate a random number in a specified range with JavaScript ?
For example: a specified range from 1 to 6 were the random number could be either 1, 2, 3, 4, 5, or 6.
function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min)
}
const rndInt = randomIntFromInterval(1, 6)
console.log(rndInt)
What it does "extra" is it allows random intervals that do not start with 1.
So you can get a random number from 10 to 15 for example. Flexibility.
Important
The following code works only if the minimum value is `1`. It does not work for minimum values other than `1`.
If you wanted to get a random integer between 1 (and only 1) and 6, you would calculate:
const rndInt = Math.floor(Math.random() * 6) + 1
console.log(rndInt)
Where:
1 is the start number
6 is the number of possible results (1 + start (6) - end (1))
Math.random()
Returns an integer random number between min (included) and max (included):
function randomInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Or any random number between min (included) and max (not included):
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
Useful examples (integers):
// 0 -> 10
Math.floor(Math.random() * 11);
// 1 -> 10
Math.floor(Math.random() * 10) + 1;
// 5 -> 20
Math.floor(Math.random() * 16) + 5;
// -10 -> (-2)
Math.floor(Math.random() * 9) - 10;
** And always nice to be reminded (Mozilla):
Math.random() does not provide cryptographically secure random
numbers. Do not use them for anything related to security. Use the Web
Crypto API instead, and more precisely the
window.crypto.getRandomValues() method.
Other solutions:
(Math.random() * 6 | 0) + 1
~~(Math.random() * 6) + 1
Try online
TL;DR
function generateRandomInteger(min, max) {
return Math.floor(min + Math.random()*(max - min + 1))
}
To get the random number
generateRandomInteger(-20, 20);
EXPLANATION BELOW
integer - A number which is not a fraction; a whole number
We need to get a random number , say X between min and max.
X, min and max are all integers
i.e
min <= X <= max
If we subtract min from the equation, this is equivalent to
0 <= (X - min) <= (max - min)
Now, lets multiply this with a random number r
which is
0 <= (X - min) * r <= (max - min) * r
Now, lets add back min to the equation
min <= min + (X - min) * r <= min + (max - min) * r
For, any given X, the above equation satisfies only when r has range of [0,1] For any other values of r the above equation is unsatisfied.
Learn more about ranges [x,y] or (x,y) here
Our next step is to find a function which always results in a value which has a range of [0,1]
Now, the range of r i.e [0,1] is very similar to Math.random() function in Javascript. Isn't it?
The Math.random() function returns a floating-point, pseudo-random
number in the range [0, 1); that is, from 0 (inclusive) up to but not
including 1 (exclusive)
Random Function using Math.random() 0 <= r < 1
Notice that in Math.random() left bound is inclusive and the right bound is exclusive. This means min + (max - min) * r will evaluate to having a range from [min, max)
To include our right bound i.e [min,max] we increase the right bound by 1 and floor the result.
function generateRandomInteger(min, max) {
return Math.floor(min + Math.random()*(max - min + 1))
}
To get the random number
generateRandomInteger(-20, 20);
Or, in Underscore
_.random(min, max)
var x = 6; // can be any number
var rand = Math.floor(Math.random()*x) + 1;
jsfiddle: https://jsfiddle.net/cyGwf/477/
Random Integer: to get a random integer between min and max, use the following code
function getRandomInteger(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
Random Floating Point Number: to get a random floating point number between min and max, use the following code
function getRandomFloat(min, max) {
return Math.random() * (max - min) + min;
}
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
Math is not my strong point, but I've been working on a project where I needed to generate a lot of random numbers between both positive and negative.
function randomBetween(min, max) {
if (min < 0) {
return min + Math.random() * (Math.abs(min)+max);
}else {
return min + Math.random() * max;
}
}
E.g
randomBetween(-10,15)//or..
randomBetween(10,20)//or...
randomBetween(-200,-100)
Of course, you can also add some validation to make sure you don't do this with anything other than numbers. Also make sure that min is always less than or equal to max.
Get a random integer between 0 and 400
let rand = Math.round(Math.random() * 400)
document.write(rand)
Get a random integer between 200 and 1500
let range = {min: 200, max: 1500}
let delta = range.max - range.min
const rand = Math.round(range.min + Math.random() * delta)
document.write(rand)
Using functions
function randBetween(min, max){
let delta = max - min
return Math.round(min + Math.random() * delta)
}
document.write(randBetween(10, 15));
// JavaScript ES6 arrow function
const randBetween = (min, max) => {
let delta = max - min
return Math.round(min + Math.random() * delta)
}
document.write(randBetween(10, 20))
I wrote more flexible function which can give you random number but not only integer.
function rand(min,max,interval)
{
if (typeof(interval)==='undefined') interval = 1;
var r = Math.floor(Math.random()*(max-min+interval)/interval);
return r*interval+min;
}
var a = rand(0,10); //can be 0, 1, 2 (...) 9, 10
var b = rand(4,6,0.1); //can be 4.0, 4.1, 4.2 (...) 5.9, 6.0
Fixed version.
ES6 / Arrow functions version based on Francis' code (i.e. the top answer):
const randomIntFromInterval = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
Example
Return a random number between 1 and 10:
Math.floor((Math.random() * 10) + 1);
The result could be:
3
Try yourself: here
--
or using lodash / undescore:
_.random(min, max)
Docs:
- lodash
- undescore
The top rated solution is not mathematically correct as same as comments under it -> Math.floor(Math.random() * 6) + 1.
Task: generate random number between 1 and 6.
Math.random() returns floating point number between 0 and 1 (like 0.344717274374 or 0.99341293123 for example), which we will use as a percentage, so Math.floor(Math.random() * 6) + 1 returns some percentage of 6 (max: 5, min: 0) and adds 1. The author got lucky that lower bound was 1., because percentage floor will "maximumly" return 5 which is less than 6 by 1, and that 1 will be added by lower bound 1.
The problems occurs when lower bound is greater than 1. For instance,
Task: generate random between 2 and 6.
(following author's logic)
Math.floor(Math.random() * 6) + 2, it is obviously seen that if we get 5 here -> Math.random() * 6 and then add 2, the outcome will be 7 which goes beyond the desired boundary of 6.
Another example,
Task: generate random between 10 and 12.
(following author's logic)
Math.floor(Math.random() * 12) + 10, (sorry for repeating) it is obvious that we are getting 0%-99% percent of number "12", which will go way beyond desired boundary of 12.
So, the correct logic is to take the difference between lower bound and upper bound add 1, and only then floor it (to substract 1, because Math.random() returns 0 - 0.99, so no way to get full upper bound, thats why we adding 1 to upper bound to get maximumly 99% of (upper bound + 1) and then we floor it to get rid of excess). Once we got the floored percentage of (difference + 1), we can add lower boundary to get the desired randomed number between 2 numbers.
The logic formula for that will be: Math.floor(Math.random() * ((up_boundary - low_boundary) + 1)) + 10.
P.s.: Even comments under the top-rated answer were incorrect, since people forgot to add 1 to the difference, meaning that they will never get the up boundary (yes it might be a case if they dont want to get it at all, but the requirenment was to include the upper boundary).
I was searching random number generator written in TypeScript and I have written this after reading all of the answers, hope It would work for TypeScript coders.
Rand(min: number, max: number): number {
return (Math.random() * (max - min + 1) | 0) + min;
}
Inspite of many answers and almost same result. I would like to add my answer and explain its working. Because it is important to understand its working rather than copy pasting one line code. Generating random numbers is nothing but simple maths.
CODE:
function getR(lower, upper) {
var percent = (Math.random() * 100);
// this will return number between 0-99 because Math.random returns decimal number from 0-0.9929292 something like that
//now you have a percentage, use it find out the number between your INTERVAL :upper-lower
var num = ((percent * (upper - lower) / 100));
//num will now have a number that falls in your INTERVAL simple maths
num += lower;
//add lower to make it fall in your INTERVAL
//but num is still in decimal
//use Math.floor>downward to its nearest integer you won't get upper value ever
//use Math.ceil>upward to its nearest integer upper value is possible
//Math.round>to its nearest integer 2.4>2 2.5>3 both lower and upper value possible
console.log(Math.floor(num), Math.ceil(num), Math.round(num));
}
Math.random() is fast and suitable for many purposes, but it's not appropriate if you need cryptographically-secure values (it's not secure), or if you need integers from a completely uniform unbiased distribution (the multiplication approach used in others answers produces certain values slightly more often than others).
In such cases, we can use crypto.getRandomValues() to generate secure integers, and reject any generated values that we can't map uniformly into the target range. This will be slower, but it shouldn't be significant unless you're generating extremely large numbers of values.
To clarify the biased distribution concern, consider the case where we want to generate a value between 1 and 5, but we have a random number generator that produces values between 1 and 16 (a 4-bit value). We want to have the same number of generated values mapping to each output value, but 16 does not evenly divide by 5: it leaves a remainder of 1. So we need to reject 1 of the possible generated values, and only continue when we get one of the 15 lesser values that can be uniformly mapped into our target range. Our behaviour could look like this pseudocode:
Generate a 4-bit integer in the range 1-16.
If we generated 1, 6, or 11 then output 1.
If we generated 2, 7, or 12 then output 2.
If we generated 3, 8, or 13 then output 3.
If we generated 4, 9, or 14 then output 4.
If we generated 5, 10, or 15 then output 5.
If we generated 16 then reject it and try again.
The following code uses similar logic, but generates a 32-bit integer instead, because that's the largest common integer size that can be represented by JavaScript's standard number type. (This could be modified to use BigInts if you need a larger range.) Regardless of the chosen range, the fraction of generated values that are rejected will always be less than 0.5, so the expected number of rejections will always be less than 1.0 and usually close to 0.0; you don't need to worry about it looping forever.
const randomInteger = (min, max) => {
const range = max - min;
const maxGeneratedValue = 0xFFFFFFFF;
const possibleResultValues = range + 1;
const possibleGeneratedValues = maxGeneratedValue + 1;
const remainder = possibleGeneratedValues % possibleResultValues;
const maxUnbiased = maxGeneratedValue - remainder;
if (!Number.isInteger(min) || !Number.isInteger(max) ||
max > Number.MAX_SAFE_INTEGER || min < Number.MIN_SAFE_INTEGER) {
throw new Error('Arguments must be safe integers.');
} else if (range > maxGeneratedValue) {
throw new Error(`Range of ${range} (from ${min} to ${max}) > ${maxGeneratedValue}.`);
} else if (max < min) {
throw new Error(`max (${max}) must be >= min (${min}).`);
} else if (min === max) {
return min;
}
let generated;
do {
generated = crypto.getRandomValues(new Uint32Array(1))[0];
} while (generated > maxUnbiased);
return min + (generated % possibleResultValues);
};
console.log(randomInteger(-8, 8)); // -2
console.log(randomInteger(0, 0)); // 0
console.log(randomInteger(0, 0xFFFFFFFF)); // 944450079
console.log(randomInteger(-1, 0xFFFFFFFF));
// Error: Range of 4294967296 covering -1 to 4294967295 is > 4294967295.
console.log(new Array(12).fill().map(n => randomInteger(8, 12)));
// [11, 8, 8, 11, 10, 8, 8, 12, 12, 12, 9, 9]
to return 1-6 like a dice basically,
return Math.round(Math.random() * 5 + 1);
Adding float with fixed precision version based on the int version in #Francisc's answer:
function randomFloatFromInterval (min, max, fractionDigits) {
const fractionMultiplier = Math.pow(10, fractionDigits)
return Math.round(
(Math.random() * (max - min) + min) * fractionMultiplier,
) / fractionMultiplier
}
so:
randomFloatFromInterval(1,3,4) // => 2.2679, 1.509, 1.8863, 2.9741, ...
and for int answer
randomFloatFromInterval(1,3,0) // => 1, 2, 3
Crypto-strong random integer number in range [a,b] (assumption: a < b )
let rand= (a,b)=> a+(b-a+1)*crypto.getRandomValues(new Uint32Array(1))[0]/2**32|0
console.log( rand(1,6) );
This function can generate a random integer number between (and including) min and max numbers:
function randomNumber(min, max) {
if (min > max) {
let temp = max;
max = min;
min = temp;
}
if (min <= 0) {
return Math.floor(Math.random() * (max + Math.abs(min) + 1)) + min;
} else {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
Example:
randomNumber(-2, 3); // can be -2, -1, 0, 1, 2 and 3
randomNumber(-5, -2); // can be -5, -4, -3 and -2
randomNumber(0, 4); // can be 0, 1, 2, 3 and 4
randomNumber(4, 0); // can be 0, 1, 2, 3 and 4
Using random function, which can be reused.
function randomNum(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
randomNum(1, 6);
This should work:
const getRandomNum = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
If the starting number is 1, as in your example (1-6), you can use Math.ceil() method instead of Math.floor().
Math.ceil(Math.random() * 6)
instead of
Math.floor(Math.random() * 6) + 1
Let's not forget other useful Math methods.
This is about nine years late, but randojs.com makes this a simple one-liner:
rando(1, 6)
You just need to add this to the head of your html document, and you can do pretty much whatever you want with randomness easily. Random values from arrays, random jquery elements, random properties from objects, and even preventing repetitions if needed.
<script src="https://randojs.com/1.0.0.js"></script>
Try using:
function random(min, max) {
return Math.round((Math.random() *( Math.abs(max - min))) + min);
}
console.log(random(1, 6));
Short Answer: It's achievable using a simple array.
you can alternate within array elements.
This solution works even if your values are not consecutive. Values don't even have to be a number.
let array = [1, 2, 3, 4, 5, 6];
const randomValue = array[Math.floor(Math.random() * array.length)];
This simple function is handy and works in ANY cases (fully tested).
Also, the distribution of the results has been fully tested and is 100% correct.
function randomInteger(pMin = 1, pMax = 1_000_000_000)
//Author: Axel Gauffre.
//Here: https://stackoverflow.com/a/74636954/5171000
//Inspired by: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#getting_a_random_number_between_two_values
//
//This function RETURNS A RANDOM INTEGER between pMin (INCLUDED) and pMax (INCLUDED).
// - pMin and pMax should be integers.
// - HOWEVER, if pMin and/or pMax are FLOATS, they will be ROUNDED to the NEAREST integer.
// - NEGATIVE values ARE supported.
// - The ORDER of the 2 arguments has NO consequence: If pMin > pMax, then pMin and pMax will simply be SWAPPED.
// - If pMin is omitted, it will DEFAULT TO 1.
// - If pMax is omitted, it will DEFAULT TO 1 BILLION.
//
//This function works in ANY cases (fully tested).
//Also, the distribution of the results has been fully tested and is 100% correct.
{
pMin = Math.round(pMin);
pMax = Math.round(pMax);
if (pMax < pMin) { let t = pMin; pMin = pMax; pMax = t;}
return Math.floor(Math.random() * (pMax+1 - pMin) + pMin);
}
I discovered a great new way to do this using ES6 default parameters. It is very nifty since it allows either one argument or two arguments. Here it is:
function random(n, b = 0) {
return Math.random() * (b-n) + n;
}
This works for me and produces values like Python's random.randint standard library function:
function randint(min, max) {
return Math.round((Math.random() * Math.abs(max - min)) + min);
}
console.log("Random integer: " + randint(-5, 5));
I understand you can generate a random number in JavaScript within a range using this function:
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Courtesy of Ionuț G. Stan here.
What I want to know is if you can generate a better random number in a range using crypto.getRandomValues() instead of Math.random(). I would like to be able to generate a number between 0 and 10 inclusive, or 0 - 1, or even 10 - 5000 inclusive.
You'll note Math.random() produces a number like: 0.8565239671015732.
The getRandomValues API might return something like:
231 with Uint8Array(1)
54328 with Uint16Array(1)
355282741 with Uint32Array(1).
So how to translate that back to a decimal number so I can keep with the same range algorithm above? Or do I need a new algorithm?
Here's the code I tried but it doesn't work too well.
function getRandomInt(min, max) {
// Create byte array and fill with 1 random number
var byteArray = new Uint8Array(1);
window.crypto.getRandomValues(byteArray);
// Convert to decimal
var randomNum = '0.' + byteArray[0].toString();
// Get number in range
randomNum = Math.floor(randomNum * (max - min + 1)) + min;
return randomNum;
}
At the low end (range 0 - 1) it returns more 0's than 1's. What's the best way to do it with getRandomValues()?
Many thanks
IMHO, the easiest way to generate a random number in a [min..max] range with window.crypto.getRandomValues() is described here.
An ECMAScript 2015-syntax code, in case the link is TL;TR:
function getRandomIntInclusive(min, max) {
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
let randomNumber = randomBuffer[0] / (0xffffffff + 1);
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(randomNumber * (max - min + 1)) + min;
}
The easiest way is probably by rejection sampling (see http://en.wikipedia.org/wiki/Rejection_sampling). For example, assuming that max - min is less than 256:
function getRandomInt(min, max) {
// Create byte array and fill with 1 random number
var byteArray = new Uint8Array(1);
window.crypto.getRandomValues(byteArray);
var range = max - min + 1;
var max_range = 256;
if (byteArray[0] >= Math.floor(max_range / range) * range)
return getRandomInt(min, max);
return min + (byteArray[0] % range);
}
Many of these answers are going to produce biased results. Here's an unbiased solution.
function random(min, max) {
const range = max - min + 1
const bytes_needed = Math.ceil(Math.log2(range) / 8)
const cutoff = Math.floor((256 ** bytes_needed) / range) * range
const bytes = new Uint8Array(bytes_needed)
let value
do {
crypto.getRandomValues(bytes)
value = bytes.reduce((acc, x, n) => acc + x * 256 ** n, 0)
} while (value >= cutoff)
return min + value % range
}
If you are using Node.js, it is safer to use the cryptographically secure pseudorandom crypto.randomInt. Don't go write this kind of sensitive methods if you don't know what you are doing and without peer review.
Official documentation
crypto.randomInt([min, ]max[, callback])
Added in: v14.10.0, v12.19.0
min <integer> Start of random range (inclusive). Default: 0.
max <integer> End of random range (exclusive).
callback <Function> function(err, n) {}.
Return a random integer n such that min <= n < max. This implementation avoids modulo bias.
The range (max - min) must be less than 2^48. min and max must be safe integers.
If the callback function is not provided, the random integer is generated synchronously.
// Asynchronous
crypto.randomInt(3, (err, n) => {
if (err) throw err;
console.log(`Random number chosen from (0, 1, 2): ${n}`);
});
// Synchronous
const n = crypto.randomInt(3);
console.log(`Random number chosen from (0, 1, 2): ${n}`);
// With `min` argument
const n = crypto.randomInt(1, 7);
console.log(`The dice rolled: ${n}`);
Necromancing.
Well, this is easy to solve.
Consider random number in ranges without crypto-random:
// 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;
}
So all you need to do is replace Math.random with a random from crypt.
So what does Math.random do ?
According to MDN, the Math.random() function returns a floating-point, pseudo-random number in the range 0 to less than 1 (inclusive of 0, but not 1)
So we need a crypto-random number >= 0 and < 1 (not <=).
So, we need a non-negative (aka. UNSIGNED) integer from getRandomValues.
How do we do this?
Simple:
Instead of getting an integer, and then doing Math.abs, we just get an UInt:
var randomBuffer = new Int8Array(4); // Int8Array = byte, 1 int = 4 byte = 32 bit
window.crypto.getRandomValues(randomBuffer);
var dataView = new DataView(array.buffer);
var uint = dataView.getUint32();
The shorthand version of which is
var randomBuffer = new Uint32Array(1);
(window.crypto || window.msCrypto).getRandomValues(randomBuffer);
var uint = randomBuffer[0];
Now all we need to do is divide uint by uint32.MaxValue (aka 0xFFFFFFFF) to get a floating-point number. And because we cannot have 1 in the result-set, we need to divide by (uint32.MaxValue+1) to ensure the result is < 1.
Dividing by (UInt32.MaxValue + 1) works because a JavaScript integer is a 64-bit floating-point number internally, so it is not limited at 32 bit.
function cryptoRand()
{
var array = new Int8Array(4);
(window.crypto || window.msCrypto).getRandomValues(array);
var dataView = new DataView(array.buffer);
var uint = dataView.getUint32();
var f = uint / (0xffffffff + 1); // 0xFFFFFFFF = uint32.MaxValue (+1 because Math.random is inclusive of 0, but not 1)
return f;
}
the shorthand of which is
function cryptoRand()
{
const randomBuffer = new Uint32Array(1);
(window.crypto || window.msCrypto).getRandomValues(randomBuffer);
return ( randomBuffer[0] / (0xffffffff + 1) );
}
Now all you need to do is replace Math.random() with cryptoRand() in the above functions.
Note that if crypto.getRandomValues uses the Windows-CryptoAPI on Windows to get the random bytes, you should not consider these values a truly cryptographically secure source of entropy.
Rando.js uses crypto.getRandomValues to basically do this for you
console.log(rando(5, 10));
<script src="https://randojs.com/2.0.0.js"></script>
This is carved out of the source code if you want to look behind the curtain:
var cryptoRandom = () => {
try {
var cryptoRandoms, cryptoRandomSlices = [],
cryptoRandom;
while ((cryptoRandom = "." + cryptoRandomSlices.join("")).length < 30) {
cryptoRandoms = (window.crypto || window.msCrypto).getRandomValues(new Uint32Array(5));
for (var i = 0; i < cryptoRandoms.length; i++) {
var cryptoRandomSlice = cryptoRandoms[i].toString().slice(1, -1);
if (cryptoRandomSlice.length > 0) cryptoRandomSlices[cryptoRandomSlices.length] = cryptoRandomSlice;
}
}
return Number(cryptoRandom);
} catch (e) {
return Math.random();
}
};
var min = 5;
var max = 10;
if (min > max) var temp = max, max = min, min = temp;
min = Math.floor(min), max = Math.floor(max);
console.log( Math.floor(cryptoRandom() * (max - min + 1) + min) );
Read this if you're concerned about the randomness of your number:
If you use a 6 sided dice to generate a random number 1 thru 5, what do you do when you land on 6? There's two strategies:
Re-roll until you get a 1 thru 5. This maintains the randomness, but creates extra work.
Map the 6 to one of the numbers you do want, like 5. This is less work, but now you skewed your distribution and are going to get extra 5s.
Strategy 1 is the "rejection sampling" mentioned by #arghbleargh and used in their answer and a few other answers.
Strategy 2 is what #Chris_F is referring to as producing biased results.
So understand that all solutions to the original post's question require mapping from one pseudo-random distribution of numbers to another distribution with a different number of 'buckets'.
Strategy 2 is probably fine because:
With strategy 2, as long as you are taking the modulo then no resulting number will be more than 2x as likely as any other number. So it is not significantly easier to guess than strategy 1.
And as long as your source distribution is MUCH bigger than your target distribution, the skew in randomness will be negligible unless you're running a Monte Carlo simulation or something (which you probably wouldn't be doing in JavaScript to begin with, or at least you wouldn't be using the crypto library for that).
Math.random() uses strategy 2, maps from a ~52 bit number (2^52 unique numbers), though some environments use less precision (see here).
I'm trying to create a javascript function that accepts 2 parameters min and max and generates a random number between the two integers. That part is easy.
Where things get rough is that I need to create a conditional that says
function generateNum (min, max) {
var randNumber = Math.ceil(Math.random()*(max - min)+min);
if (randNumber === max) {
// store the result (probably in an array) and generate a new
// number with the same behavior as randNumber (e.g. it is also
// stores it's total in the array and recursively "re-generates
// a new number until max is not hit)
}
}
The idea is to recursive-ise this so that a running total of the number of max hits is stored, combined, and then returned.
For example: The script receives min / max parameters of 5/10 generateNum(5,10){}. If the value generated by randNumber were 5, 6, 7, 8, or 9 then there would be no recursion and the function would return that value. If the value generated by randNumber is 10, then the value 10 is stored in an array and the function now "re-tries" recursively (meaning that as many times as 10 is generated, then that value is stored as an additional object in the array and the function re-tries). When the process stops (which could be infinite but has a parabolically decreasing probability of repeating with each recursion). The final number (5, 6, 7, 8, 9) would be added to the total of generated max values and the result would be returned.
Quite an unusual mathematic scenario, let me know how I can clarify if that doesn't make sense.
That part is easy.
Not as easy as you think... The algorithm that you have is broken; it will almost never give you the minimum value. Use the Math.floor method instead, and add one to the range:
var randNumber = Math.floor(Math.random() * (max - min + 1)) + min;
To do this recursively is simple, just call the method from itself:
function generateNum (min, max) {
var randNumber = Math.floor(Math.random()*(max - min + 1)) + min;
if (randNumber == max) {
randNumber += generateNum(min, max);
}
return randNumber;
}
You can also solve this without recursion:
function generateNum (min, max) {
var randNumber = 0;
do {
var num = Math.floor(Math.random()*(max - min + 1)) + min;
randNumber += num;
} while (num == max);
return randNumber;
}
There is no need to use an array in either case, as you don't need the seprate values in the end, you only need the sum of the values.
I assume that you don't really need a recursive solution since you tagged this for-loop. This will return the number of times the max number was picked:
function generateNum (min, max) {
var diff = max - min;
if(diff <= 0)
return;
for(var i = 0; diff == Math.floor(Math.random()*(diff + 1)); i++);
return i;
}
Example outputs:
generateNum(1,2) // 3
generateNum(1,2) // 1
generateNum(1,2) // 0
generateNum(5,10) // 0
generateNum(5,10) // 1
Two things:
1) the probability to roll 10 stays (theoretically the same on each roll (re-try)), the low probability is of hitting n times 10 in a row
2) I don't see why recursion is needed, what about a while loop?
var randNumber;
var arr = [];
while ((randNumber = Math.ceil(Math.random()*(max - min)+min)) === max) {
arr.push(
}
I'd consider an idea that you don't need to use not only recursion and arrays but not even a for loop.
I think you need a simple expression like this one (separated into three for clarity):
function generateNum (min, max)
{
var randTail = Math.floor(Math.random()*(max - min)+min);
var randRepeatMax = Math.floor(Math.log(Math.random()) / Math.log(1/(max-min+1)));
return randRepeatMax*max + randTail;
}
Assuming one random number is as good as another, this should give you the same distribution of values as the straightforward loop.
Recursive method:
function generateNum (min, max) {
var res = Math.floor(Math.random() * (max - min + 1)) + min;
return (res === max) ? [res].concat(generateNum(min, max)) : res;
}