Question

[Solved] Random number between int.MinValue and int.MaxValue, inclusive

Here’s a bit of a puzzler: Random.Next() has an overload that accepts a minimum value and a maximum value. This overload returns a number that is greater than or equal to the minimum value (inclusive) and less than the maximum value (exclusive).

I would like to include the entire range including the maximum value. In some cases, I could accomplish this by just adding one to the maximum value. But in this case, the maximum value can be int.MaxValue, and adding one to this would not accomplish what I want.

So does anyone know a good trick to get a random number from int.MinValue to int.MaxValue, inclusively?

UPDATE:

Note that the lower range can be int.MinValue but can also be something else. If I know it would always be int.MinValue then the problem would be simpler.

Solution #1:

The internal implementation of Random.Next(int minValue, int maxValue) generates two samples for large ranges, like the range between Int32.MinValue and Int32.MaxValue. For the NextInclusive method I had to use another large range Next, totaling four samples. So the performance should be comparable with the version that fills a buffer with 4 bytes (one sample per byte).

public static class RandomExtensions
{
    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        if (maxValue == Int32.MaxValue)
        {
            if (minValue == Int32.MinValue)
            {
                var value1 = random.Next(Int32.MinValue, Int32.MaxValue);
                var value2 = random.Next(Int32.MinValue, Int32.MaxValue);
                return value1 < value2 ? value1 : value1 + 1;
            }
            return random.Next(minValue - 1, Int32.MaxValue) + 1;
        }
        return random.Next(minValue, maxValue + 1);
    }

}

Some results:

new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(24917099).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
var random = new Random(784288084);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue

Update: My implementation has mediocre performance for the largest possible range (Int32.MinValueInt32.MaxValue), so I came up with a new one that is 4 times faster. It produces around 22,000,000 random numbers per second in my machine. I don’t think that it can get any faster than that.

public static int NextInclusive(this Random random, int minValue, int maxValue)
{
    if (maxValue == Int32.MaxValue)
    {
        if (minValue == Int32.MinValue)
        {
            var value1 = random.Next() % 0x10000;
            var value2 = random.Next() % 0x10000;
            return (value1 << 16) | value2;
        }
        return random.Next(minValue - 1, Int32.MaxValue) + 1;
    }
    return random.Next(minValue, maxValue + 1);
}

Some results:

new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue
new Random(1655705829).NextInclusive(int.MinValue, int.MaxValue); // = int.MaxValue
var random = new Random(1704364573);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // = int.MinValue
Respondent: Jonathan Wood

Solution #2:

Well, I have a trick. I’m not sure I’d describe it as a “good trick”, but I feel like it might work.

public static class RandomExtensions
{
    public static int NextInclusive(this Random rng, int minValue, int maxValue)
    {
        if (maxValue == int.MaxValue)
        {
            var bytes = new byte[4];
            rng.NextBytes(bytes);
            return BitConverter.ToInt32(bytes, 0);
        }
        return rng.Next(minValue, maxValue + 1);
    }
}

So, basically an extension method that will simply generate four bytes if the upper-bound is int.MaxValue and convert to an int, otherwise just use the standard Next(int, int) overload.

Note that if maxValue is int.MaxValue it will ignore minValue. Guess I didn’t account for that…

Respondent: Theodor Zoulias

Solution #3:

No casting, no long, all boundary cases taken into account, best performance.

static class RandomExtension
{
    private static readonly byte[] bytes = new byte[sizeof(int)];

    public static int InclusiveNext(this Random random, int min, int max)
    {
        if (max < int.MaxValue)
            // can safely increase 'max'
            return random.Next(min, max + 1);

        // now 'max' is definitely 'int.MaxValue'
        if (min > int.MinValue)
            // can safely decrease 'min'
            // so get ['min' - 1, 'max' - 1]
            // and move it to ['min', 'max']
            return random.Next(min - 1, max) + 1;

        // now 'max' is definitely 'int.MaxValue'
        // and 'min' is definitely 'int.MinValue'
        // so the only option is
        random.NextBytes(bytes);
        return BitConverter.ToInt32(bytes, 0);
    }
}
Respondent: Joshua Robinson

Solution #4:

Split the ranges in two, and compensate for the MaxValue:

r.Next(2) == 0 ? r.Next(int.MinValue, 0) : (1 + r.Next(-1, int.MaxValue))

If we make the ranges of equal size, we can get the same result with different math. Here we rely on the fact that int.MinValue = -1 - int.MaxValue:

r.Next(int.MinValue, 0) - (r.Next(2) == 0 ? 0 : int.MinValue)
Respondent: Alex

Solution #5:

I’d suggest using System.Numerics.BigInteger like this:

class InclusiveRandom
{
    private readonly Random rnd = new Random();

