2011-08-31 17 views
5

sé que la clase Random C# no hace "verdaderos azar" números, pero estoy subiendo con un problema con este código:Números C# azar no están siendo "aleatoria"

public void autoAttack(enemy theEnemy) 
    { 
     //Gets the random number 
     float damage = randomNumber((int)(strength * 1.5), (int)(strength * 2.5)); 

     //Reduces the damage by the enemy's armor 
     damage *= (100/(100 + theEnemy.armor)); 

     //Tells the user how much damage they did 
     Console.WriteLine("You attack the enemy for {0} damage", (int)damage); 

     //Deals the actual damage 
     theEnemy.health -= (int)damage; 

     //Tells the user how much health the enemy has left 
     Console.WriteLine("The enemy has {0} health left", theEnemy.health); 
    } 

I a continuación, llamar a la función aquí (me llamó 5 veces en aras de comprobar si los números eran al azar):

 if (thePlayer.input == "fight") 
     { 
      Console.WriteLine("you want to fight"); 
      thePlayer.autoAttack(enemy1); 
      thePlayer.autoAttack(enemy1); 
      thePlayer.autoAttack(enemy1); 
     } 

Sin embargo, al comprobar la salida, me da exactamente el mismo número por cada 3 llamadas a funciones. Sin embargo, cada vez que ejecute el programa, aparece un número diferente (que se repite 3 veces) así:

You attack the enemy for 30 damage. 
The enemy has 70 health left. 

You attack the enemy for 30 damage. 
The enemy has 40 health left. 

You attack the enemy for 30 damage. 
The enemy has 10 health left. 

haré luego reconstruir/debug/a ejecutar el programa, y ​​obtener un número diferente en lugar de 30 , pero repetirá las 3 veces.

Mi pregunta es: ¿cómo puedo asegurarme de obtener un número aleatorio diferente cada vez que llamo a esta función? Solo estoy obteniendo el mismo número "aleatorio" una y otra vez.

Aquí es la llamada clase al azar que he utilizado:

private int randomNumber(int min, int max) 
    { 
     Random random = new Random(); 
     return random.Next(min, max); 
    } 
+4

¿Cómo es tu función 'randomNumber'? – Nija

+0

Es posible que desee consultar [esta publicación] (http://www.codeducky.org/random-numbers-c-net-primer/), que trata este problema, así como otros problemas con la clase .NET Random – ChaseMedallion

Respuesta

26

Mi conjetura es que randomNumber crea una nueva instancia de Random ... cada vez que a su vez crea un nuevo generador de números pseudo-aleatorios basados ​​en la hora actual ... que no cambia tan a menudo como crees.

No hagas eso. Use la misma instancia de Random repetidamente ... pero no lo haga "soluciónelo" creando una variable estática Random. Eso tampoco funcionará bien a largo plazo, ya que Random no es seguro para subprocesos. Todo se verá bien en las pruebas, y misteriosamente recuperarás todos los ceros cuando tengas mala suerte con la simultaneidad :(

Afortunadamente, no es demasiado difícil conseguir algo que funcione con thread-locals, especialmente si eres en .NET 4. se termina con una nueva instancia de Random por hilo

he escrito un article on this very topic que pueden serle útil, incluyendo este código:.

using System; 
using System.Threading; 

public static class RandomProvider 
{  
    private static int seed = Environment.TickCount; 

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random> 
     (() => new Random(Interlocked.Increment(ref seed))); 

    public static Random GetThreadRandom() 
    { 
     return randomWrapper.Value; 
    } 
} 

Si cambia su llamada new Random() a RandomProvider.GetThreadRandom() que probablemente hará todo lo que necesites ed (de nuevo, suponiendo .NET 4). Eso no se ocupa de la capacidad de prueba, pero un paso a la vez ...

+0

¿Cómo se puede modificar esto para usar un valor mínimo y máximo como en la pregunta? – Julien

0

¿Qué es randomNumber?

Normalmente se siembra un generador de números pseudoaleatorios (con una cosa relacionada con el tiempo, o algo aleatorio como un tiempo entre dos pulsaciones de tecla o paquetes de red o algo así).

No indica qué generador está utilizando ni cómo se siembra.

+0

Agregué la clase de número aleatorio que utilicé a mi publicación original – Mento

7

No nos mostró el código para randomNumber. Si se ve algo como

private int randomNumber(int m, int n) { 
    Random rg = new Random(); 
    int y = rg.Next(); 
    int z = // some calculations using m and n 
    return z; 
} 

Bueno, entonces está su problema.Si sigues creando nuevas instancias de Random, es posible que a veces tengan la misma semilla (la semilla predeterminada es el reloj del sistema que tiene una precisión limitada, créalas lo suficientemente rápido y obtienen la misma semilla) y luego la secuencia producida por este generador siempre será el mismo.

Para solucionar esto, se tiene que crear una instancia de Random vez:

private readonly Random rg = new Random(); 
private int randomNumber(int m, int n) { 
    int y = this.rg.Next(); 
    int z = // some calculations using m and n 
    return z; 
} 

Y para aclarar otro punto, incluso si lo hace, la salida de Random todavía no es "verdadero" al azar. Es solo psuedorandom.

+1

+1 para el ejemplo. Sin embargo, es más probable que * casi siempre * arroje los mismos valores si se invoca en una sucesión muy corta .... ;-) "El valor inicial predeterminado se deriva del reloj del sistema y tiene una resolución finita." –

+0

@pst, no, no produce el mismo valor para eso. El 'seed' (valor inicial) depende del reloj, pero su estado interno cambia cuando lo usa. –

+0

@ J-16 SDiZ: Creo que lo que pst significa es que si tienes un método que crea una nueva instancia de 'Aleatorio' y devuelve un único valor aleatorio, e invocas ese método continuamente en un ciclo, verás el mismo el valor se repite varias veces ya que la semilla no cambia, y luego se repite otro valor varias veces a medida que la semilla ha cambiado desde el cambio de reloj, y así sucesivamente. – jason

0

si genera números aleatorios en el bucle, probablemente no sea aleatorio. porque los números aleatorios se crean básicamente internamente en la hora actual del sistema. Así coloque este código en el bucle:

Thread.Sleep(10); 

Así que el sistema se irá a dormir durante 10 m seg. Y obtendrás un nuevo número aleatorio nuevo. Es una solución garantizada. Pero esto también afectará el rendimiento del sistema.

+0

Durante mi confusión, usar Sleep fue mi solución aproximada cuando seguí obteniendo 2-3 valores repetidos y estaba desconcertado. Por cierto, todavía recibía repeticiones en (100). En (500), parecía aleatorio. Pero sí, el rendimiento es obvio. @ La respuesta de Jason anterior es una buena solución. – nanonerd

0

Crea una instancia del objeto al azar fuera del método. (Aleatorio aleatorio = nuevo Aleatorio(); debe escribirse antes del método)

También es vital que entienda que al azar no es really random.

Cuestiones relacionadas