P. Frank
P. Frank

Reputation: 5745

Random integer in a certain range excluding one number

I would like get a random number in a range excluding one number (e.g. from 1 to 1000 exclude 577). I searched for a solution, but never solved my issue.

I want something like:

Math.floor((Math.random() * 1000) + 1).exclude(577);

I would like to avoid for loops creating an array as much as possible, because the length is always different (sometimes 1 to 10000, sometimes 685 to 888555444, etc), and the process of generating it could take too much time.

I already tried:

How could I achieve this?

Upvotes: 19

Views: 18984

Answers (8)

kliuengineering
kliuengineering

Reputation: 1

import random

def rng_generator():
  a = random.randint(0, 100)
  if a == 577:
    rng_generator()
  else:
    print(a)

#main()
rng_generator()

Upvotes: 0

Marco Bonelli
Marco Bonelli

Reputation: 69522

The fastest way to obtain a random integer number in a certain range [a, b], excluding one value c, is to generate it between a and b-1, and then increment it by one if it's higher than or equal to c.

Here's a working function:

function randomExcluded(min, max, excluded) {
    var n = Math.floor(Math.random() * (max-min) + min);
    if (n >= excluded) n++;
    return n;
}

This solution only has a complexity of O(1).

Upvotes: 32

guest271314
guest271314

Reputation: 1

Exclude the number from calculations:

function toggleRand() {
  // demonstration code only.
  // this algorithm does NOT produce random numbers.
  // return `0` - `576` , `578` - `n`  
  return [Math.floor((Math.random() * 576) + 1)
          ,Math.floor(Math.random() * (100000 - 578) + 1)
         ]
         // select "random" index 
         [Math.random() > .5 ? 0 : 1];
}

console.log(toggleRand());


Alternatively, use String.prototype.replace() with RegExp /^(577)$/ to match number that should be excluded from result; replace with another random number in range [0-99] utilizing new Date().getTime(), isNaN() and String.prototype.slice()

console.log(
  +String(Math.floor(Math.random()*(578 - 575) + 575))
  .replace(/^(577)$/,String(isNaN("$1")&&new Date().getTime()).slice(-2))
);


Could also use String.prototype.match() to filter results:

console.log(
  +String(Math.floor(Math.random()*10)) 
  .replace(/^(5)$/,String(isNaN("$1")&&new Date().getTime()).match(/[^5]/g).slice(-1)[0])
);

Upvotes: -2

Marco Bonelli
Marco Bonelli

Reputation: 69522

You could just continue generating the number until you find it suits your needs:

function randomExcluded(start, end, excluded) {
    var n = excluded
    while (n == excluded)
        n = Math.floor((Math.random() * (end-start+1) + start));
    return n;
}

myRandom = randomExcluded(1, 10000, 577);

By the way this is not the best solution at all, look at my other answer for a better one!

Upvotes: 2

Jesus Cuesta
Jesus Cuesta

Reputation: 175

One possibility is not to add 1, and if that number comes out, you assign the last possible value.

For example:

var result = Math.floor((Math.random() * 100000));
if(result==577) result = 100000;

In this way, you will not need to re-launch the random method, but is repeated. And meets the objective of being a random.

Upvotes: 5

Stryner
Stryner

Reputation: 7328

As @ebyrob suggested, you can create a function that makes a mapping from a smaller set to the larger set with excluded values by adding 1 for each value that it is larger than or equal to:

// min - integer
// max - integer
// exclusions - array of integers
//            - must contain unique integers between min & max
function RandomNumber(min, max, exclusions) {
    // As @Fabian pointed out, sorting is necessary 
    // We use concat to avoid mutating the original array
    // See: http://stackoverflow.com/questions/9592740/how-can-you-sort-an-array-without-mutating-the-original-array
    var exclusionsSorted = exclusions.concat().sort(function(a, b) {
        return a - b
    });

    var logicalMax = max - exclusionsSorted.length;
    var randomNumber = Math.floor(Math.random() * (logicalMax - min + 1)) + min;

    for(var i = 0; i < exclusionsSorted.length; i++) {
        if (randomNumber >= exclusionsSorted[i]) {
            randomNumber++;
        }
    }

    return randomNumber;
}

Example Fiddle

Also, I think @JesusCuesta's answer provides a simpler mapping and is better.

Update: My original answer had many issues with it.

Upvotes: 3

ebyrob
ebyrob

Reputation: 674

To expand on @Jesus Cuesta's answer:

function RandomNumber(min, max, exclusions) {
    var hash = new Object();
    for(var i = 0; i < exclusions.length; ++i ) {  // TODO: run only once as setup
       hash[exclusions[i]] = i + max - exclusions.length;
    }
    var randomNumber = Math.floor((Math.random() * (max - min - exclusions.length)) + min);
    if (hash.hasOwnProperty(randomNumber)) {
       randomNumber = hash[randomNumber];
    }
    return randomNumber;
}

Note: This only works if max - exclusions.length > maximum exclusion. So close.

Upvotes: 1

Gautham
Gautham

Reputation: 305

Generate a random number and if it matches the excluded number then add another random number(-20 to 20)

var max = 99999, min = 1, exclude = 577;
var num = Math.floor(Math.random() * (max - min)) + min ;
while(num == exclude || num > max || num < min ) {
    var rand = Math.random() > .5 ? -20 : 20 ;
    num += Math.floor((Math.random() * (rand));
}   

Upvotes: 0

Related Questions