    public byte Next(byte min, byte max) => (byte)NextHelper(min, max);
    public sbyte Next(sbyte min, sbyte max) => (sbyte)NextHelper(min, max);
    public short Next(short min, short max) => (short)NextHelper(min, max);
    public ushort Next(ushort min, ushort max) => (ushort)NextHelper(min, max);
    public int Next(int min, int max) => (int)NextHelper(min, max);
    public uint Next(uint min, uint max) => (uint)NextHelper(min, max);
    public long Next(long min, long max) => (long)NextHelper(min, max);
    public ulong Next(ulong min, ulong max) => (ulong)NextHelper(min, max);

    private BigInteger NextHelper(BigInteger min, BigInteger max)
    {
        if (max <= min)
            throw new ArgumentException($"max {max} should be greater than min {min}");

        return min + RandomHelper(max - min);
    }

    private BigInteger RandomHelper(BigInteger bigInteger)
    {
        byte[] bytes = bigInteger.ToByteArray();
        BigInteger random;

        do
        {
            rnd.NextBytes(bytes);
            bytes[bytes.Length - 1] &= 0x7F;
            random = new BigInteger(bytes);
        } while (random > bigInteger);

        return random;
    }
}

I tested it with sbyte.

var rnd = new InclusiveRandom();
var frequency = Enumerable.Range(sbyte.MinValue, sbyte.MaxValue - sbyte.MinValue + 1).ToDictionary(i => (sbyte)i, i => 0ul);
var count = 100000000;
for (var i = 0; i < count; i++)
    frequency[rnd.Next(sbyte.MinValue, sbyte.MaxValue)]++;
foreach (var i in frequency)
    chart1.Series[0].Points.AddXY(i.Key, (double)i.Value / count);

chart1.ChartAreas[0].AxisY.StripLines
    .Add(new StripLine { Interval = 0, IntervalOffset = 1d / 256, StripWidth = 0.0003, BackColor = Color.Red });

Distribution is OK.

enter image description here

Respondent: Dialecticus

Solution #6:

This is guaranteed to work with negative and non-negative values:

public static int NextIntegerInclusive(this Random r, int min_value, int max_value)
{
  if (max_value < min_value)
  {
    throw new InvalidOperationException("max_value must be greater than min_value.");
  }
  long offsetFromZero =(long)min_value; // e.g. -2,147,483,648
  long bound = (long)max_value; // e.g. 2,147,483,647
  bound -= offsetFromZero; // e.g. 4,294,967,295 (uint.MaxValue)
  bound += Math.Sign(bound); // e.g. 4,294,967,296 (uint.MaxValue + 1)
  return (int) (Math.Round(r.NextDouble() * bound) + offsetFromZero); // e.g. -2,147,483,648 => 2,147,483,647
}
Respondent: Alex

Solution #7:

It’s actually interesting that this isn’t the implementation for Random.Next(int, int), because you can derive the behavior of exclusive from the behavior of inclusive, but not the other way around.

public static class RandomExtensions
{
    private const long IntegerRange = (long)int.MaxValue - int.MinValue;

    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new ArgumentOutOfRangeException(nameof(minValue));
        }

        var buffer = new byte[4];
        random.NextBytes(buffer);
        var a = BitConverter.ToInt32(buffer, 0);
        var b = a - (long)int.MinValue;
        var c = b * (1.0 / IntegerRange);
        var d = c * ((long)maxValue - minValue + 1);
        var e = (long)d + minValue;
        return (int)e;
    }
}
new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(-451732719).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
new Random(-394328071).NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue
Respondent: T K

Solution #8:

As I understand it you want Random to put out a value between -2.147.483.648 and +2.147.483.647. But the problem is that Random given those values will only give values from -2.147.483.648 to +2.147.483.646, as the maximum is exclusive.

Option 0: Take the thing away and learn to do without it

Douglas Adams was not a Programmer AFAIK, but he has some good advice for us: “The technology involved in making anything invisible is so infinitely complex that nine hundred and ninety-nine billion, nine hundred and ninety-nine million, nine hundred and ninety-nine thousand, nine hundred and ninety-nine times out of a trillion it is much simpler and more effective just to take the thing away and do without it.”

This might be such a case.

Option 1: We need a bigger Random!

Random.Next() uses Int32 as Argument. One option I can think off would be to use a different Random Function Which can take the next higher level of Integers (Int64) as input. An Int32 is implicitly cast into an Int64. Int64 Number = Int64(Int32.MaxValue)+1;

But afaik, you would have to go outside of .NET libraries to do this. At that point, you might as well look for a Random that is inclusive of the Max.

But I think there is a mathematical reason it had to exclude one value.

Option 2: Roll more

Another way is to use two calls of Random – each for one half of the range – and then add them.

Number1 = rng.Next(-2.147.483.648, 0);
Number2 = rng.Next(0, 2.147.483.647);
resut = Number1 + Number2;

However, I am 90% certain that will ruin the random distribution. My P&P RPG experience gave me some experience with dice chances and I know for a fact rolling 2 dice (or the same 2 times) will get you a very different result distribution than one specific die. If you do not need this random distribution, that is an option. But if you do not care too much about the distribution it is worth a check.

