Random number between int.MinValue and int.MaxValu

2020-07-02 08:27发布

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.

标签: c# .net random
14条回答
何必那么认真
2楼-- · 2020-07-02 09:10

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...

查看更多
爷的心禁止访问
3楼-- · 2020-07-02 09:10

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;
        }
    }
查看更多
闹够了就滚
4楼-- · 2020-07-02 09:11

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;
    }
}
查看更多
爷、活的狠高调
5楼-- · 2020-07-02 09:13

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);
    }
}
查看更多
Evening l夕情丶
6楼-- · 2020-07-02 09:13

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);
}
查看更多
家丑人穷心不美
7楼-- · 2020-07-02 09:15

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
}
查看更多
登录 后发表回答