4

This has been asked dozens of times, but somehow, after reading many answers, I'm not convinced. I'm not cleared about the best way to do it, performance and code simplicity.

  1. Should I set the list [1.. 100] and keep picking random (it will run 10 times) from there to another array, avoiding searching for it every new random?

  2. Should I develop and run 10 times (at least) a random function to return a 1.. 100, checking if it is not a dupe and put it into an array?

  3. Some Javascript function that I'm missing?

Thanks

Unmitigated
  • 76,500
  • 11
  • 62
  • 80
noneJavaScript
  • 835
  • 3
  • 21
  • 41

5 Answers5

4

You can use a while loop to generate random numbers with Math.random() and add the numbers to a Set which contains only unique values.

var randoms = new Set();
while(randoms.size<10){
  randoms.add(1 + Math.floor(Math.random() * 100));
}
console.log([...randoms.values()]);

You can also just use an Array and check if the generated random number already exists in it before pushing it to the Array.

var randoms = [];
while(randoms.length<10){
  var random = Math.ceil(1 + Math.floor(Math.random() * 100));
  if(randoms.indexOf(random)==-1){
    randoms.push(random);
  }
}
console.log(randoms);

For a more generic function, you can use this:

function generateRandoms(min, max, numOfRandoms, unique){
  /*min is the smallest possible generated number*/
  /*max is the largest possible generated number*/
  /*numOfRandoms is the number of random numbers to generate*/
  /*unique is a boolean specifying whether the generated random numbers need to be unique*/
    var getRandom = function(x, y){
      return Math.floor(Math.random() * (x - y + 1) + y);
    }
    var randoms = [];
    while(randoms.length<numOfRandoms){
      var random = getRandom(min, max);
      if(randoms.indexOf(random)==-1||!unique){
        randoms.push(random);
      }
    }
    return randoms;
}

function generateRandoms(min, max, numOfRandoms, unique){
    var getRandom = function(x, y){
      return Math.floor(Math.random() * (x - y + 1) + y);
    }
    var randoms = [];
    while(randoms.length<numOfRandoms){
      var random = getRandom(min, max);
      if(randoms.indexOf(random)==-1||!unique){
        randoms.push(random);
      }
    }
    return randoms;
}
console.log(generateRandoms(1, 100, 10, true));
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
  • The Set method is great, but the while loop one is already posted https://stackoverflow.com/questions/2380019/generate-unique-random-numbers-between-1-and-100 – Huangism Aug 17 '18 at 15:22
  • @Huangism, but not the Set method, which is exactly what I was looking for, clean, fast solution. Don't try to close this, StackO, Officer. – noneJavaScript Aug 17 '18 at 15:40
  • The close votes aren't mine, the other post could use an update as well – Huangism Aug 17 '18 at 15:44
  • 1
    Pedantic note: this should probably use `1 + Math.floor(Math.random() * 100)` as `Math.random` _can_ return 0, but never 1 – searlea Aug 17 '18 at 15:56
  • 1
    @searlea Thanks for telling me. I have edited my answer. – Unmitigated Aug 17 '18 at 15:57
3

This technique creates N1 numbers (the total range) and shuffles them, then picks the top N2 number (how many we actually want), we'll use Fisher-Yates shuffle.

const n1 = 100;
const n2 = 10;

let pool = [...Array(n1).keys()];

var result = [];

while (result.length < n2) {
   let index = Math.floor(Math.random() * pool.length);
   result = result.concat(pool.splice(index, 1));       
}

console.log(result);
Terry Lennox
  • 29,471
  • 5
  • 28
  • 40
  • 2
    I prefer using a shuffle, but a real [Fisher-Yates shuffle](https://bost.ocks.org/mike/shuffle/) would be the correct way of doing it. – Pointy Aug 17 '18 at 15:20
  • 1
    You're right, I'm actually in the process of updating to use the Fisher-Yates :) – Terry Lennox Aug 17 '18 at 15:20
2

var randomArray = [];

while(randomArray.length < 10) {
  var random = Math.round(Math.random() * 100);
  if(randomArray.indexOf(random) === -1) {
    randomArray.push(random);
  }
 
}
console.log(randomArray);
Mark Baijens
  • 13,028
  • 11
  • 47
  • 73
0

#2 would be the most efficient.

var nums = []
while(nums.length < 10) {
  var n = Math.round(Math.random()*100);
  if (!nums.includes(n)) nums.push(n);
}
console.log(nums);

You could also use Set in a newer browser, which would be a little faster than manually checking for existence:

var nums = new Set();
while(nums.size < 10) {
  var n = Math.round(Math.random()*100);
  nums.add(n);
}
console.log([...nums.values()]);
Zach Bloomquist
  • 5,309
  • 29
  • 44
0

This function adds all numbers from betweenStart to betweenEnd, randomizes them over randomRuns loops and returns a list with amount entries:

function randomNumbersBetweenXAndY(betweenStart, betweenEnd, amount, randomRuns) {
    if (betweenStart === void 0) { betweenStart = 0; }
    if (betweenEnd === void 0) { betweenEnd = 100; }
    if (amount === void 0) { amount = 10; }
    if (randomRuns === void 0) { randomRuns = 1; }
    //Verify parameters
    var maxPossibleCandidates = Math.abs(betweenStart - betweenEnd) + 1;
    if (amount > maxPossibleCandidates) {
        console.warn("You cannot get more unique numbers between " + betweenStart + " and " + betweenStart + " than " + maxPossibleCandidates + ". " + amount + " is too many!");
        amount = maxPossibleCandidates;
    }
    //array to return
    var list = [];
    //fill array
    for (var index = betweenStart; index <= betweenEnd; index++) {
        list.push(index);
    }
    //Randomize
    while (randomRuns--) {
        for (var index = 0; index < list.length; index++) {
            var randomIndex = Math.floor(Math.random() * list.length);
            var tmp = list[index];
            list[index] = list[randomIndex];
            list[randomIndex] = tmp;
        }
    }
    //Return data
    return list.slice(0, amount);
}
//TEST
console.log(randomNumbersBetweenXAndY(1, 100, 10));
Emil S. Jørgensen
  • 6,216
  • 1
  • 15
  • 28