2009-05-31 15 views
19

Estoy trabajando en Microsoft Visual C# 2008 Express.¿Por qué parece que mi generador de números aleatorios no es aleatorio en C#?

me encontré con este fragmento de código:

public static int RandomNumber(int min, int max) 
    { 
     Random random = new Random(); 

     return random.Next(min, max); 
    } 

el problema es que me he encontrado que más de 100 veces, y siempre me da la misma respuesta cuando mi min = 0 y max = 1. Recibo 0 cada vez. (Creé una función de prueba para ejecutarla, en realidad, obtengo 0 cada vez). Estoy teniendo dificultades para creer que es una coincidencia ... ¿hay algo más que pueda hacer para examinar o probar esto? (He vuelto a ejecutar la prueba con min = 0 y max = 10 y las primeras 50 veces, el resultado fue siempre "5", la segunda 50 veces, el resultado fue siempre "9".

?? I .. necesita algo un poco más consistente al azar ...

-Adeena

+16

Por alguna razón, creo que "consistentemente aleatorio" es realmente divertido. – jcollum

+0

Me gustaría agregar, dependiendo de la finalidad de su aplicación, que usar pseudoaleatorio siempre (al menos teóricamente) hará que su aplicación sea pirateable. –

Respuesta

47

El problema con min = 0 y max = 1 es que min es inclusivo y max es exclusivo. Así que el único valor posible para esa combinación es 0.

+0

Para hacer las cosas más confusas, la documentación indica que maxValue debe ser mayor igual o igual que minValue. Por lo tanto, puede pasar al azar. Siguiente (0,0) y funcionará exactamente igual que al azar. Luego (0,1) aunque no haya números que coincidan con los parámetros de entrada especificados. – Kevin

-2

en VB siempre comienzo con la función aleatorio() Sólo tiene que llamar aleatorio() y luego ejecutar su función aleatoria también hago lo siguiente:

Function RandomInt(ByVal lower As Integer, ByVal upper As Integer) As Integer 
    Return CInt(Int((upper - lower + 1) * Rnd() + lower)) 
End Function 

Espero que esto ayude! :)

+0

¿por qué está marcado? constantemente obtengo números aleatorios de esto ... – Jason

+1

Porque estamos discutiendo C# – TheSoftwareJedi

+1

¿eres real? ¿Sabes cuántas veces he obtenido respuestas de C# a preguntas de VB y pude averiguar qué estaba haciendo mal? es el concepto, hombre, no la sintaxis ... jeezz ... – Jason

32
random = new Random(); 

Esto inicia generador de números aleatorios con el tiempo actual (en seg). Cuando llama a su función muchas veces antes de que cambie el reloj del sistema, el generador de números aleatorios se inicia con el mismo valor, por lo que devuelve la misma secuencia de valores.

+5

La documentación para el constructor Random() describe este problema casi textualmente: http://msdn.microsoft.com/en-us/library/h343ddh9.aspx – las3rjock

7

que la sobrecarga de Siguiente() devuelve:

A 32 bits con signo número entero mayor que o igual a minValue y menos de maxValue; es decir, el rango de valores de retorno incluye minValue pero no MaxValue. Si minValue es igual a maxValue, se devuelve minValue.

0 es el único valor posible para que regrese. Quizás desee random.NextDouble(), que devolverá un doble entre 0 y 1.

18

No cree un método de envoltura para Next. Desperdicia ciclos creando una nueva instancia de la clase Aleatoria. Solo usa el mismo!

Random myRand = new Random(); 

for(int i = 0; i < 10; i++) 
{ 
    Console.WriteLine(myRand.Next(0, 10).ToString()); 
} 

Eso debería darle diez valores aleatorios.

Como se ha dicho: Random es pseudoaleatorio (como todas las implementaciones), y si crea 100 instancias con la misma inicialización, obtendrá 100 instancias de los mismos resultados. Asegúrate de reutilizar la clase.

Además, como han dicho las personas, tenga en cuenta que MinValue es inclusivo y MaxValue es exclusivo. Para lo que quieras, haz myRand.Next (0, 2).

+0

Hacerlo 10 o más veces fue solo la prueba ... No me di cuenta de que "max" era exclusivo. – adeena

+1

A veces querrás un método de contenedor (o más bien una clase de contenedor) para Aleatorio para que puedas simularlo. – BlackWasp

+0

@adeena, BlackWasp: si necesita sobrecargar/ajustar Next por cualquier razón, vaya por ello. Sin embargo, si busca obtener otro valor aleatorio, use Siguiente y use la misma clase Aleatoria. De esta forma, no corres demasiado riesgo de obtener un montón de Random con la misma semilla. – Eric

3

Además del problema 0-1 ya mencionado en otras respuestas, su problema es real cuando está buscando un rango de 0-10 y obtiene resultados idénticos 50 veces seguidas.

Se supone que new Random() devuelve un número aleatorio con una inicialización inicializada del temporizador (segundo actual), pero aparentemente está llamando a este código 50 veces por segundo. MSDN sugiere: "Para mejorar el rendimiento, crea un Aleatorio para generar muchos números aleatorios a lo largo del tiempo, en lugar de crear repetidamente un nuevo Random para generar un número aleatorio". Si crea su generador aleatorio una vez fuera del método, eso debería solucionar su problema de "no aleatoriedad" así como también mejorar el rendimiento.

