Случайное число между int.MinValue и int.MaxValue, включительно

Здесь немного Random.Next(): Random.Next() имеет перегрузку, которая принимает минимальное значение и максимальное значение. Эта перегрузка возвращает число, которое больше или равно минимальному значению (включительно) и меньше максимального значения (исключая).

Я хотел бы включить весь диапазон, включая максимальное значение. В некоторых случаях я мог бы сделать это, просто добавив один к максимальному значению. Но в этом случае максимальное значение может быть int.MaxValue, и добавление одного к этому не достигнет того, что я хочу.

Так кто-нибудь знает хороший прием, чтобы получить случайное число от int.MinValue до int.MaxValue включительно?

ОБНОВИТЬ:

Обратите внимание, что нижний диапазон может быть int.MinValue но может быть и другим. Если бы я знал, что это всегда будет int.MinValue тогда проблема будет проще.

Ответ 1

Внутренняя реализация internal implementation из Random.Next(int minValue, int maxValue) генерирует две выборки для больших диапазонов, например, диапазон между Int32.MinValue и Int32.MaxValue. Для метода NextInclusive мне пришлось использовать другой большой диапазон Next, всего четыре выборки. Поэтому производительность должна быть сопоставима с версией, которая заполняет буфер 4 байтами (один образец на байт).

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);
    }

}

Некоторые результаты:

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

Обновление: Моя реализация имеет посредственную производительность для максимально возможного диапазона (Int32.MinValue - Int32.MaxValue), поэтому я придумал новую, которая в 4 раза быстрее. Он производит около 22 000 000 случайных чисел в секунду на моей машине. Я не думаю, что это может быть быстрее, чем это.

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);
}

Некоторые результаты:

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

Ответ 2

Ну, у меня есть трюк. Я не уверен, что назвал бы это "хорошим трюком", но я чувствую, что это может сработать.

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);
    }
}

Таким образом, в основном это метод расширения, который просто сгенерирует четыре байта, если верхняя граница будет int.MaxValue и преобразуется в int, в противном случае просто используйте стандартную перегрузку Next(int, int).

Обратите внимание, что если maxValue равно int.MaxValue оно будет игнорировать minValue. Думаю, я не учел этого...

Ответ 3

Нет кастинга, нет long, все граничные случаи приняты во внимание, лучшая производительность.

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);
    }
}

Ответ 4

Разделите диапазоны на две части и компенсируйте MaxValue:

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

Если мы сделаем диапазоны одинакового размера, мы можем получить тот же результат с другой математикой. Здесь мы полагаемся на тот факт, что int.MinValue = -1 - int.MaxValue:

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

Ответ 5

Я бы предложил использовать System.Numerics.BigInteger следующим образом:

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;
    }
}

Я проверил это со 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 });

Распределение в порядке.

enter image description here

Ответ 6

Это гарантированно работает с отрицательными и неотрицательными значениями:

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
}

Ответ 7

Насколько я понимаю, вы хотите, чтобы Random выдал значение между -2.147.483.648 и +2.147.483.647. Но проблема в том, что Random, учитывая эти значения, будет давать значения только от -2.147.483.648 до +2.147.483.64 6, поскольку максимум является исключительным.

Вариант 0: убери вещь и научись обходиться без нее

Дуглас Адамс не был программистом AFAIK, но у него есть несколько полезных советов для нас: "Технология создания чего-то невидимого настолько бесконечно сложна, что девятьсот девяносто девять миллиардов, девятьсот девяносто девять миллионов, девятьсот девяносто Девять тысяч девятьсот девяносто девять раз из триллиона гораздо проще и эффективнее просто забрать вещь и обойтись без нее. "

Это может быть такой случай.

Вариант 1: нам нужен больший случайный выбор!

Random.Next() использует Int32 в качестве аргумента. Один вариант, который я могу обдумать, - это использовать другую случайную функцию, которая может принимать следующий более высокий уровень целых чисел (Int64) в качестве входных данных. Int32 неявно преобразуется в Int64. Int64 Number = Int64(Int32.MaxValue)+1;

