JavaScript for Random Numbers with Recursion - javascript

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;
}

Related

Why not unique numbers are still coming?

I need to generate the unique numbers, join it with the letter and push to array.
arr = []
for (var i = 0; i < 5; i++) {
function generateRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
var randomNum = generateRandomNumber(1, 15);
if (!arr.includes(randomNum)) {
arr.push('B' + randomNum.toString())
} else {
if (arr.length < 5) {
return generateRandomNumber(min, max)
} else break
}
}
I have provisioned uniqueness check, however the same values are still coming.
Condition only checked once in the loop.
Also if the condition is in the else state, there is a chance that there will also be same number as previous.
You can use this approach
let arr = []
function generateRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function add(){
var randomNum = generateRandomNumber(1, 15);
if(arr.length < 5){
if (!arr.includes('B' + randomNum.toString())) {
arr.push('B' + randomNum.toString())
}
add()
}
}
add()
console.log(arr)
I have provisioned uniqueness check, however the same values are still coming.
The uniqueness check is incorrect.
The array contains only strings starting with letter 'B' (added by the line arr.push('B' + randomNum.toString())) while the uniqueness check searches for numbers.
The call arr.includes(randomNum) always returns false. Each and every generated value of randomNum is prefixed with 'B' and pushed into the array, no matter what the array contains.
Try a simpler approach: generate numbers, add them to the list if they are not already there, stop when the list is large enough (five items).
Then run through the list and add the 'B' prefix to each item.
Like this:
function generateRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// Generate 5 unique random numbers between 1 and 15
let arr = []
while (arr.length < 5) {
let num = generateRandomNumber(1, 15);
// Ignore a value that has already been generated
if (!arr.includes(num)) {
arr.push(num);
}
}
// Add the 'B' prefix to the generated numbers
arr = arr.map((num) => `B${num}`);
console.log(arr);

How to utilize mersenne twister to get evenly distributed int in the range of [0,n] from random unsigned 32-bit integer in javascript? [duplicate]

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).

Javascript: Generate a random number within a range using crypto.getRandomValues

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).

Splice only working part of the time