También considere this post para un generador de números pseudoaleatorio mejor que el suministrado por el sistema, si necesita números pseudoaleatorios de "calidad superior".

1

Como otros han mencionado, el azar está construyendo varias veces por segundo utiliza el mismo segundo que la semilla, por lo que me había puesto el constructor aleatoria fuera de su bucle, y pasarlo como un parámetro, como esto:

public static int RandomNumber(Random random, int min, int max) 
{ 
    return random.Next(min, max); 
} 

También mencionado por otros, el máximo es exclusivo, por lo que si quiere un 0 o 1, debe usar [0,2] como su [mín., Máx.], O un máximo mayor y luego hacer un binario Y con 1.

public static int RandomOneOrZero(Random random) 
{ 
    return random.Next(0, int.MaxValue) & 1; 
} 
+1

El objeto Aleatorio también podría declararse como una variable estática, por lo que no es necesario pasarlo al método cada vez. – Whatsit

+0

Me gusta el método RandomOneOrZero. ¿Lo está usando porque sabe que es más aleatorio que usar [0,2] o por otro motivo? –

+0

@Matt: Sería la misma cantidad de aleatoriedad en cualquier momento. Acabo de azotarlo de la parte superior de mi cabeza. @Whatsit: Buen punto. No estaba seguro de cuál era su caso de uso, así que lo adiviné. Una variable estática o de instancia probablemente sea más apropiada. –

6

Siempre obtiene 0 porque Random.Next devuelve enteros. Es necesario llamar Random.NextDouble, que devolverá un número entre 0 y 1. Además, debe reutilizar la instancia aleatoria, así:

[ThreadStatic] 
static Random random; 
public static Random Random { 
    get { 
     if (random == null) random = new Random(); 
     return random; 
    } 
} 
public static int RandomInteger(int min, int max) 
{ 
    return Random.Next(min, max); 
} 
public static double RandomDouble() //Between 0 and 1 
{ 
    return Random.NextDouble(); 
} 

Si desea números aleatorios criptográficamente seguros, utilice la clase RNGCryptoServiceProvider; ver this article

EDIT: Seguridad de los hilos

+2

-1 porque este código es peligrosamente incorrecto. Random no es seguro para subprocesos y está accediendo a una instancia compartida sin proporcionar ningún bloqueo o una instancia por subproceso. –

+0

Solucionado; gracias por el consejo – SLaks

+0

Cool, -1 eliminado. –

1

Ésta es una adición a las respuestas, como la respuesta a esta pregunta es específica de los límites deben ser (0, 2) No (0, 1).

Sin embargo, si desea utilizar un método de envoltura estático, debe recordar que Random no es seguro para subprocesos, por lo que debe proporcionar su propio mecanismo de sincronización o proporcionar una instancia por subproceso. Aquí es una implementación en gran medida sin bloqueo que utiliza un generador para sembrar cada generador para cada subproceso:

public static class ThreadSafeRandom 
{ 
    private static readonly Random seed = new Random(); 

    [ThreadStatic] 
    private static Random random; 

    public static int Next(int min, int max) 
    { 
     if (random == null) 
     { 
      lock (seed) 
      { 
       random = new Random(seed.Next()); 
      } 
     } 

     return random.Next(min, max); 
    } 

    // etc. for other members 
} 
+0

Environment.TickCount (la semilla predeterminada) es lo suficientemente bueno para una semilla; no tiene sentido hacer una instancia semilla separada. – SLaks

+1

@SLaks - Si confía en Environment.TickCount, posiblemente obtenga la misma semilla para cada instancia por subproceso, lo que hará que produzcan la misma secuencia de valores pseudoaleatorios. Dudo mucho que esto sea lo que quieres. Al darles una semilla explícitamente diferente, evita que ocurra este patrón de comportamiento. –

0

Varios críticos han señalado que random() emplea una semilla en base a la segunda corriente en el reloj del sistema y cualquier otra instancia de Random creado en el mismo segundo tendrá la misma semilla. Esto es incorrecto. La semilla para el constructor sin parámetros de Random se basa en el recuento de ticks, o número de milisegundos desde el momento del inicio. Este valor se actualiza en la mayoría de los sistemas aproximadamente cada 15 milisegundos, pero puede variar según el hardware y la configuración del sistema.

0

he encontrado una manera muy simple, pero eficaz para generar números aleatorios con sólo tomar los dos últimos dígitos de los milisegundos de fecha y hora actuales:

int seed = Convert.ToInt32(DateTime.Now.Millisecond.ToString().Substring(1, 2)); 
    int cnr = new Random(seed).Next(100); 

Es crudo, pero funciona! :-)

Por supuesto generaría estadísticamente el mismo número cada cien veces. De forma alternativa, puede tomar los tres dígitos o concatenar con otros valores de fecha y hora como segundos aproximadamente.

1

Está mal entendiendo la línea "al azar. Siguiente (mínimo, máximo)"."min" está en el lugar del número más bajo permitido generar al azar. Mientras que "max" está en el lugar del número más bajo que NO se permite generar, no está en el lugar donde se dibujó el número más grande permitido. Entonces, cuando la línea es aleatoria. Luego (0, 1) básicamente solo permite que se dibuje 0.

Cuestiones relacionadas