Но, на самом деле, вы должны выйти за пределы библиотек .NET, чтобы сделать это. В этот момент вы могли бы также искать Рандом, включающий Макс.

Но я думаю, что есть математическая причина, по которой он должен был исключить одно значение.

Вариант 2: бросьте больше

Другой способ - использовать два вызова Random - каждый для одной половины диапазона - и затем добавить их.

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

Тем не менее, я уверен на 90%, что разрушит случайное распределение. Мой опыт P & P RPG дал мне некоторый опыт с шансами на кости, и я знаю, что если бросить 2 кубика (или 2 одинаковых), вы получите распределение результатов, отличное от одного конкретного кубика. Если вам не нужно это случайное распределение, это вариант. Но если вы не слишком заботитесь о распространении, стоит проверить.

Вариант 3: Вам нужен полный спектр? Или вы просто заботитесь о том, чтобы в нем были минимум и максимум?

Я предполагаю, что вы проводите какое-то тестирование, и вам нужны и Int.MaxValue, и Int.MinValue. Но нужно ли вам каждое значение между или вы можете обойтись без одного из них? Если вам нужно потерять значение, вы бы предпочли потерять 4, а не Int.MaxValue?

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

код, подобный этому, получит каждое число от MinValue до MaxValue, , кроме 4. Но в большинстве случаев код, который может иметь дело с 3 и 5, также может иметь дело с 4. Нет необходимости явно тестировать 4.

Конечно, это предполагает, что 4 - это не какой-то важный номер теста, который должен быть запущен (я избегал 1 и 0 по этим причинам). Вы также можете выбрать случайное число для пропуска:

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

А затем используйте > skipAbleNumber вместо > 4.

Ответ 8

На самом деле интересно, что это не реализация для Random.Next(int, int), потому что вы можете вывести поведение исключающего из поведения включающего, но не наоборот.

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

Ответ 9

Этот метод может дать вам случайное целое число в любых целочисленных пределах. Если максимальный предел меньше, чем int.MaxValue, тогда он использует обычный Random.Next(Int32, Int32), но с добавлением 1 к верхнему пределу, чтобы включить его значение. Если нет, но с нижним пределом больше, чем int.MinValue, он понижает нижний предел на 1, чтобы сместить диапазон на 1 меньше, добавив 1 к результату. Наконец, если оба предела являются int.MinValue и int.MaxValue, он генерирует случайное целое число "a", равное 0 или 1 с вероятностью 50% каждого, затем генерирует два других целых числа, первое между int.MinValue и -1 включительно, значения 2147483648, а второе - от 0 до int.MaxValue включительно, значения также 2147483648, и, используя их со значением "a", он выбирает целое число с абсолютно равной вероятностью.

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);
}

Ответ 10

Как насчет этого?

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("\n20 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();
      }
   }
}

Ответ 11

Вы можете попробовать это. Немного хакер, но вы можете получить как мин, так и макс. Включительно.

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();
}

Ответ 12

Вы не можете использовать Random.Next() для достижения того, чего хотите, потому что вы не можете соответствовать последовательности из N чисел N+1 и не пропустить одно :). Период.

Но вы можете использовать Random.NextDouble(), который возвращает двойной результат: 0 <= x < 1 aka [0, 1) между 0, где [ это знак включения и ) эксклюзив

Как мы соотносим N чисел с [0, 1)? Вам нужно разделить [0, 1) на N равных сегментов: [0, 1/N), [1/N, 2/N) ,... [N-1/N, 1)

И здесь становится важным, чтобы одна граница была инклюзивной, а другая - эксклюзивной - все N сегментов абсолютно равны!

Вот мой код: я сделал это как простую консольную программу.

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;
        }
    }

Ответ 13

Будет ли это работать для вас?

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

Ответ 14

Вы можете добавить 1 к сгенерированному числу случайным образом, чтобы оно оставалось случайным, и охватывать целое число полного диапазона.

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;
    }
}

Ответ 15

Вы должны создать новый объект

var r=new Random();
r.Next(int.MinValue,int.MaxValue)