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




.net random (10)

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

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

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

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

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

ОБНОВИТЬ:

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


Вы можете добавить 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;
    }
}

Вы не можете использовать 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;
        }
    }

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

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

Насколько я понимаю, вы хотите, чтобы 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 .


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

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

Разделите диапазоны на две части и компенсируйте 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)

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

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
}

Я бы предложил использовать 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 });

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


Внутренняя реализация 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 ), поэтому я 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






random