javascript - generate a new random number - javascript

I have a variable that has a number between 1-3.
I need to randomly generate a new number between 1-3 but it must not be the same as the last one.
It happens in a loop hundreds of times.
What is the most efficient way of doing this?

May the powers of modular arithmetic help you!!
This function does what you want using the modulo operator:
/**
* generate(1) will produce 2 or 3 with probablity .5
* generate(2) will produce 1 or 3 with probablity .5
* ... you get the idea.
*/
function generate(nb) {
rnd = Math.round(Math.random())
return 1 + (nb + rnd) % 3
}
if you want to avoid a function call, you can inline the code.

Here is a jsFiddle that solves your problem : http://jsfiddle.net/AsMWG/
I've created an array containing 1,2,3 and first I select any number and swap it with the last element. Then I only pick elements from position 0 and 1, and swap them with last element.

var x = 1; // or 2 or 3
// this generates a new x out of [1,2,3] which is != x
x = (Math.floor(2*Math.random())+x) % 3 + 1;

You can randomly generate numbers with the random number generator built in to javascript. You need to use Math.random().
If you're push()-ing into an array, you can always check if the previously inserted one is the same number, thus you regenerate the number. Here is an example:
var randomArr = [];
var count = 100;
var max = 3;
var min = 1;
while (randomArr.length < count) {
var r = Math.floor(Math.random() * (max - min) + min);
if (randomArr.length == 0) {
// start condition
randomArr.push(r);
} else if (randomArr[randomArr.length-1] !== r) {
// if the previous value is not the same
// then push that value into the array
randomArr.push(r);
}
}

As Widor commented generating such a number is equivalent to generating a number with probability 0.5. So you can try something like this (not tested):
var x; /* your starting number: 1,2 or 3 */
var y = Math.round(Math.random()); /* generates 0 or 1 */
var i = 0;
var res = i+1;
while (i < y) {
res = i+1;
i++;
if (i+1 == x) i++;
}

The code is tested and it does for what you are after.
var RandomNumber = {
lastSelected: 0,
generate: function() {
var random = Math.floor(Math.random()*3)+1;
if(random == this.lastSelected) {
generateNumber();
}
else {
this.lastSelected = random;
return random;
}
}
}
RandomNumber.generate();

Related

Round to the next whole number javascript

I want to achieve something like this in JavaScript:
input = 2455.55
f(input) = 2456
f(input) = 2460
f(input) = 2500
f(input) = 3000
f(input) = 2455.55
I am using the Math.round() method for now but only get to 2,546 with it. Wondering if there is a best way to achieve the rest.
You can divide your number by ten until you get a non-integer, round it up and then multiply by ten again the same amount of time. Something like this:
function roundUp(n) {
var n2 = n;
var i=0;
while (Number.isInteger(n2)) {
n2 /= 10;
i++;
}
return Math.round(n2) * Math.pow(10, i);
}
console.log(roundUp(2455.55)); // 2456
console.log(roundUp(2456)); // 2460
console.log(roundUp(2460)); // 2500
console.log(roundUp(2500)); // 3000
Based on your desired output it looks like you need to track the number of function calls. This doesn't seem to be an argument to your function.
Given the constraint that you have only a single argument, the implementation looks probably like
var lastNum = 0
var digitsToRound = 0
function roundUp(input) {
// Verify whether the function is called with the same argument as last call.
// Note that we cannot compare floating point numbers.
// See https://dev.to/alldanielscott/how-to-compare-numbers-correctly-in-javascript-1l4i
if (Math.abs(input - lastNum) < Number.EPSILON) {
// If the number of digitsToRound exceeds the number of digits in the input we want
// to reset the number of digitsToRound. Otherwise we increase the digitsToRound.
if (digitsToRound > (Math.log10(input) - 1)) {
digitsToRound = 0;
} else {
digitsToRound = digitsToRound + 1;
}
} else {
// The function was called with a new input, we reset the digitsToRound
digitsToRound = 0;
lastNum = input;
}
// Compute the factor by which we need to divide and multiply to round the input
// as desired.
var factor = Math.max(1, Math.pow(10, digitsToRound));
return Math.ceil(input / factor) * factor;
}
console.log(roundUp(2455.55)); // 2456
console.log(roundUp(2455.55)); // 2460
console.log(roundUp(2455.55)); // 2500
console.log(roundUp(2455.55)); // 3000
thanks, nice one! Inspired by your answer, I solved it like so:
function roundNumber(num, n) {
const divider = Math.pow(10, n);
return Math.round(num / divider) * divider;
};

