简体   繁体   中英

Generate a non-repeating random number in JavaScript

How do I ensure that I don't get a repeat of a random number? Right now, this isn't working. I'm using a local array to store previous results.

getUniqueRandomNumber(x){
     var index;
     var viewedIndices = [];
     index = Math.floor(Math.random() * (x));
     if(viewedIndices.includes(index)) 
     {
       viewedIndices.push(index);
       this.getUniqueRandomNumber(x);
     }
     else { 
       console.log(index);
       return index;
    }
 }

You need to make viewedIndicies persistent , so that further calls of getUniqueRandomNumber can see elements previously added. Rather than keeping track of the indicies, it would probably be easier to keep track of just the plain numbers chosen. You can use a Set instead of an array for less computational complexity ( .has is O(1) , .includes is O(N) ).

 const makeGetUniqueRandomNumber = (x) => { const chosenNumbers = new Set(); return () => { if (chosenNumbers.size === x) { throw new Error('No more uniques!'); } let num; do { num = Math.floor(Math.random() * x); } while (chosenNumbers.has(num)); chosenNumbers.add(num); return num; }; }; const getRand5 = makeGetUniqueRandomNumber(5); console.log( getRand5(), getRand5(), getRand5(), getRand5(), getRand5() ); try { getRand5(); } catch(e) { console.log(e.message); } const anotherGetRand5 = makeGetUniqueRandomNumber(5); console.log( anotherGetRand5(), anotherGetRand5(), anotherGetRand5(), anotherGetRand5(), anotherGetRand5() ); 

You may also generate the whole array of random numbers ahead of time, and then splice each time another is chosen, but that'll be inefficient when the number of possibilities is large but you only need a few random numbers. The right choice depends on the proportion of unique numbers needed in one session to the size of the random range.

If developing in an ancient environment which doesn't understand ES6 (ES2015) syntax, then you can use an array instead of a Set, and pass the code through Babel:

 "use strict"; var makeGetUniqueRandomNumber = function makeGetUniqueRandomNumber(x) { var chosenNumbers = []; return function () { if (chosenNumbers.length === x) { throw new Error('No more uniques!'); } var num; do { num = Math.floor(Math.random() * x); } while (chosenNumbers.includes(num)); chosenNumbers.push(num); return num; }; }; var getRand5 = makeGetUniqueRandomNumber(5); console.log(getRand5(), getRand5(), getRand5(), getRand5(), getRand5()); try { getRand5(); } catch (e) { console.log(e.message); } var anotherGetRand5 = makeGetUniqueRandomNumber(5); console.log(anotherGetRand5(), anotherGetRand5(), anotherGetRand5(), anotherGetRand5(), anotherGetRand5()); 

You have 2 mistakes, oné is the array inside the function this cleared for each try, and then there is wrong logic ending up in an infinite loop.

const usedIndexes = [];    
function getUniqueRandomNumber(x) {
  const index = Math.floor(Math.random() * (x));
  if (usedIndexes.includes(index)) {
    return this.getUniqueRandomNumber(x);
  } else { 
    console.log(index);
    usedIndexes.push(index);
    return index;
  }
}

Also, I would think about using Set , in this situation instead of the array.

const usedIndexes = new Set();    
function getUniqueRandomNumber(max, min = 0) {
  const newNumber = Math.floor(Math.random() * (max - min) + min);
  if (usedIndexes.has(newNumber)) {
    return this.getUniqueRandomNumber(max, min);
  } else { 
    usedIndexes.add(newNumber);
    return newNumber;
  }
}

I have also edited variables names to better reflect their actual use and added a minimum for a random number.

This is not working because every time you call getUniqueRandomNumber it re-initializes your viewedIndices array to empty array. So to make your code work declare this array above the function call.

Do you just want the code you wrote to work or do you want a better solution? Picking random numbers until you don't get a repeat is a recipe for disaster down the line as your program stalls for several seconds trying to find a number that hasn't been used. Sure if you're only asking for a few numbers maybe it won't take forever but then the code sits in your code base and 5 years from now someone else is using it not knowing there is a time bomb in the code. Imagine there are 10000 elements in the array and 9999 have been picked. It could easily take 1 million re-tries before it ends up picking the one unused index.

The code appears to be choosing indices with variable names like index and viewedIndices

One way to pick random elements is just just remove then from the array at random. If you need to make copy of the array

 const array = ["a", "b", "c", "d", "e", "f", "g"]; while (array.length) { const ndx = Math.random() * array.length | 0; const elem = array.splice(ndx, 1)[0]; console.log(elem); } 

Note: using Math.random() * value | 0 Math.random() * value | 0 to get a random 0 -> positive integer is faster than Math.floor(Math.random() * value) as | is an operator, not a function attached to the Math object that has to be checked on every call to see if it has been replaced.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM