Generating non-repeating random numbers in JS

I have the following function

function randomNum(max, used){ newNum = Math.floor(Math.random() * max + 1); if($.inArray(newNum, used) === -1){ console.log(newNum + " is not in array"); return newNum; }else{ return randomNum(max,used); } } 

Basically I create a random number from 1 to 10 and check if this number has already been created by adding it to the array and checking on it the new number created. I call it by adding it to a variable ..

 UPDATED: for(var i=0;i < 10;i++){ randNum = randomNum(10, usedNums); usedNums.push(randNum); //do something with ranNum } 

This works, but in Chrome I get the following error:

 Uncaught RangeError: Maximum call stack size exceeded 

I think this is because I call the function inside myself too many times. This means that my code is not suitable.

Can someone help me with the logic? What's the best way to make sure my numbers are not duplicate?

+13
javascript jquery
source share
9 answers

If I understand correctly, are you just looking for a permutation (i.e. numbers randomized without repeats) of numbers 1-10? Maybe try creating a randomized list of these numbers, once, at the beginning, and then just work your way through them?

This will compute a random permutation of numbers in nums :

 var nums = [1,2,3,4,5,6,7,8,9,10], ranNums = [], i = nums.length, j = 0; while (i--) { j = Math.floor(Math.random() * (i+1)); ranNums.push(nums[j]); nums.splice(j,1); } 

So, for example, if you were looking for random numbers from 1 to 20 that were even even, you could use:

 nums = [2,4,6,8,10,12,14,16,18,20]; 

Then just read ranNums to call random numbers.

This does not risk that he will increasingly look for unused numbers, as you found in your approach.

EDIT : after reading this and running the jsperf test, it seems like a much better way to do this is to switch the Fisher-Yates Shuffle:

 function shuffle(array) { var i = array.length, j = 0, temp; while (i--) { j = Math.floor(Math.random() * (i+1)); // swap randomly chosen element with current element temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]); 

In principle, this is more efficient, avoiding the use of “expensive” array operations.

BONUS EDIT . Another possibility is to use generators (assuming support ):

 function* shuffle(array) { var i = array.length; while (i--) { yield array.splice(Math.floor(Math.random() * (i+1)), 1)[0]; } } 

Then to use:

 var ranNums = shuffle([1,2,3,4,5,6,7,8,9,10]); ranNums.next().value; // first random number from array ranNums.next().value; // second random number from array ranNums.next().value; // etc. 

where ranNums.next().value will eventually be evaluated to undefined after you go through all the elements in the shuffled array.

In general, it will not be as effective as Fisher-Yates Shuffle, because you are still splice -in the array. But the difference is that you only do this work now when you need it, and don’t do it all in advance, so depending on your use case, it might be better.

+27
source share

The problem is that as you approach saturation, you begin to take more time and longer to generate a unique number "randomly". For example, in the above example, max is 10. After the array of numbers used contains 8 numbers, it can potentially take a long time to find the 9th and 10th. This is probably the place where the maximum call stack error is generated.

jsFiddle Demo showing iteration count being maxed

Iterating inside your recursion, you can see that a large amount of execution occurs when the array is fully saturated, but the function is called. In this case, the function should exit.

jsFiddle Demo with early break

 if( used.length >= max ) return undefined; 

And the last way to do iterative checks and infinite recursion would look like this: jsFiddle Demo

 function randomNum(max, used, calls){ if( calls == void 0 ) calls = 0; if( calls++ > 10000 ) return undefined; if( used.length >= max ) return undefined; var newNum = Math.floor(Math.random() * max + 1); if($.inArray(newNum, used) === -1){ return newNum; }else{ return randomNum(max,used,calls); } } 
+1
source share

This will do what you are looking for:

 let anArrayOfUniqueNumbers = []; let numberGenerator = function(arr) { if (arr.length >= 10) return; let newNumber = Math.floor(Math.random() * 10 + 1); if (arr.indexOf(newNumber) < 0) { arr.push(newNumber); } numberGenerator(arr); }; numberGenerator(anArrayOfUniqueNumbers); 

We have:

  • new array
  • function that takes an array as an argument
This function will be:
  • Check if the array it is running on has ten indexes already, and if not:
  • Random number generation from 1 to 10
  • If this random number is not already in the array, insert it into the array
  • Run again

Due to the security clause ( if (arr.length >= 10) return; ), the function will stop execution as soon as the parameters are executed.

+1
source share
 function randomNumbers(max) { function range(upTo) { var result = []; for(var i = 0; i < upTo; i++) result.push(i); return result; } function shuffle(o){ for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); return o; } var myArr = shuffle(range(max)); return function() { return myArr.shift(); }; } 

Built a little test, try this on jsfiddle :

 var randoms = randomNumbers(10), rand = randoms(), result = []; while(rand != null) { result.push(rand); rand = randoms(); } console.log(result); 

The function is randomly provided by dzone.com .

0
source share

Here's how I achieve it using underscore.js

Get n integers from min to max . Where n is the argument size .

 var randomNonRepeatingIntFromInterval = function(min, max, size) { var values = []; while (values.length < size) { values.push(Math.floor(Math.random() * ( max - min + 1) + min)); values = _.uniq(values); } return values; } 
0
source share
 <!DOCTYPE html> <html> <body> <h2>JavaScript Math.random()</h2> <p>Math.random() returns a random number between 0 (included) and 1 (excluded):</p> <p id="demo"></p> <script> var storeArray = [] function callRamdom(){ var randomNumber = Math.floor(Math.random() * 5); return randomNumber; } function randomStore(){ var localValue = callRamdom() var status = false; for(i=0;i<5; i++){ var aa = storeArray[i]; if(aa!=localValue){ console.log(storeArray[i]+"hhhhh"+ localValue); if(i==4){ status=true; } } else break; } if(status==true){ storeArray.push(localValue); } if(storeArray.length!=5){ randomStore(); } return storeArray; } document.getElementById("demo").innerHTML = randomStore(); </script> </body> </html> 
0
source share

Sorry, this is a new answer to an old question, but this can be done more efficiently with a map. What you need is a random choice, not a random case. A non-repeating random event is pointless.

Where _a is the collection, and r is not part of the collection, we get a random value of r:

 function aRandom(f){ var r = Math.random(); aRandom._a[r] ? aRandom(f) : f(r,aRandom._a[r] = 1); } aRandom._a = {}; //usage: aRandom(function(r){ console.log(r) }); 

Override aRandom._a when the browser becomes sluggish. To avoid possible sluggishness, you really need to use a UUID generation algorithm with sufficient entropy so that the chances of repetition are actually zero, and not rough, crowding out differentiability. I chose the function name aRandom, because the Latin prefix A means "from here." Since the more it is used, the farther from a random exit. The function generates a million unique values ​​of 2100 ms on a Macbook.

The advantage of the above solution is not to limit the set. In addition, several subscribers can use it simultaneously and assume that their values ​​are different from all other subscribers. This is convenient for things like noise canceling with the insured without overlapping.

However, it can be modified to return integers to limit the use of the plunger to a specified length:

 function aRandom(f,c){ var r = Math.floor(Math.random()*c); aRandom._a[r] ? aRandom(f,c) : f(r,aRandom._a[r] = 1); } aRandom._a = {}; //usage: var len = 10; var resultset = []; for(var i =0; i< len; i++){ aRandom(function(r){ resultset.push(r); }, len); } console.log(resultset); 
0
source share

 let arr = []; do { let num = Math.floor(Math.random() * 10 + 1); arr.push(num); arr = arr.filter((item, index) => { return arr.indexOf(item) === index }); } while (arr.length < 10); console.log(arr); 

0
source share

You really do not want to lose random numbers. Truly random numbers should be able to repeat.

A truly random number is like throwing dice. Any number may appear further.

Shuffled numbers are like drawing playing cards. Each number can appear only once.

What you are really asking is to shuffle the list of numbers, and then use the first number of numbers from the shuffled list.

Think about how to make a list of numbers in order, and then use a random number generator to randomly select a number from a copy of this list. Each time, put the selected number at the end of the new list and remove it from the copy of the old list, reducing this list. When you are finished, the new list will contain the shuffled numbers, and a copy of the old list will be empty.

Alternatively, you can select a number and use it immediately, reducing the copy of the list by deleting the used number. Since you removed the number from the list, it cannot appear again.

-2
source share

All Articles