How can I generate a random number within a range but exclude some, without keep generating and checking if the generated number is one of those that I want to exclude?
10 Answers
One possible solution without regeneration the random each time is to use the following algorithm:
public int getRandomWithExclusion(Random rnd, int start, int end, int... exclude) {
int random = start + rnd.nextInt(end - start + 1 - exclude.length);
for (int ex : exclude) {
if (random < ex) {
break;
}
random++;
}
return random;
}
This method can be either called with an array reference, e.g.
int[] ex = { 2, 5, 6 };
val = getRandomWithExclusion(rnd, 1, 10, ex)
or by directly inserting the numbers into the call:
val = getRandomWithExclusion(rnd, 1, 10, 2, 5, 6)
It generates a random number (int) between start
and end
(both inclusive) and does not give you any number which is contained in the array exclude
. All other numbers occur with equal probability. Note, that the following constrains must hold: exclude
is sorted ascendingly and all numbers are within the range provided and all of them are mutually different.

- 38,639
- 9
- 64
- 83
-
+1 same thought here. But I would prefer checking `random < ex` inside loop and immediate return `random` then. Otherwise do `random++` and go on looping. Should perform better on large exclude arrays. – Fabian Barney Jun 22 '11 at 16:53
-
1@Fatal, @Howard, this is an exceedingly clever solution. Where did you learn of it or how did you come up with it? At first I was sure this wouldn't work but after stepping through it I found it certainly does work and I'm impressed. For everyday code I wouldn't want to see this in our codebase because it gives speed at the expensive of readability. – Paul Jun 22 '11 at 18:35
-
2This approach fascinates me. I did some benchmarking against this: `do{ draw=start+rnd.nextInt(end);} while (Arrays.binarySearch(ex, draw) >=0);` and found that sometimes searching the array is faster. As the ratio of exclusions to pool approaches 1 (e.g. 10 excluded numbers in a pool of 11) searching the array gets really slow (100 million iterations took 56s vs 3.5s using the above) but if you expand the pool to 1000 e.g. searching the array is quicker (4.1s vs 4.7s). Again, that's doing it 100 million times. A single time takes about 10000 nanos for the above vs 20000 nanos for the search. – Paul Jun 22 '11 at 18:59
-
I'm not convinced that the distribution is uniform among the not excluded numbers. If I have the range 1,2,3 and I exclude 2, won't 3 appear 66% of the time and 1 only 33% of the time? – Fabio Jan 12 '17 at 17:43
-
4I want to make a remark about your answer. Your solution works only if your ex array is ordered, otherwise it doesn't work correctly – Davide May 01 '17 at 11:13
-
@Fabio — it won't, because there is no 66% and 33%, it's fifty-fifty. You have 3 numbers and you excluded one of them, so it will get a random number from the range [1, 2]. Half of the time it will get 1 and stop there, and half of the time it will get 2 and add 1 then, resulting in 3. – Anonymouse Nov 08 '20 at 20:07
-
-
To make sure this method always works fine, inside the method, we need to sort `exclude` first before doing anything else. – Jan 13 '21 at 14:34
/**
* @param start start of range (inclusive)
* @param end end of range (exclusive)
* @param excludes numbers to exclude (= numbers you do not want)
* @return the random number within start-end but not one of excludes
*/
public static int nextIntInRangeButExclude(int start, int end, int... excludes){
int rangeLength = end - start - excludes.length;
int randomInt = RANDOM.nextInt(rangeLength) + start;
for(int i = 0; i < excludes.length; i++) {
if(excludes[i] > randomInt) {
return randomInt;
}
randomInt++;
}
return randomInt;
}
The idea is to reduce the range wherein the random number is generated to the difference between start and end minus count of numbers within that range that are excluded.
So you get a range length which is identical with the count of possible valid numbers. In other words: You've removed all holes from range.
After generating the random number you've to put the "holes" back in the range. This can be achieved by incrementing the generated number as long as there are excluded numbers lower than or equal to the generated one. The lower exclude numbers are "holes" in the range before the generated number. And the generated number is shifted to right for every hole before that number.

- 1,044
- 1
- 17
- 36

- 14,219
- 5
- 40
- 60
The best approach that you can follow to randomize numbers, excluding some is selecting the numbers that you want first and then randomly select the numbers selected. For example, in pseudo-code:
List<Number> numbers;
numbers.add(1);
numbers.add(2);
numbers.add(3);
//You can do a "for" without adding the excluded numbers..
//Then, your randomizer could be...
public Number getRandoNumber() {
int index = Random.get(0, numbers.size());
return numbers.get(index);
}
Now, you don't need to check if the "generated number" is allowed or not, because it doesn't exist at all.
If you don´t want them to repeat, you can do something like:
Collections.shuffle(numbers);
public Number getRandomNotRepeat() {
if(numbers.size() == 0)
throw new RuntimeException("No more numbers");
Number n = numbers.get(0);
numbers.removeFirst();
return n;
}