Random number for given counts

I am generating 4 digit random number by using Math.floor(Math.random() * 9999). Now I have another requirement. I have to get the number of random number to be generated in textbox. eg: If they enter 5 in textbox it has to return 5 four-digit random number. Any idea how to do it? any reference?
Simply call the method a couple of times, depending on input. Note that you need to use below random number creation method instead of yours to guarantee 4 digit numbers.
function getRand() {
return Math.floor(Math.random() * (9999 - 1000) + 1000);
}
document.getElementById('btn').addEventListener('click', () => {
const length = document.getElementById('foo').value;
const numbers = Array.from({length}, getRand);
document.getElementById('bar').innerText = numbers.join(', ');
});
<input id="foo" type="number">
<button id="btn">Get</button>
<div id="bar"></div>
You can simply call your 4 digit random number generator function() n time (n is given number in input field) as below:
for(let i=1;i<=this.n;i++) {
this.ara.push(this.random());
}
random() {
return Math.floor(Math.random()*(9999-1000) + 1000);
}
See this typescript implementation (Angular).
Call getRandoms with n = 5, and high = 9999. Handle the 5-element return array as you wish.
// pseudo-randomly generate an integer in the range low to high
function getRandom( high, low ) {
// default low is 0
if ('undefined' == typeof low) low = 0;
var range = high - low + 1;
var r = Math.floor( range*Math.random() + .5);
return Math.min(low + r, high);
};
// get n pseudo-random number in the range low to high
function getRandoms( n, high, low ) {
// default low is 0
if ('undefined' == typeof low) low = 0;
var randoms = new Array(); // initialize return
for (var i = 0; i < n; i++) {
randoms.push(getRandom(high, low));
}
return randoms;
};

Get a random number focused on center