Here i have an array with undefined number of elements. I tried to print random element of this array and cut it. Here my code.
function rand(min, max){
return (Math.floor(Math.random() * (max - min + 1)) + min).toFixed(0);
}
$('#do').click(function(){
var count = chamarr.length;
var num = 0;
if (count == 1) {
$('#output').html('Nothing can found');
} else {
num = rand(1,chamarr.length);
$('#output').html(chamarr[num]);
chamarr.splice(num,1);
}
});
When I logged an array is cutted, I saw that always ok, but sometimes element is not cut!
My guess is that the problem is with your randnum method:
function rand(min, max){
return (Math.floor(Math.random() * (max - min + 1)) + min).toFixed(0);
}
I believe this will give you a value in the range [min, max] - inclusive at both ends. (Well, actually, it will give you a string version of that value as toFixed returns a string, but when you use it later it'll get coerced back into a number.)
Now you're calling it like this:
num = rand(1,chamarr.length);
So if the array is 6 elements long, you'll get a value in the range [1, 6]. But then you'll try to take chamarr[num] - and the range of valid indexes is [0, 5] as arrays are 0-based. If you try to take element 6, that will give you undefined - but then splicing at element 6 won't do anything.
I would change your rand method to be exclusive at the upper bound, like this:
function rand(min, max) {
return (Math.floor(Math.random() * (max - min)) + min).toFixed(0);
}
and then call it like this:
num = rand(0, chamarr.length);
That will give you a value in the right range for both indexing and splicing.
EDIT: In response to comments etc:
It's probably worth removing the toFixed(0) part of the rand function; you don't really want a string, after all. This isn't really part of what was wrong before, but it's generally cleaner:
function rand(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
You might also want a version of the function that makes the 0 lower bound implicit
If you're not going to use random numbers anywhere else in your code you could inline the Math.floor() / Math.random() calls instead of having a separate function, but personally I'd want to keep them well away from the "logic" code which just wants to get a random number and use it.
The reason I'd change the function is that having an exclusive upper bound is much more common in computer science - it typically goes along with 0-indexing for things like collections. You typically write for loops with inclusive lower bounds and exclusive lower bounds, etc.
The problem is that num is an index out of range. You should do this:
num = rand(0, chamarr.length - 1);
You can simplify your logic:
function rand(max) {
return Math.round( Math.random() * max ) % max;
}
var arr = [1, 2, 3, 4],
len = arr.length,
num = rand(len);
if ( len === 1 ) {
// Do your "Nothing here" output
}
else {
arr.splice(num, 1);
// etc, etc, etc...
}

Generating unique random numbers (integers) between 0 and 'x'

I need to generate a set of unique (no duplicate) integers, and between 0 and a given number.
That is:
var limit = 10;
var amount = 3;
How can I use Javascript to generate 3 unique numbers between 1 and 10?
Use the basic Math methods:
Math.random() returns a random number between 0 and 1 (including 0, excluding 1).
Multiply this number by the highest desired number (e.g. 10)
Round this number downward to its nearest integer
Math.floor(Math.random()*10) + 1
Example:
//Example, including customisable intervals [lower_bound, upper_bound)
var limit = 10,
amount = 3,
lower_bound = 1,
upper_bound = 10,
unique_random_numbers = [];
if (amount > limit) limit = amount; //Infinite loop if you want more unique
//Natural numbers than exist in a
// given range
while (unique_random_numbers.length < limit) {
var random_number = Math.floor(Math.random()*(upper_bound - lower_bound) + lower_bound);
if (unique_random_numbers.indexOf(random_number) == -1) {
// Yay! new random number
unique_random_numbers.push( random_number );
}
}
// unique_random_numbers is an array containing 3 unique numbers in the given range
Math.floor(Math.random() * (limit+1))
Math.random() generates a floating point number between 0 and 1, Math.floor() rounds it down to an integer.
By multiplying it by a number, you effectively make the range 0..number-1. If you wish to generate it in range from num1 to num2, do:
Math.floor(Math.random() * (num2-num1 + 1) + num1)
To generate more numbers, just use a for loop and put results into an array or write them into the document directly.
function generateRange(pCount, pMin, pMax) {
min = pMin < pMax ? pMin : pMax;
max = pMax > pMin ? pMax : pMin;
var resultArr = [], randNumber;
while ( pCount > 0) {
randNumber = Math.round(min + Math.random() * (max - min));
if (resultArr.indexOf(randNumber) == -1) {
resultArr.push(randNumber);
pCount--;
}
}
return resultArr;
}
Depending on range needed the method of returning the integer can be changed to: ceil (a,b], round [a,b], floor [a,b), for (a,b) is matter of adding 1 to min with floor.
Math.floor(Math.random()*limit)+1
for(i = 0;i <amount; i++)
{
var randomnumber=Math.floor(Math.random()*limit)+1
document.write(randomnumber)
}
Here’s another algorithm for ensuring the numbers are unique:
generate an array of all the numbers from 0 to x
shuffle the array so the elements are in random order
pick the first n
Compared to the method of generating random numbers until you get a unique one, this method uses more memory, but it has a more stable running time – the results are guaranteed to be found in finite time. This method works better if the upper limit is relatively low or if the amount to take is relatively high.
My answer uses the Lodash library for simplicity, but you could also implement the algorithm described above without that library.
// assuming _ is the Lodash library
// generates `amount` numbers from 0 to `upperLimit` inclusive
function uniqueRandomInts(upperLimit, amount) {
var possibleNumbers = _.range(upperLimit + 1);
var shuffled = _.shuffle(possibleNumbers);
return shuffled.slice(0, amount);
}
Something like this
var limit = 10;
var amount = 3;
var nums = new Array();
for(int i = 0; i < amount; i++)
{
var add = true;
var n = Math.round(Math.random()*limit + 1;
for(int j = 0; j < limit.length; j++)
{
if(nums[j] == n)
{
add = false;
}
}
if(add)
{
nums.push(n)
}
else
{
i--;
}
}
var randomNums = function(amount, limit) {
var result = [],
memo = {};
while(result.length < amount) {
var num = Math.floor((Math.random() * limit) + 1);
if(!memo[num]) { memo[num] = num; result.push(num); };
}
return result; }
This seems to work, and its constant lookup for duplicates.
These answers either don't give unique values, or are so long (one even adding an external library to do such a simple task).
1. generate a random number.
2. if we have this random already then goto 1, else keep it.
3. if we don't have desired quantity of randoms, then goto 1.
function uniqueRandoms(qty, min, max){
var rnd, arr=[];
do { do { rnd=Math.floor(Math.random()*max)+min }
while(arr.includes(rnd))
arr.push(rnd);
} while(arr.length<qty)
return arr;
}
//generate 5 unique numbers between 1 and 10
console.log( uniqueRandoms(5, 1, 10) );
...and a compressed version of the same function:
function uniqueRandoms(qty,min,max){var a=[];do{do{r=Math.floor(Math.random()*max)+min}while(a.includes(r));a.push(r)}while(a.length<qty);return a}
/**
* Generates an array with numbers between
* min and max randomly positioned.
*/
function genArr(min, max, numOfSwaps){
var size = (max-min) + 1;
numOfSwaps = numOfSwaps || size;
var arr = Array.apply(null, Array(size));
for(var i = 0, j = min; i < size & j <= max; i++, j++) {
arr[i] = j;
}
for(var i = 0; i < numOfSwaps; i++) {
var idx1 = Math.round(Math.random() * (size - 1));
var idx2 = Math.round(Math.random() * (size - 1));
var temp = arr[idx1];
arr[idx1] = arr[idx2];
arr[idx2] = temp;
}
return arr;
}
/* generating the array and using it to get 3 uniques numbers */
var arr = genArr(1, 10);
for(var i = 0; i < 3; i++) {
console.log(arr.pop());
}
I think, this is the most human approach (with using break from while loop), I explained it's mechanism in comments.
function generateRandomUniqueNumbersArray (limit) {
//we need to store these numbers somewhere
const array = new Array();
//how many times we added a valid number (for if statement later)
let counter = 0;
//we will be generating random numbers until we are satisfied
while (true) {
//create that number
const newRandomNumber = Math.floor(Math.random() * limit);
//if we do not have this number in our array, we will add it
if (!array.includes(newRandomNumber)) {
array.push(newRandomNumber);
counter++;
}
//if we have enought of numbers, we do not need to generate them anymore
if (counter >= limit) {
break;
}
}
//now hand over this stuff
return array;
}
You can of course add different limit (your amount) to the last 'if' statement, if you need less numbers, but be sure, that it is less or equal to the limit of numbers itself - otherwise it will be infinite loop.
Just as another possible solution based on ES6 Set ("arr. that can contain unique values only").
Examples of usage:
// Get 4 unique rnd. numbers: from 0 until 4 (inclusive):
getUniqueNumbersInRange(4, 0, 5) //-> [5, 0, 4, 1];
// Get 2 unique rnd. numbers: from -1 until 2 (inclusive):
getUniqueNumbersInRange(2, -1, 2) //-> [1, -1];
// Get 0 unique rnd. numbers (empty result): from -1 until 2 (inclusive):
getUniqueNumbersInRange(0, -1, 2) //-> [];
// Get 7 unique rnd. numbers: from 1 until 7 (inclusive):
getUniqueNumbersInRange(7, 1, 7) //-> [ 3, 1, 6, 2, 7, 5, 4];
The implementation:
function getUniqueNumbersInRange(uniqueNumbersCount, fromInclusive, untilInclusive) {
// 0/3. Check inputs.
if (0 > uniqueNumbersCount) throw new Error('The number of unique numbers cannot be negative.');
if (fromInclusive > untilInclusive) throw new Error('"From" bound "' + fromInclusive
+ '" cannot be greater than "until" bound "' + untilInclusive + '".');
const rangeLength = untilInclusive - fromInclusive + 1;
if (uniqueNumbersCount > rangeLength) throw new Error('The length of the range is ' + rangeLength + '=['
+ fromInclusive + '…' + untilInclusive + '] that is smaller than '
+ uniqueNumbersCount + ' (specified count of result numbers).');
if (uniqueNumbersCount === 0) return [];
// 1/3. Create a new "Set" – object that stores unique values of any type, whether primitive values or object references.
// MDN - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
// Support: Google Chrome 38+(2014.10), Firefox 13+, IE 11+
const uniqueDigits = new Set();
// 2/3. Fill with random numbers.
while (uniqueNumbersCount > uniqueDigits.size) {
// Generate and add an random integer in specified range.
const nextRngNmb = Math.floor(Math.random() * rangeLength) + fromInclusive;
uniqueDigits.add(nextRngNmb);
}
// 3/3. Convert "Set" with unique numbers into an array with "Array.from()".
// MDN – https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
// Support: Google Chrome 45+ (2015.09+), Firefox 32+, not IE
const resArray = Array.from(uniqueDigits);
return resArray;
}
The benefits of the current implementation:
Have a basic check of input arguments – you will not get an unexpected output when the range is too small, etc.
Support the negative range (not only from 0), e. g. randoms from -1000 to 500, etc.
Expected behavior: the current most popular answer will extend the range (upper bound) on its own if input bounds are too small. An example: get 10000 unique numbers with a specified range from 0 until 10 need to throw an error due to too small range (10-0+1=11 possible unique numbers only). But the current top answer will hiddenly extend the range until 10000.
I wrote this C# code a few years back, derived from a Wikipedia-documented algorithm, which I forget now (feel free to comment...). Uniqueness is guaranteed for the lifetime of the HashSet. Obviously, if you will be using a database, you could store the generated numbers there. Randomness was ok for my needs, but probably can be improved using a different RNG. Note: count must be <= max - min (duh!) and you can easily modify to generate ulongs.
private static readonly Random RndGen = new Random();
public static IEnumerable<int> UniqueRandomIntegers(int count, int min, int max)
{
var rv = new HashSet<int>();
for (var i = max - min - count + 1; i <= max - min; i++)
{
var r = (int)(RndGen.NextDouble() * i);
var v = rv.Contains(r) ? i : r;
rv.Add(v);
yield return v;
}
}
Randomized Array, Sliced
Similar to #rory-okane's answer, but without lodash.
Both Time Complexity and Space Complexity = O(n) where n=limit
Has a consistent runtime
Supports a positive or negative range of numbers
Theoretically, this should support a range from 0 to ±2^32 - 1
This limit is due to Javascript arrays only supporting 2^32 - 1 indexes as per the ECMAScript specification
I stopped testing it at 10^8 because my browser got weird around here and strangely only negative numbers to -10^7 - I got an Uncaught RangeError: Invalid array length error (shrug)
Bonus feature: Generate a randomized array of n length 0 to limit if you pass only one argument
let uniqueRandomNumbers = (limit, amount = limit) => {
let array = Array(Math.abs(limit));
for (let i = 0; i < array.length; i++) array[i] = i * Math.sign(limit);
let currentIndex = array.length;
let randomIndex;
while(currentIndex > 0) {
randomIndex = Math.floor(Math.random() * currentIndex--);
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
}
return array.slice(0, Math.abs(amount));
}
console.log(uniqueRandomNumbers(10, 3));
console.log(uniqueRandomNumbers(-10, 3));
//bonus feature:
console.log(uniqueRandomNumbers(10));
Credit:
I personally got here because I was trying to generate random arrays of n length. Other SO questions that helped me arrive at this answer for my own use case are below. Thank you everyone for your contributions, you made my life better today.
Most efficient way to create a zero filled JavaScript array?
How to randomize (shuffle) a JavaScript array?
Also the answer from #ashleedawg is where I started, but when I discovered the infinite loop issues I ended up at the sliced randomized array approach.
const getRandomNo = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
This function returns a random integer between the specified values. The value is no lower than min (or the next integer greater than min if min isn't an integer) and is less than (but not equal to) max.
Example
console.log(`Random no between 0 and 10 ${getRandomNo(0,10)}`)
Here's a simple, one-line solution:
var limit = 10;
var amount = 3;
randoSequence(1, limit).slice(0, amount);
It uses randojs.com to generate a randomly shuffled array of integers from 1 through 10 and then cuts off everything after the third integer. If you want to use this answer, toss this within the head tag of your HTML document:
<script src="https://randojs.com/1.0.0.js"></script>

Categories

Resources