- 30,436
- 41
- 178
- 315

- 242
- 2
- 13
This is the most reliable way to do it.
Create an array with your range, remove all excluding elements
Choose a random index and return the value from that position.
public int getRandomNumber(int start, int end, List<Integer> excludingNumbers) {
int[] range = IntStream.rangeClosed(start, end).toArray();
List<Integer> rangeExcluding = Arrays.stream( range ).boxed().collect( Collectors.toList() );
rangeExcluding.removeAll(list);
int newRandomInt = new Random().nextInt(rangeExcluding.size())
return rangeExcluding.get(newRandomInt);
}
Range includes start and end

- 345
- 1
- 5
- 20
Create a map that takes the output of a random function without the range restriction and map it to the range you want with restrictions.
For example, if I want a random int from 1-10 but never 7 I could do something like this:
int i = rand(1, 9);
if i>=7
i++;
return i;
As long as you ensure that your mapping is 1:1, you can avoid skewing the randomness of your rand
function.

- 687
- 7
- 15
-
The other way round would be better: create numbers from1 to 8 and map 7 and 8 to 8 and 9. This way you keep the distribution. – keuleJ Jun 22 '11 at 16:39
-
@keuleJ: you forgot about mapping 9 to 10 Besides, it was just meant as a trivial example. Whichever map you come up with is going to be specific to your algorithm. As long as your map is consistent (each possible input maps to the same number of outputs and each desired output is mapped to by the same number of possible inputs) you'll keep the distribution. – Ax. Jun 22 '11 at 16:59
-
@Ax you're right. But you mapped 7 and 8 to 8. It's easy to mess up the distribution in these kind of algorithms... – keuleJ Jun 22 '11 at 17:07
-
@keuleJ: No I didn't. It's doing the same thing as Howard's code, but on a trivial scale. – Ax. Jun 22 '11 at 18:56
I think the additional question is: What are the numbers you want to exlcude? Do they represent some sort of range or are they totally random?
If it was a range of numbers you want to ignore, you could generate your random numbers from within few sets that represent only valid numbers:
rand(1,9);
rand(15,19);
rand(22,26);
this way you are sure you will never select excluded: <0,10,11,12,13,14,20,21,>27
Then, when you get your 3 numbers, you could again randomly select one of them.
If excluded numbers are all over the place than I'm afraid you'd have to check it every time against some sort of collection of excluded numbers.

- 7,748
- 2
- 33
- 54
-
1I had to retract my earlier statement. While this seems like a good idea, it actually skews the results unless each of the "selected" ranges are of equal size. Say I want to exclude the number 2 from a range from 0 to 10, numbers 0 and 1 will appear twice and frequently than the rest of the numbers. To fix this, you'd have to favor picking the random number that came from larger ranges, but I'm not sure how to accommodate that and maintain an even distribution. – Carcigenicate Nov 08 '16 at 22:54
exclude numbers should be with in range parameter
private int GiveMeANumber(int range,int... exclude)
{
Set<Integer> integers=new HashSet<>();
int count=range;
for(int i=0;i<count;i++)
integers.add(i);
integers.removeAll(Arrays.asList(exclude));
int index = new Random().nextInt(range - exclude.length);
count=0;
for (int value:integers){
if(count==index)
return value;
count++;
}
return 0;
}

- 5,278
- 43
- 65
- 115

- 21
- 3
example: val random = arrayOf((-5..-1).random(), (1..10).random()).random()
In this example you exclude 0 in numbers between -5 and 10

- 17
- 4
Depending on how large the list of random numbers you're excluding, I would just generate your numbers and check if it's in the array of excluded numbers- if it is, just discard it. I know you don't want to check every time, but I can't think of another way besides specifying the ranges explicitly and if you have more than 5 numbers you're excluding that might be a bit worse.

- 2,871
- 2
- 19
- 22
Something that could work and applies both to int and double numbers could be like :
public int getRandomNumberWithExclusion( int start, int end )
{
Random r = new Random();
int result = -1;
do
{
result = start + r.nextInt( end - start );
}//do
while( !isAllowed( result ) );
return result;
}//met
private boolean isAllowed( int number )
{
//your test for restricted values here
}//met
Regards, Stéphane

- 37,840
- 15
- 114
- 173
-
1"[...] without keep generating and checking if the generated number is one of those that I want to exclude" – Fabian Barney Jun 22 '11 at 16:28