Option 3: Do you need the full range? Or do you just care for min and max to be in it?

I assume you are doing some form of testing and you need both Int.MaxValue and Int.MinValue to be in the range. But do you need every value in between as well, or could you do without one of them?
If you have to lose value, would you prefer loosing 4 rather then Int.MaxValue?

Number = rng.Next(Int.MinValue, Int.MaxValue);
if(Number > 3)
  Number = Number +1;

code like this would get you every number between MinValue and MaxValue, except 4. But in most cases code that can deal with 3 and 5 can also deal with 4. There is no need to explicitly test 4.

Of course, that assumes 4 is not some important test number that has to be run (I avoided 1 and 0 for those reasons). You could also decide the number to “skip” Randomly:

skipAbleNumber = rng.Next(Int.MinValue +1, Int.MaxValue);

And then use > skipAbleNumber rather than > 4.

Respondent: corranrogue9

Solution #9:

You can not use Random.Next() to achieve what you want, because you can not correspond sequence of N numbers to N+1 and not miss one :). Period.

But you can use Random.NextDouble(), which returns double result:
0 <= x < 1 aka [0, 1)
between 0, where [ is inclusive sign and ) exclusive

How do we correspond N numbers to [0, 1)?
You need to split [0, 1) to N equal segments:
[0, 1/N), [1/N, 2/N), … [N-1/N, 1)

And here is where it becomes important that one border is inclusive and another is exclusive – all N segments are absolutely equal!

Here is my code: I made it as a simple console program.

class Program
    {
        private static Int64 _segmentsQty;
        private static double _step;
        private static Random _random = new Random();

        static void Main()
        {
            InclusiveRandomPrep();
            for (int i = 1; i < 20; i++)
            {
                Console.WriteLine(InclusiveRandom());
            }

            Console.ReadLine();
        }

        public static void InclusiveRandomPrep()
        {
            _segmentsQty = (Int64)int.MaxValue - int.MinValue;
            _step = 1.0 / _segmentsQty;
        }
        public static int InclusiveRandom()
        {
            var randomDouble = _random.NextDouble();
            var times = randomDouble / _step;
            var result = (Int64)Math.Floor(times);
            return (int)result + int.MinValue;
        }
    }
Respondent: Christopher

Solution #10:

This method can give you a random integer within any integer limits. If the maximum limit is less than int.MaxValue, then it uses the ordinary Random.Next(Int32, Int32) but with adding 1 to upper limit to include its value. If not but with lower limit greater than int.MinValue, it lowers the lower limit with 1 to shift the range 1 less that add 1 to the result. Finally, if both limits are int.MinValue and int.MaxValue, it generates a random integer ‘a’ that is either 0 or 1 with 50% probability of each, then it generates two other integers, the first is between int.MinValue and -1 inclusive, 2147483648 values, and the second is between 0 and int.MaxValue inclusive , 2147483648 values also, and using them with the value of ‘a’ it select an integer with totally equal probability.

private int RandomInclusive(int min, int max)
{
    if (max < int.MaxValue)
        return Random.Next(min, max + 1);
    if (min > int.MinValue)
        return Random.Next(min - 1, max) + 1;
    int a = Random.Next(2);
    return Random.Next(int.MinValue, 0) * a + (Random.Next(-1, int.MaxValue) + 1) * (1 - a);
}
Respondent: tgralex

Solution #11:

What about this?

using System;

public class Example
{
   public static void Main()
   {
      Random rnd = new Random();
      int min_value = max_value;
      int max_value = min_value;
      Console.WriteLine("
20 random integers from 10 to 20:");
      for (int ctr = 1; ctr <= 20; ctr++) 
      {
         Console.Write("{0,6}", rnd.Next(min_value, max_value));
         if (ctr % 5 == 0) Console.WriteLine();
      }
   }
}

Solution #12:

You can try this. A bit hacky but can get you both min and max inclusive.

static void Main(string[] args)
{
    int x = 0;
    var r = new Random();
    for (var i = 0; i < 32; i++)
        x = x | (r.Next(0, 2) << i);
     Console.WriteLine(x);
     Console.ReadKey();
}
Respondent: Prochu1991

Solution #13:

You can add 1 to generated number randomly so it still random and cover full range integer.

public static class RandomExtension
{
    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        var randInt = random.Next(minValue, maxValue);
        var plus = random.Next(0, 2);
        return randInt + plus;
    }
}
Respondent: Anil Goel

Solution #14:

Will this work for you?

int random(Random rnd, int min, int max) 
{
    return Convert.ToInt32(rnd.NextDouble() * (max - min) + min);
}
Respondent: Ali Zeinali

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Most Popular

To Top
India and Pakistan’s steroid-soaked rhetoric over Kashmir will come back to haunt them both clenbuterol australia bossier man pleads guilty for leadership role in anabolic steriod distribution conspiracy