Случайное число между 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)