Is it possible to get a random number between 1-100 and keep the results mainly within the 40-60 range? I mean, it will go out of that range rarely, but I want it to be mainly within that range... Is it possible with JavaScript/jQuery?
Right now I'm just using the basic Math.random() * 100 + 1.
The simplest way would be to generate two random numbers from 0-50 and add them together.
This gives a distribution biased towards 50, in the same way rolling two dice biases towards 7.
In fact, by using a larger number of "dice" (as #Falco suggests), you can make a closer approximation to a bell-curve:
function weightedRandom(max, numDice) {
let num = 0;
for (let i = 0; i < numDice; i++) {
num += Math.random() * (max/numDice);
}
return num;
}
JSFiddle: http://jsfiddle.net/797qhcza/1/
You have some good answers here that give specific solutions; let me describe for you the general solution. The problem is:
I have a source of more-or-less uniformly distributed random numbers between 0 and 1.
I wish to produce a sequence of random numbers that follow a different distribution.
The general solution to this problem is to work out the quantile function of your desired distribution, and then apply the quantile function to the output of your uniform source.
The quantile function is the inverse of the integral of your desired distribution function. The distribution function is the function where the area under a portion of the curve is equal to the probability that the randomly-chosen item will be in that portion.
I give an example of how to do so here:
http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/
The code in there is in C#, but the principles apply to any language; it should be straightforward to adapt the solution to JavaScript.
Taking arrays of numbers, etc. isn't efficient. You should take a mapping which takes a random number between 0 to 100 and maps to the distribution you need. So in your case, you could take f(x)=-(1/25)x2+4x to get a distribution with the most values in the middle of your range.
I might do something like setup a "chance" for the number to be allowed to go "out of bounds". In this example, a 20% chance the number will be 1-100, otherwise, 40-60:
$(function () {
$('button').click(function () {
var outOfBoundsChance = .2;
var num = 0;
if (Math.random() <= outOfBoundsChance) {
num = getRandomInt(1, 100);
} else {
num = getRandomInt(40, 60);
}
$('#out').text(num);
});
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Generate</button>
<div id="out"></div>
fiddle: http://jsfiddle.net/kbv39s9w/
I needed to solve this problem a few years ago and my solution was easier than any of the other answers.
I generated 3 randoms between the bounds and averaged them. This pulls the result towards the centre but leaves it completely possible to reach the extremities.
It looks stupid but you can use rand twice:
var choice = Math.random() * 3;
var result;
if (choice < 2){
result = Math.random() * 20 + 40; //you have 2/3 chance to go there
}
else {
result = Math.random() * 100 + 1;
}
Sure it is possible. Make a random 1-100. If the number is <30 then generate number in range 1-100 if not generate in range 40-60.
There is a lot of different ways to generate such random numbers. One way to do it is to compute the sum of multiple uniformly random numbers. How many random numbers you sum and what their range is will determine how the final distribution will look.
The more numbers you sum up, the more it will be biased towards the center. Using the sum of 1 random number was already proposed in your question, but as you notice is not biased towards the center of the range. Other answers have propose using the sum of 2 random numbers or the sum of 3 random numbers.
You can get even more bias towards the center of the range by taking the sum of more random numbers. At the extreme you could take the sum of 99 random numbers which each were either 0 or 1. That would be a binomial distribution. (Binomial distributions can in some sense be seen as the discrete version of normal distributions). This can still in theory cover the full range, but it has so much bias towards the center that you should never expect to see it reach the endpoints.
This approach means you can tweak just how much bias you want.
What about using something like this:
var loops = 10;
var tries = 10;
var div = $("#results").html(random());
function random() {
var values = "";
for(var i=0; i < loops; i++) {
var numTries = tries;
do {
var num = Math.floor((Math.random() * 100) + 1);
numTries--;
}
while((num < 40 || num >60) && numTries > 1)
values += num + "<br/>";
}
return values;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
The way I've coded it allows you to set a couple of variables:
loops = number of results
tries = number of times the function will try to get a number between 40-60 before it stops running through the while loop
Added bonus: It uses do while!!! Awesomeness at its best
You can write a function that maps random values between [0, 1) to [1, 100] according to weight. Consider this example:
Here, the value 0.95 maps to value between [61, 100].
In fact we have .05 / .1 = 0.5, which, when mapped to [61, 100], yields 81.
Here is the function:
/*
* Function that returns a function that maps random number to value according to map of probability
*/
function createDistributionFunction(data) {
// cache data + some pre-calculations
var cache = [];
var i;
for (i = 0; i < data.length; i++) {
cache[i] = {};
cache[i].valueMin = data[i].values[0];
cache[i].valueMax = data[i].values[1];
cache[i].rangeMin = i === 0 ? 0 : cache[i - 1].rangeMax;
cache[i].rangeMax = cache[i].rangeMin + data[i].weight;
}
return function(random) {
var value;
for (i = 0; i < cache.length; i++) {
// this maps random number to the bracket and the value inside that bracket
if (cache[i].rangeMin <= random && random < cache[i].rangeMax) {
value = (random - cache[i].rangeMin) / (cache[i].rangeMax - cache[i].rangeMin);
value *= cache[i].valueMax - cache[i].valueMin + 1;
value += cache[i].valueMin;
return Math.floor(value);
}
}
};
}
/*
* Example usage
*/
var distributionFunction = createDistributionFunction([
{ weight: 0.1, values: [1, 40] },
{ weight: 0.8, values: [41, 60] },
{ weight: 0.1, values: [61, 100] }
]);
/*
* Test the example and draw results using Google charts API
*/
function testAndDrawResult() {
var counts = [];
var i;
var value;
// run the function in a loop and count the number of occurrences of each value
for (i = 0; i < 10000; i++) {
value = distributionFunction(Math.random());
counts[value] = (counts[value] || 0) + 1;
}
// convert results to datatable and display
var data = new google.visualization.DataTable();
data.addColumn("number", "Value");
data.addColumn("number", "Count");
for (value = 0; value < counts.length; value++) {
if (counts[value] !== undefined) {
data.addRow([value, counts[value]]);
}
}
var chart = new google.visualization.ColumnChart(document.getElementById("chart"));
chart.draw(data);
}
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(testAndDrawResult);
<script src="https://www.google.com/jsapi"></script>
<div id="chart"></div>
Here's a weighted solution at 3/4 40-60 and 1/4 outside that range.
function weighted() {
var w = 4;
// number 1 to w
var r = Math.floor(Math.random() * w) + 1;
if (r === 1) { // 1/w goes to outside 40-60
var n = Math.floor(Math.random() * 80) + 1;
if (n >= 40 && n <= 60) n += 40;
return n
}
// w-1/w goes to 40-60 range.
return Math.floor(Math.random() * 21) + 40;
}
function test() {
var counts = [];
for (var i = 0; i < 2000; i++) {
var n = weighted();
if (!counts[n]) counts[n] = 0;
counts[n] ++;
}
var output = document.getElementById('output');
var o = "";
for (var i = 1; i <= 100; i++) {
o += i + " - " + (counts[i] | 0) + "\n";
}
output.innerHTML = o;
}
test();
<pre id="output"></pre>
Ok, so I decided to add another answer because I felt like my last answer, as well as most answers here, use some sort of half-statistical way of obtaining a bell-curve type result return. The code I provide below works the same way as when you roll a dice. Therefore, it is hardest to get 1 or 99, but easiest to get 50.
var loops = 10; //Number of numbers generated
var min = 1,
max = 50;
var div = $("#results").html(random());
function random() {
var values = "";
for (var i = 0; i < loops; i++) {
var one = generate();
var two = generate();
var ans = one + two - 1;
var num = values += ans + "<br/>";
}
return values;
}
function generate() {
return Math.floor((Math.random() * (max - min + 1)) + min);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
I'd recommend using the beta distribution to generate a number between 0-1, then scale it up. It's quite flexible and can create many different shapes of distributions.
Here's a quick and dirty sampler:
rbeta = function(alpha, beta) {
var a = 0
for(var i = 0; i < alpha; i++)
a -= Math.log(Math.random())
var b = 0
for(var i = 0; i < beta; i++)
b -= Math.log(Math.random())
return Math.ceil(100 * a / (a+b))
}
var randNum;
// generate random number from 1-5
var freq = Math.floor(Math.random() * (6 - 1) + 1);
// focus on 40-60 if the number is odd (1,3, or 5)
// this should happen %60 of the time
if (freq % 2){
randNum = Math.floor(Math.random() * (60 - 40) + 40);
}
else {
randNum = Math.floor(Math.random() * (100 - 1) + 1);
}
The best solution targeting this very problem is the one proposed by BlueRaja - Danny Pflughoeft but I think a somewhat faster and more general solution is also worth mentioning.
When I have to generate random numbers (strings, coordinate pairs, etc.) satisfying the two requirements of
The result set is quite small. (not larger than 16K numbers)
The result set is discreet. (like integer numbers only)
I usually start by creating an array of numbers (strings, coordinate pairs, etc.) fulfilling the requirement (In your case: an array of numbers containing the more probable ones multiple times.), then choose a random item of that array. This way, you only have to call the expensive random function once per item.
Distribution
5% for [ 0,39]
90% for [40,59]
5% for [60,99]
Solution
var f = Math.random();
if (f < 0.05) return random(0,39);
else if (f < 0.95) return random(40,59);
else return random(60,99);
Generic Solution
random_choose([series(0,39),series(40,59),series(60,99)],[0.05,0.90,0.05]);
function random_choose (collections,probabilities)
{
var acc = 0.00;
var r1 = Math.random();
var r2 = Math.random();
for (var i = 0; i < probabilities.length; i++)
{
acc += probabilities[i];
if (r1 < acc)
return collections[i][Math.floor(r2*collections[i].length)];
}
return (-1);
}
function series(min,max)
{
var i = min; var s = [];
while (s[s.length-1] < max) s[s.length]=i++;
return s;
}
You can use a helper random number to whether generate random numbers in 40-60 or 1-100:
// 90% of random numbers should be between 40 to 60.
var weight_percentage = 90;
var focuse_on_center = ( (Math.random() * 100) < weight_percentage );
if(focuse_on_center)
{
// generate a random number within the 40-60 range.
alert (40 + Math.random() * 20 + 1);
}
else
{
// generate a random number within the 1-100 range.
alert (Math.random() * 100 + 1);
}
If you can use the gaussian function, use it. This function returns normal number with average 0 and sigma 1.
95% of this number are within average +/- 2*sigma. Your average = 50, and sigma = 5 so
randomNumber = 50 + 5*gaussian()
The best way to do that is generating a random number that is distributed equally in a certain set of numbers, and then apply a projection function to the set between 0 and a 100 where the projection is more likely to hit the numbers you want.
Typically the mathematical way of achieving this is plotting a probability function of the numbers you want. We could use the bell curve, but let's for the sake of easier calculation just work with a flipped parabola.
Let's make a parabola such that its roots are at 0 and 100 without skewing it. We get the following equation:
f(x) = -(x-0)(x-100) = -x * (x-100) = -x^2 + 100x
Now, all the area under the curve between 0 and 100 is representative of our first set where we want the numbers generated. There, the generation is completely random. So, all we need to do is find the bounds of our first set.
The lower bound is, of course, 0. The upper bound is the integral of our function at 100, which is
F(x) = -x^3/3 + 50x^2
F(100) = 500,000/3 = 166,666.66666 (let's just use 166,666, because rounding up would make the target out of bounds)
So we know that we need to generate a number somewhere between 0 and 166,666. Then, we simply need to take that number and project it to our second set, which is between 0 and 100.
We know that the random number we generated is some integral of our parabola with an input x between 0 and 100. That means that we simply have to assume that the random number is the result of F(x), and solve for x.
In this case, F(x) is a cubic equation, and in the form F(x) = ax^3 + bx^2 + cx + d = 0, the following statements are true:
a = -1/3
b = 50
c = 0
d = -1 * (your random number)
Solving this for x yields you the actual random number your are looking for, which is guaranteed to be in the [0, 100] range and a much higher likelihood to be close to the center than the edges.
This answer is really good. But I would like to post implementation instructions (I'm not into JavaScript, so I hope you will understand) for different situation.
Assume you have ranges and weights for every range:
ranges - [1, 20], [21, 40], [41, 60], [61, 100]
weights - {1, 2, 100, 5}
Initial Static Information, could be cached:
Sum of all weights (108 in sample)
Range selection boundaries. It basically this formula: Boundary[n] = Boundary[n - 1] + weigh[n - 1] and Boundary[0] = 0. Sample has Boundary = {0, 1, 3, 103, 108}
Number generation:
Generate random number N from range [0, Sum of all weights).
for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
Take ith range and generate random number in that range.
Additional note for performance optimizations. Ranges don't have to be ordered neither ascending nor descending order, so for faster range look-up range that has highest weight should go first and one with lowest weight should go last.

Javascript shift() always returning 0

I have a deck of cards object that essentially just fills a deck array with 0-51 (52 cards) and then shuffles the cards. I have a cards.deal() method that returns a card. When I use cards.pop() I always get a different number, which is what is expected; however, when I used cards.shift() it always returns 0. I'm content with using pop but would just like to understand for learning purposes why I'm always getting a 0 when I use shift. I've changed the number of shuffles to huge numbers like 10,000 just to make sure it's shuffling enough to actually swap the first element in the array. Could it be possible that the Math.ceil method is not returning a 0, even after 10,000 random numbers generated between 0 and 51? What is going on here?
function Deck(){
var cards = [];
this.shuffle = function(){
var tempCardHolder = 0;
var randomNumber = 0;
var randomNumber2 = 0;
// fill the card array with 52 cards
for (x = 0; x <= 51; x++){
cards[x] = x;
}
// now let's shuffle them up by swapping two cards randomly 50 times
for (y = 0; y < 100; y++){
// make sure we get two random numbers that are different
do {
randomNumber = Math.ceil(Math.random() * 51);
randomNumber2 = Math.ceil(Math.random() * 51);
} while (randomNumber == randomNumber2);
tempCardHolder = cards[randomNumber];
cards[randomNumber] = cards[randomNumber2];
cards[randomNumber2] = tempCardHolder;
}
}
this.deal = function(){
return cards.shift();
}
this.getNumCardsLeft = function(){
return cards.length;
}
}
Use Math.floor to round your numbers. For inclusive ranges, make sure you multiply one larger than your desired maximum value
Here is an example of a random integer generator for inclusive end points
function random_int(max, min) {
min = min || 0;
return min + Math.floor(Math.random() * (max - min + 1));
}

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