12

I'm generating random numbers from 1 to 20 by calling generateRandom(). How can I exclude some values, say 8 and 15?

function generateRandom(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

var test = generateRandom(1, 20)
StephenTG
  • 2,579
  • 6
  • 26
  • 36
DevEx
  • 4,337
  • 13
  • 46
  • 68

15 Answers15

17

it should be or instead of and

function generateRandom(min, max) {
    var num = Math.floor(Math.random() * (max - min + 1)) + min;
    return (num === 8 || num === 15) ? generateRandom(min, max) : num;
}

var test = generateRandom(1, 20)
Anas Jame
  • 222
  • 2
  • 8
  • 4
    I already try this solution and it will stop working in a browser when the recursion is too deep. The browser prevents the code to continue looping. Also, for mobile solutions (what I'm testing right now) is even a huge problem. I prefer the @Bathsheba recommendation or to subtract/add a number randomly. – Juan Saravia Feb 23 '15 at 15:31
  • function generateRandom(min, max,depth) { if(depth>10) { return 1; } var num = Math.floor(Math.random() * (max - min + 1)) + min; return (num === 8 || num === 15) ? generateRandom(min, max,depth+1) : num; } var test = generateRandom(1, 20,0) – Anas Jame Feb 25 '15 at 17:26
9

One way, which will maintain the generator's statistical properties, is to generate a number in [1, 18]. Then apply, in this order:

  1. If the number is 8 or more, add 1.

  2. If the number is 15 or more, add 1.

I'd be reluctant to reject and re-sample as that can cause correlation plains to appear in linear congruential generators.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 2
    This means 9 and 16 will be returned twice as often as other numbers. Not sure if OP cares though... – LCIII Jan 17 '16 at 04:24
  • 1
    No it doesn't. Note that I say "and more". Try it and see. – Bathsheba Jan 17 '16 at 18:07
  • It's unclear that you need to re-check the number after each step. For example if I got 14, then I need to check if it's >=8, then add 1, then check for the new number (15) if its >=15 and not for the original one (14). Other than that, this is an excellent way and can be generalized: `let rand = generateRandom(0, 100-optionsToSkip.length); for(let opt of optionsToSkip) { if(rand >= opt) rand+=1; }` – NonameSL Nov 09 '17 at 20:27
4

Right now I'm using this and it works without causing browser issues with infinities loops, also tested in mobile devices (using Ionic/Cordova):

function getRandomIndex(usedIndexs, maxIndex) {
    var result = 0;
    var min = 0;
    var max = maxIndex - 1;
    var index = Math.floor(Math.random()*(max-min+1)+min);

    while(usedIndexs.indexOf(index) > -1) {
        if (index < max) {
            index++;
        } else {
          index = 0;
        }
    }

    return index;
}
Juan Saravia
  • 7,661
  • 6
  • 29
  • 41
4

To generate random number between 1 and 20 excluding some given numbers, you can simply do this:

function generateRandom(min, max, exclude) {
  let random;
  while (!random) {
    const x = Math.floor(Math.random() * (max - min + 1)) + min;
    if (exclude.indexOf(x) === -1) random = x;
  }
  return random;
}
const test = generateRandom(1, 20, [8, 15]);
ziishaned
  • 4,944
  • 3
  • 25
  • 32
2
    /**
    * Returns a random integer between min (inclusive) and max (inclusive).
    * Pass all values as an array, as 3rd argument which values shouldn't be generated by the function.
    * 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) {
   const minimum = Math.ceil(min);
   const maximum = Math.floor(max);

   return Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
}


function getRandomIntExcludingExistingNumbers(min, max, excludeArrayNumbers) {
  let randomNumber;

  if(!Array.isArray(excludeArrayNumbers)) {
    randomNumber = getRandomInt(min, max);
    return randomNumber;
  }

  do {
    randomNumber = getRandomInt(min, max);
  } while ((excludeArrayNumbers || []).includes(randomNumber));

  return randomNumber;
}

const randomNumber = getRandomIntExcludingExistingNumbers(1, 10, [1, 2, 4, 5, 9]);

// It will return random integer between 1 to 10 excluding 1,2,4,5,9

Explanation:

getRandomInt function generates random numbers between min and max values.

I am utilizing that function to make "getRandomIntExcludingExistingNumbers" function to avoid specific values.

we will simply call getRandomInt(min, max) values. Then in do while loop we will check if randomly generated values belongs to any of the values which shouldn't be generated.

If it is unique integer outside exclude values then we will return the value.

If our value is from the excluded values, then from do -- while loop, we will once again call getRandomInt to generate new values.

Akash Jani
  • 21
  • 3
  • 1
    Please, don't post code without an explanation as an answer. Try to explain what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Zsolt Meszaros Feb 03 '21 at 08:32
  • Thanks for pointing out about explanation @zsolt-meszaros. It was my first answer so. – Akash Jani Feb 10 '21 at 06:20
1

Here is a slightly modified answer that is similar to all the others but it allows your to pass a single or an array of failing numbers

function generateRandom(min, max, failOn) {
    failOn = Array.isArray(failOn) ? failOn : [failOn]
    var num = Math.floor(Math.random() * (max - min + 1)) + min;
    return failOn.includes(num) ? generateRandom(min, max, failOn) : num;
}
James Harrington
  • 3,138
  • 30
  • 32
0

You could make use of a recursive function

function generateRandom(min, max, num1, num2) {
    var rtn = Math.floor(Math.random() * (max - min + 1)) + min;
    return rtn == num1 || rtn == num2 ? generateRandom(min, max, num1, num2) : rtn;
}
Amit Joki
  • 58,320
  • 7
  • 77
  • 95
0

I think it should be like this, if you want good distribution on all numbers. and, for this solution, it is required to higher max than 15 and lower min that 8

function generateRandom(min, max) {
    var v =  Math.floor(Math.random() * (max - min + 1 - 2)) + min;
    if (v == 8) return max-1;
    else if (v == 15) return max-2;
    else return v;
}

var test = generateRandom(1, 20)
Adem
  • 9,402
  • 9
  • 43
  • 58
  • 'else's are not required here ^^ – Hacketo Dec 10 '14 at 16:50
  • I dont understand. I should return value ? – Adem Dec 10 '14 at 16:52
  • 'return' break the actual scope of the function (they return value), so the else keyword is useless in this case ^^, my comment was just for information – Hacketo Dec 10 '14 at 16:55
  • ah, I see :) yes it can be used or not. I just want to write a propert if block. – Adem Dec 10 '14 at 16:57
  • Idk, but "max-2" doesn't look really random to me; – Felype Feb 15 '18 at 13:21
  • I first try to generate random betwen 20-2 (2 is 1 for 8, 1 for 15). then if I see 8 is hit, and I return 18, if it hits 15, then I return 19. 18 and 19 are not generated by random function. so, I use 8 and 15 to match 18 and 19 – Adem Feb 15 '18 at 20:12
0

You can build an array dynamically. Depending on where you are getting the excluded numbers. Something like:

var excluded = [8, 15];
var random = [];
for(var i = min; i <= max; i++) {
  if(excluded.indexOf(i) !== -1) {
    random.push(i);
  }
}

Then use the tips found in the answer for this post: How can I generate a random number within a range but exclude some?. Should get you to where you want to go.

Community
  • 1
  • 1
sareed
  • 665
  • 9
  • 19
0

Here is a really stupidly overcomplicated solution...

        <script>
    var excludedNumbers = [];
    excludedNumbers.push(8);
    excludedNumbers.push(15);
    excludedNumbers.push(10);

    var array = generateExclude(excludedNumbers, 1, 20);

    function generateExclude(excludedNumbers, min, max){
        var newNumbers = [];
        for(var i = min; i <= max; i++) {
            for(var j = 0; j < excludedNumbers.length; j++) {
                var checker = $.inArray(i, excludedNumbers)
                if(checker > -1){

                }else{
                    if($.inArray(i, newNumbers)<= -1){
                        newNumbers.push(i); 
                    }

                }
            };
        };
        return newNumbers;
    }
    function generateRandomNumbers(items){
        var num = items[Math.floor(Math.random()*items.length)];;
        return num;
    }
    console.log(generateRandomNumbers(array))
    </script>
Rinrub
  • 137
  • 10
0

I have answered a similar question for Java: Generate random numbers except certain values. I just copy and paste the answer as follows.

Actually, we do not need to use contains(random) with a while loop.

To simplify the question, let's see what happens if we only have one excluding value. We can split the result to 2 parts. Then the number of possible values is range-1. If the random number is less than the excluded value, just return it. Otherwise, we could add 1.

For multiple excluding values, We can split the result set into size+1 parts, where size means the number of excluding values. Then the number of possible values is range-size. Then we sort excluding values in ascending order. If random number is less than the excluding value minus i, then we just return the random number add i, where i is the index of the the excluding value.

public int generateRandomNumberWithExcepts(int start, int end, List<Integer> excepts) {
    int size = excepts.size();
    int range = end - start + 1 - size;
    int randNum = random.nextInt(range) + start;
    excepts.sort(null); // sort excluding values in ascending order
    int i=0;
    for(int except : excepts) {
        if(randNum < except-i){
            return randNum + i;
        }
        i++;
    }
    return randNum + i;
}
Hongyang
  • 79
  • 1
  • 4
0

I've read through all these answers and they differ a lot in philosophy, so I thought I might add my very own 2 bits, despite of this question having an answer, because I do think there is a better and more elegant way of approaching this problem.

We can make a function that takes min, max and blacklist as parameters and outputs a random result without using recursion (and with close to 0 if statements):

const blrand = function(min, max, blacklist) { if(!blacklist) blacklist = [] let rand = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; let retv = 0; while(blacklist.indexOf(retv = rand(min,max)) > -1) { } return retv; }

usage: let randomNumber = blrand(0, 20, [8, 15]);

Felype
  • 3,087
  • 2
  • 25
  • 36
0

You can simply do like this

function generatedRandExclude(showed,max) {
     let randNo = -1;
     while(showed.length < max) {
         randNo = Math.floor(Math.random() * Math.floor(max)); 
          if(!showed.includes(randNo)) {
             showed.push(randNo);
              break;
          }
     }
     return randNo;
}

let showed = [];
function run() {
    console.log(generatedRandExclude(showed,6));
}
run();
run();
run();
run();

generatedRandExclude generate random number excluded using array showed.

Onk_r
  • 836
  • 4
  • 21
0

This is a simple and neat idea, I am a electromechanical engineer and I am just learning JS. This is going to print a random numeber between 1 and 100. Except 8 and 15

 var r;   // this is the random integer.
 var val; //this will serve as validator for the random integer.

 val=0; 
 while(val==0)    {
 r=Math.round(Math.random()*100)+1;
 if(r!=8 && r!=15){val=1;}    //a valid number will be any number different from 8 and 15
                              //then validator will change and go out from the loop.
                }   
document.write(r);
0

You could take an offset for random values greater or equal than zerow ith a sorted (ascending) array and return a sum with adjusted random value.

const
    getRandomWithoutZero = (lower, upper, gaps) => () => {
        const r = Math.floor(Math.random() * (upper - lower + 1 - gaps.length) + lower);
        return gaps.reduce((s, g) => s + (s >= g), r);
    },
    random = getRandomWithoutZero(-9, 9, [-3, 0, 4]),
    count = {};

for (let i = 0; i < 1.6e6; i++) {
    const r = random();
    count[r] = (count[r] || 0) + 1;
}

console.log(count);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392