Exclude values from Random.Range()?

12,992

Solution 1

The best way to do this is to use your favourite generator to generate an integer n between 1 and 17 then transform using

if (n > 5){
    n += 3;
}

If you sample between 1 and 20 then discard values you can introduce statistical anomalies, particularly with low discrepancy sequences.

Solution 2

So you actually want 17 (20 - 3) different values

  [1..5] U [9..20]

and you can implement something like this:

  // Simplest, not thread-safe
  private static Random random = new Random();

  ...  

  int r = (r = random.Next(1, 17)) > 5
    ? r + 3
    : r;

In general (and complicated) case I suggest generating an array of all possible values and then take the item from it:

  int[] values = Enumerable
    .Range(1, 100) // [1..100], but
    .Where(item => item % 2 == 1) // Odd values only
    .Where(item => !(item >= 5 && item <= 15)) // with [5..15] range excluded
    //TODO: Add as many conditions via .Where(item => ...) as you want
    .ToArray();

  ...

  int r = values[random.Next(values.Length)];

Solution 3

Yes, You simple use where statment in LINQ

   var list = Enumerable.Range(1, 20).Where(a => a < 6 || a > 8).ToArray();

Other way witout LINQ

        public IEnumerable RangeBetween()
        {
            foreach (var i in Enumerable.Range(1, 20))
            {
                if (i < 6 || i > 8)
                {
                    yield return i;
                }
            }
        }

EDIT: Now I see is not a strict C# question . It affect Unity and Random. But for complete answer I sugest You use code above with Enumerable.Range and next use this for generate the number:

list[Random.Next(list.Length)];

Solution 4

Another way is to make an array of the valid return values and then randomly select one:

void Main()
{
    var rng = new Random();
    var validValues = Enumerable.Range(1, 20).Except(new int[] {6, 7, 8}).ToArray();

    for (int i = 0; i < 25; i++)
    {
        Console.Write(validValues[rng.Next(0, validValues.Length)]);
        Console.Write(" ");
    }
}

EDIT: Oops! Just noticed this was for Unity3D so this example might not be appropriate. It works using the standard Random class, though.

Share:
12,992

Related videos on Youtube

Dinovr
Author by

Dinovr

Updated on September 14, 2022

Comments

  • Dinovr
    Dinovr 4 months

    If you are using Random.Range() to generate values, is there any way to exclude some values within the range (for example: pick a number between 1 and 20, but not 6 through 8)?

    • Bathsheba
      Bathsheba
      I've added the random tag to introduce this question to the many random number experts on this site, and to give my answer some credibility.
    • Andrey Korneyev
      Andrey Korneyev
      You can write your own method to check if generated number falls into excluded ranges and in this case re-generate it.
  • Programmer
    Programmer over 6 years
    This is for game engine. If you can avoid linq, avoid it. It allocates memory and some functions wont work in iOS.
  • blogprogramisty.net
    blogprogramisty.net over 6 years
    @Programmer Avoid lambda expression too?
  • Bathsheba
    Bathsheba over 6 years
    Plus one, this is statistically correct; I'm trusting that the assignment on the conditional test to a variable you've defined on the left of the assignment is well-defined in C#!
  • Programmer
    Programmer over 6 years
    I wouldn't say that. I mean if he calling that code most of the time, he should avoid it.
  • Ignacio
    Ignacio over 6 years
    I think that using this method there is a chance (really small one, but still) that the program enters in an infinite loop... if you always get a number between 6 and 8.
  • Programmer
    Programmer over 6 years
    I feel like commenting on this one. This is a great way to freeze your game. @Ignacio You are right. This has happened to me before. No while loop while generating random number in Unity.
  • Isaak Eriksson
    Isaak Eriksson over 6 years
    @Programmer LINQ works fine on iOS if you use Lists instead of arrays. It is a great asset for abstracting the complications of loops and lists. I think the reason why it works only with Lists on iOS because LINQ doesn't use IEnumerable (which has problems on iOS) on Lists whereas on arrays it does. Interesting.
  • Ignacio
    Ignacio over 6 years
    @Programmer, not just in Unity, It can freeze any program
  • Programmer
    Programmer over 6 years
    @Ignacio Good to know that.
  • Programmer
    Programmer over 6 years
    @MyIsaak Yes it works with List. It's not working because of AOT features not available on iOS. Although I use few linq functions from it only when necessary. Still, the first code in his answer will still allocate memory just to generate random number....Not worth the sacrifice.
  • Benjamin Gruenbaum
    Benjamin Gruenbaum over 6 years
    Uh, it really can't freeze a program. Computers rely on being able to generate random numbers and use random processes everywhere - things like the internet and its protocols rely on that - there is a chance that a packet will drop and never arrive but statistically this never happens. If you run this program on a computer since the start of the universe until now it would not ever get stuck - it's easy to prove that on a uniform probability (or any probability with a non-zero chance for a number not between 6 and 8 to appear) that it would likely not get stuck for over a second ever.
  • Everts
    Everts over 6 years
    Unity3D provides a Random class and most users rely on that one. Also, Linq is not so recommended in Unity for internal reason (FullAOT on iOS) which makes some of its functionalities not always work.