'How to get a random number from a range, excluding some values
In C#, how do I get a random number from a range of values - like 1..100, but that number should not be in some specific list of values, like 5, 7, 17, 23?
Solution 1:[1]
If you care about Big O, check out this algorithm. It assumes that the excluded values array is sorted in ascending order and contains values within 0 and n-1 range (inclusive).
public static int random_except_list(int n, int[] x)
{
Random r = new Random();
int result = r.Next(n - x.Length);
for (int i = 0; i < x.Length; i++)
{
if (result < x[i])
return result;
result++;
}
return result;
}
If you call it with:
random_except_list(8, new int[]{3,4,6})
it will return one of the following values: 0, 1, 2, 5, 7.
Solution 2:[2]
This is what I do in this situation, it's not perfect but works well for me. I usually do it only for 1 number but this is how it can be for a group of excluded numbers:
Let's say I want to exclude [5, 7, 17, 23] from a random between 1-100. I always have a substitution for each of the excluded numbers such as [6, 8, 18, 24]. If the random number falls into any of the excluded numbers, I replace it with its substitution.
I came here looking for a better solution but I couldn't find any so I ended up sharing mine.
Solution 3:[3]
you can use a do-while statement to pick another Random if it equals what number you want to exclude. this code is to exclude the number you picked before
int newNumber;
do {
newNumber = Random.Range (0, 100);
} while(number == newNumber);
number = newNumber;
Solution 4:[4]
This is the Extention method I use:
Random random = new Random();
public static int RandomNumber(int minN, int maxN, IEnumerable<int> exNumbers)
{
int result = exNumbers.First();
while (exNumbers.ToList().Contains(result))
{
result = random.Next(minN, maxN + 1);
}
return result;
}
Solution 5:[5]
From Java, but I'm pretty sure you can change the language easily :)
Solution:
/**
* Get a random number between a range and exclude some numbers
*
* @param start start number
* @param end end number
* @param excludes list of numbers to be excluded
* @return value between {@code start} (inclusive) and {@code end} (inclusive)
*/
private int getRandomWithExclusion(int start, int end, List<Integer> excludes) {
Collections.sort(excludes); // this method only works with sorted excludes
int random = start + new Random().nextInt(end - start + 1 - excludes.size());
for (int exclude : excludes) {
if (random < exclude) {
break;
}
random++;
}
return random;
}
Solution 6:[6]
Create an array containing all the numbers you want (or whatever container your language uses) minus all the number you don't want and select at random from the array.
Solution 7:[7]
Use a function to generate random numbers between 1 and 100, than write an if statement e.g. if random number is equal to 5, 7, 17, 23, generate the random number again, else use the random number that was generated in the first place.
Solution 8:[8]
Put the allowed numbers into an array, generate a random integer from 0 to the length of this array minus one. Use this integer as an index to get the random number itself from the array of allowed numbers.
If the original array contains large objects instead of numbers, then making another array by deep copying the allowed objects won't be effective. In this case, the array of allowed objects should contain only a pointer, a reference, or an index to the objects in the original array. In this case you generate a random integer to select one element of this array, and use this pointer/reference/index to get the selected object itself from the original array.
Here is a working example for the general case (just one possible solution!):
using System;
using System.Collections.Generic;
public static class RandomElementSelector
{
public static IList<T> CollectAllowedElements<T>(IList<T> allElements, IList<T> excludedElements)
{
List<T> allowedElements = new List<T>();
foreach (T element in allElements)
if (!excludedElements.Contains(element))
allowedElements.Add(element);
return allowedElements;
}
public static T SelectRandomElement<T>(IList<T> allowedElements)
{
Random random = new Random();
int randomIndex = random.Next(allowedElements.Count);
return allowedElements[randomIndex];
}
public static T SelectRandomElement<T>(IList<T> allElements, IList<T> excludedElements)
{
IList<T> allowedElements = CollectAllowedElements(allElements, excludedElements);
return SelectRandomElement(allowedElements);
}
}
public class Test
{
public static void Main()
{
const int N = 100;
// Example #1
int[] allNumbers = new int[N];
for (int i = 0; i < allNumbers.Length; ++i)
allNumbers[i] = i + 1;
int[] excludedNumbers = { 5, 7, 17, 23 };
Console.WriteLine(RandomElementSelector.SelectRandomElement(allNumbers, excludedNumbers));
// Example #2
List<string> allStrings = new List<string>();
for (int i = 0; i < N; ++i)
allStrings.Add("Item #" + (i + 1));
string[] excludedStrings = { "Item #5", "Item #7", "Item #17", "Item #23" };
Console.WriteLine(RandomElementSelector.SelectRandomElement(allStrings, excludedStrings));
}
}
Solution 9:[9]
Try this:
public static int GetRandomNumber(int Min, int Max, int[] ExcludedNumbers)
{
Random randomGenerator = new Random();
int currentNumber = randomGenerator.Next(Min, Max);
while (ExcludedNumbers.Contains(currentNumber))
{
currentNumber = randomGenerator.Next(Min, Max);
}
return currentNumber;
}
Here is an example of its usage:
int randomNumber = GetRandomNumber(1, 101, new int[] { 5, 7, 17, 23 });
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Voicu |
| Solution 2 | Maher Manoubi |
| Solution 3 | Ahmed Mansy |
| Solution 4 | |
| Solution 5 | nnhthuan |
| Solution 6 | stmfunk |
| Solution 7 | Saint |
| Solution 8 | |
| Solution 9 | Jonathan Barraone |
