.NET Random – Inclusive or Exclusive Upper Bound in Pseudo-Random Number Generators

netrandom

I am creating a little randomization library. One of the methods is similar to .NET's System.Random.Next(int start, int end). The thing is, I am not sure how to decide on whether the upperp bound should be inclusive or exclusive. While .NET's Random.Next has an exclusive upper bound, there are other frameworks and libraries that work with inclusive bound.

First there's a question of why do some library makers choose upper bound to be exclusive. IMO, from the library user's perspective, it doesn't read nice.

For example, throwing a d6 die looks like this:

var dieResult = rand.Next(1, 7);

The second argument hints either that 7 is a valid result, or that I want a random number from the range of 7 consecutive numbers starting with the number 1. None of which is true. I can't see how choosing an exclusive upper bound makes any sense.

Additionally, by being exclusive, upper bound prevents a PRNG from choosing the Int32.MaxValue which is another strange thing.

So, to summarize:

  • Is there an explanation as to why did .NET devs choose upper bound to be exclusive

Best Answer

The simple, slightly humorous, and not entirely likely answer: They copied Java.

public int nextInt(int n)

Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified value (exclusive), drawn from this random number generator's sequence. ...

More likely, it dates back to C and its random() which generates 0 to INT_MAX. To get it back into the range you want, you would typically do a (rand() % range) + 1. So, if you wanted a d6 you would do dieResult = (rand() % 6) + 1. Note the 'exclusive' range there. Do also note that this is a very poor random number generator because of its non-uniform distribution.

The choice of inclusive or exclusive is one of debate, and one can find reasons for it on both sides. And someone is going to be unhappy and someone is going to get a off by one error with whichever choice is made.

The exclusive high value makes it much easier to work with zero based indexes such as the code given in the docs:

  int mIndex = rnd.Next(0, malePetNames.Length);
  int fIndex = rnd.Next(0, femalePetNames.Length);

I remember that nicely in perl back in the day:

$rnd = $array[rand @array];

Note the 0 based indexing and the exclusive top range.

On the other hand, python uses an inclusive range. C++'s std::uniform_int_distribution also uses an inclusive range for its random numbers.

If it was inclusive, you'd be adding a - 1 to each of those max values for selecting an element from the list. On the other hand an exclusive range, makes dice rollers look funny and trying to get from 0 to the Int32.MaxValue a bit more tricky. It also averts some index out of bounds errors in the code snippets from above. Pick which off by one error you'll make occasionally.

Trying to do both makes the library unwieldy and confusing. Pick one. Document it. Stick with it.

.Net (and Java) probably stuck with the one that was most familiar to its users from other languages and possibly one that made interfacing to existing libraries at the time a tad bit easier.