c# .net random

c# - Número aleatorio entre int.MinValue y int.MaxValue, inclusive



.net random (14)

Aquí hay un poco de rompecabezas: Random.Next() tiene una sobrecarga que acepta un valor mínimo y un valor máximo. Esta sobrecarga devuelve un número que es mayor o igual que el valor mínimo (inclusive) y menor que el valor máximo (exclusivo).

Me gustaría incluir todo el rango, incluido el valor máximo. En algunos casos, podría lograr esto simplemente agregando uno al valor máximo. Pero en este caso, el valor máximo puede ser int.MaxValue , y agregar uno a esto no lograría lo que quiero.

Entonces, ¿alguien sabe un buen truco para obtener un número aleatorio de int.MinValue a int.MaxValue , inclusive?

ACTUALIZAR:

Tenga en cuenta que el rango inferior puede ser int.MinValue pero también puede ser otra cosa. Si sé que siempre sería int.MinValue entonces el problema sería más simple.


¿Qué hay de esto?

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


Bueno, tengo un truco. No estoy seguro de describirlo como un "buen truco", pero siento que podría funcionar.

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

Entonces, básicamente, un método de extensión que simplemente generará cuatro bytes si el límite superior es int.MaxValue y se convierte en int , de lo contrario, simplemente use la sobrecarga estándar Next(int, int) .

Tenga en cuenta que si maxValue es int.MaxValue , ignorará minValue . Supongo que no tomé en cuenta eso ...


Divida los rangos en dos y compense el MaxValue :

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

Si hacemos los rangos de igual tamaño, podemos obtener el mismo resultado con diferentes matemáticas. Aquí confiamos en el hecho de que int.MinValue = -1 - int.MaxValue :

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


En realidad, es interesante que esta no sea la implementación de Random.Next (int, int), porque puede derivar el comportamiento exclusivo del comportamiento inclusivo, pero no al revés.

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


Este método puede darle un número entero aleatorio dentro de cualquier límite entero. Si el límite máximo es menor que int.MaxValue, entonces usa el Random.Next ordinario (Int32, Int32) pero al agregar 1 al límite superior para incluir su valor. Si no, pero con un límite inferior mayor que int.MinValue, reduce el límite inferior con 1 para cambiar el rango 1 menos que agrega 1 al resultado. Finalmente, si ambos límites son int.MinValue e int.MaxValue, genera un entero aleatorio ''a'' que es 0 o 1 con 50% de probabilidad de cada uno, luego genera otros dos enteros, el primero es entre int.MinValue y -1 inclusive, 2147483648 valores, y el segundo está entre 0 e int.MaxValue inclusive, 2147483648 valores también, y usándolos con el valor de ''a'' selecciona un número entero con probabilidad totalmente igual.

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


Esto está garantizado para trabajar con valores negativos y no negativos:

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 }


Funcionara para ti?

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


La implementación interna de Random.Next(int minValue, int maxValue) genera dos muestras para grandes rangos, como el rango entre Int32.MinValue e Int32.MaxValue . Para el método NextInclusive tuve que usar otro rango grande Next , totalizando cuatro muestras. Por lo tanto, el rendimiento debe ser comparable con la versión que llena un búfer con 4 bytes (una muestra por 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); } }

Algunos resultados:

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

Actualización: mi implementación tiene un rendimiento mediocre para el mayor rango posible ( Int32.MinValue - Int32.MaxValue ), por lo que se me ocurrió una nueva que es 4 veces más rápida. Produce alrededor de 22,000,000 números aleatorios por segundo en mi máquina. No creo que pueda ser más rápido que eso.

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

Algunos resultados:

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


No puede usar Random.Next() para lograr lo que desea, porque no puede corresponder la secuencia de N números a N+1 y no perder uno :). Período.

Pero puede usar Random.NextDouble() , que devuelve un resultado doble: 0 <= x < 1 aka [0, 1) entre 0, donde [ es signo inclusivo y ) exclusivo

¿Cómo correspondemos N números a [0, 1)? Debe dividir [0, 1) en N segmentos iguales: [0, 1/N) , [1/N, 2/N) , ... [N-1/N, 1)

Y aquí es donde se vuelve importante que un borde sea inclusivo y otro exclusivo: ¡todos los N segmentos son absolutamente iguales!

Aquí está mi código: lo hice como un simple programa de consola.

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


Puede agregar 1 al número generado aleatoriamente para que siga siendo aleatorio y cubra un entero de rango completo.

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


Puedes probar esto. Un poco hacky pero puede obtener ambos min y 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(); }


Según tengo entendido, desea que Random ponga un valor entre -2.147.483.648 y +2.147.483.647. Pero el problema es aleatorio dado que esos valores solo darán valores de -2.147.483.648 a +2.147.483.64 6 , ya que el máximo es exclusivo.

Opción 0: quítatelo y aprende a prescindir de él

Douglas Adams no era un programador AFAIK, pero tiene algunos buenos consejos para nosotros: "La tecnología involucrada en hacer que algo sea invisible es tan infinitamente compleja que novecientos noventa y nueve mil novecientos noventa y nueve millones novecientos noventa -nueve mil novecientos noventa y nueve veces de un billón es mucho más simple y efectivo simplemente quitar la cosa y prescindir de ella ".

Este podría ser un caso así.

Opción 1: ¡Necesitamos un Random más grande!

Random.Next usa Int32 como argumento. Una opción que puedo pensar sería usar una función aleatoria diferente que puede tomar el siguiente nivel más alto de enteros (Int64) como entrada. Un Int32 se convierte implícitamente en un Int64. Int64 Number = Int64(Int32.MaxValue)+1;

Pero afaik, tendrías que salir de las bibliotecas .NET para hacer esto. En ese punto, también podrías buscar un Aleatorio que incluya el Máx.

Pero creo que hay una razón matemática por la que tuvo que excluir un valor.

Opción 2: rodar más

Otra forma es usar dos llamadas de Aleatorio, cada una para la mitad del rango, y luego agregarlas.

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

Sin embargo, estoy 90% seguro de que arruinará la distribución de randon. Mi experiencia en P&P RPG me dio algo de experiencia con las posibilidades de dados y sé que, de hecho, tirar 2 dados (o las mismas 2 veces) te dará una distribución de resultados muy diferente a un dado específico. Si no necesita esta distribución aleatoria, esa es una opción. Pero si no te importa demasiado la distribución, vale la pena comprobarlo.

Opción 3: ¿necesita la gama completa? ¿O simplemente te interesa que min y max estén en él?

Supongo que está haciendo alguna forma de prueba y necesita que Int.MaxValue e Int.MinValue estén en el rango. ¿Pero también necesita todos los valores intermedios , o podría prescindir de uno de ellos? Si tiene que perder un valor, ¿preferiría perder 4 en lugar de Int.MaxValue?

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

un código como este le proporcionaría todos los números entre MinValueand y Maxvalue, excepto 4 . Pero en la mayoría de los casos, el código que puede manejar 3 y 5 también puede manejar 4. No hay necesidad de probar explícitamente 4.

Por supuesto que asumes 4 no es un número de prueba importante que deba ejecutarse (evité 1 y 0 por esas razones). También puede decidir el número para "omitir" aleatoriamente:

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

Y luego use > skipAbleNumber lugar de > 4 .


Sin conversión, sin long , se tienen en cuenta todos los casos límite, el mejor rendimiento.

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


Sugeriría usar System.Numerics.BigInteger esta manera:

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

Lo probé con 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 });

La distribución está bien.