19

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?

9
  • 3
    I don't understand the problem you're having and why the solutions you posted don't work for you. Wouldn't a simple while loop that checks for the forbidden number be sufficient? Commented Dec 9, 2015 at 15:34
  • 8
    Get a random number from 1 to 99999, if the number is >= 577, then add 1. Commented Dec 9, 2015 at 15:34
  • 2
    @ebyrob ok but if is 99999 the number and i add 1 it make an error because 10000 not exsist. I dont want create "if" condition Commented Dec 9, 2015 at 15:36
  • 1
    But logically, a loop makes the most sense. Get a random number, does this number equal the forbidden number? If so, then get a new random number. Unless you're randomly picking from something like an array that doesn't contain the forbidden number to start with, then you're forced to check on every pick. Commented Dec 9, 2015 at 15:41
  • 1
    So generate the number, if it is in that list, generate a new one, continue until you have one. I highly doubt it will keep generating 557.... Commented Dec 9, 2015 at 15:41

8 Answers 8

32

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).

Sign up to request clarification or add additional context in comments.

6 Comments

yes is fine but n++ get not random number but random + 1
@P.Frank That's the trick! For example assume you want it from 1 to 10, but not 5. You generate it from 1 to 9, then, if it is higher than or equal to 5, you add 1. Doing this you can only obtain [1, 2, 3, 4, 6, 7, 8, 9, 10], which is what you want. All the numbers have the same probability to be generated, and 5 will never be generated.
I have choose your solution, but the solution of @guest271314 is too good for me. thank you
What if i wanted to pass an array of numbers(any size)..say 5,6 & 7 to be excluded? How would I approach this?
Why is the greater than important? Why can't we increment the random number only if n=excluded? @MarcoBonelli
|
5

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.

2 Comments

This also seems to work. Not sure assignment is any faster than addition but nice twist. Why the down vote? I think for multiple exclusions this would actually be faster. (and not require sorting)
this is the only correct algorithm, but Marco's version below is more elegant.
3

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.

3 Comments

Yep, the logical conclusion, loop on the smaller set.
@Fabian Thank you for pointing that out. I updated the function accordingly.
This is too great for me. Thanks
2

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!

4 Comments

This has infinite slowdown potential.
Yeah, if you think about it mathematically, but since that JS can generate thousands of randoms in a microsecond, this is never going to be slow.
very surprisingly you would suggest this Marco, given you know the correct answer below. should be deleted.
@JoeBlow this is still an answer, and there's no need to delete it because it answers the question properly, like many others do
1

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.

Comments

0

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)); } 

Comments

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

Comments

-2

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]) );

26 Comments

Certainly an interesting solution, might hurt the random distribution though.
this is not the intended random distribution. The first time it's called, it only returns a number < 576.
@Fabian Could use Math.random() > .5 ? 0 : 1 to select random index between 0 , 1 to return
This solution is very bad! Splitting the range in two, and then looking on the first or the second part based on Math.random() > .5 ? 0 : 1 will NOT generate pure random numbers, as the probability of obtaining each number is not equal. Take for example: [Math.floor((Math.random() * 1) + 1), Math.floor(Math.random() * (100000 - 3 + 1))][Math.random() > .5 ? 0 : 1]: it will generate 1 in 50% of the cases.
It simply does not generate random numbers. It's really a shame when non-mathematicians just make totally wild guesses at things like this: the problem is, this utter nonsense will now be seen on the internet for 1000 years. The poster here should just click delete for goodness sake. Note that this is a RIDICULOUSLY well-known problem in comp sci and the solution is absolutely trivial. Here's a full, long-winded explanation with source code stackoverflow.com/a/35315960/294884 Also the correct answer is given below at stackoverflow.com/a/34184614/294884
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.