Generador de números aleatorios solo generando un número aleatorio


Tengo la siguiente función:

//Function to get random number
public static int RandomNumber(int min, int max)
{
    Random random = new Random();
    return random.Next(min, max);
}

Cómo lo llamo:

byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
    mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);

Si paso ese bucle con el depurador durante el tiempo de ejecución obtengo diferentes valores (que es lo que quiero). Sin embargo, si pongo un punto de interrupción dos líneas debajo de ese código, todos los miembros de la matriz "mac" tienen el mismo valor.

¿Por qué sucede eso?

 662
Author: Taryn, 2009-04-20

7 answers

Cada vez que lo haces new Random() se inicializa usando el reloj. Esto significa que en un bucle apretado obtienes el mismo valor muchas veces. Debe mantener una única instancia Random y seguir usando Next en la misma instancia.

//Function to get a random number 
private static readonly Random random = new Random(); 
private static readonly object syncLock = new object(); 
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}

Editar (ver comentarios): ¿por qué necesitamos un lock aquí?

Básicamente, Next va a cambiar el estado interno de la instancia Random. Si hacemos eso al mismo tiempo desde múltiples hilos, podría argumentar " acabamos de hacer el resultado es aún más aleatorio", pero lo que estamos en realidadhaciendo es potencialmente romper la implementación interna, y también podríamos comenzar a obtener los mismos números de diferentes hilos, lo que podría ser un problema - y podría no. Sin embargo, la garantía de lo que sucede internamente es el problema más grande, ya que Random no hace ninguna garantía de seguridad del hilo. Por lo tanto, hay dos enfoques válidos:

  • sincronizar para que no accedamos a él en el mismo tiempo de diferentes hilos
  • use diferentes Random instancias por hilo

Cualquiera de los dos puede estar bien; pero mutexar una única instancia de múltiples llamantes al mismo tiempo es solo pedir problemas.

El lock logra el primero (y más simple) de estos enfoques; sin embargo, otro enfoque podría ser:

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

Esto es entonces por hilo, por lo que no es necesario sincronizar.

 904
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-29 06:45:03

Para facilitar la reutilización en toda la aplicación, una clase estática puede ayudar.

public static class StaticRandom
{
    private static int seed;

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

    static StaticRandom()
    {
        seed = Environment.TickCount;
    }

    public static Random Instance { get { return threadLocal.Value; } }
}

Puede usar luego usar instancia aleatoria estática con código como

StaticRandom.Instance.Next(1, 100);
 98
Author: Phil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-27 22:05:02

La solución de Mark puede ser bastante costosa ya que necesita sincronizarse cada vez.

Podemos evitar la necesidad de sincronización utilizando el patrón de almacenamiento específico del subproceso:


public class RandomNumber : IRandomNumber
{
    private static readonly Random Global = new Random();
    [ThreadStatic] private static Random _local;

    public int Next(int max)
    {
        var localBuffer = _local;
        if (localBuffer == null) 
        {
            int seed;
            lock(Global) seed = Global.Next();
            localBuffer = new Random(seed);
            _local = localBuffer;
        }
        return localBuffer.Next(max);
    }
}

Mida las dos implementaciones y verá una diferencia significativa.

 52
Author: Hans Malherbe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-23 11:26:10

Mi respuesta de aquí:

Simplemente reiterando la solución correcta:

namespace mySpace
{
    public static class Util
    {
        private static rnd = new Random();
        public static int GetRandom()
        {
            return rnd.Next();
        }
    }
}

Así que puedes llamar:

var i = Util.GetRandom();

Todo el tiempo.

Si necesita estrictamente un verdadero método estático sin estado para generar números aleatorios, puede confiar en un Guid.

public static class Util
{
    public static int GetRandom()
    {
        return Guid.NewGuid().GetHashCode();
    }
}

Va a ser un poco más lento, pero puede ser mucho más aleatorio que Random.Next, al menos por mi experiencia.

Pero no:

new Random(Guid.NewGuid().GetHashCode()).Next();

El la creación innecesaria de objetos lo hará más lento, especialmente bajo un bucle.

Y nunca :

new Random().Next();

No solo es más lento (dentro de un bucle), su aleatoriedad es... bueno, no muy bueno según mí..

 31
Author: nawfal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:07

Prefiero usar la siguiente clase para generar números aleatorios:

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);
 21
Author: fARcRY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-22 17:43:17

1) Como dijo Marc Gravell, intente usar UN generador aleatorio. Siempre es genial agregar esto al constructor: System.Ambiente.TickCount.

2) Un consejo. Supongamos que desea crear 100 objetos y suponga que cada uno de ellos debe tener su propio generador aleatorio (útil si calcula CARGAS de números aleatorios en un período de tiempo muy corto). Si hicieras esto en un bucle (generación de 100 objetos), podrías hacer esto así (para asegurar total aleatoriedad):

int inMyRandSeed;

for(int i=0;i<100;i++)
{
   inMyRandSeed = System.Environment.TickCount + i;
   .
   .
   .
   myNewObject = new MyNewObject(inMyRandSeed);  
   .
   .
   .
}

// Usage: Random m_rndGen = new Random(inMyRandSeed);

Saludos.

 12
Author: sabiland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-25 17:24:34

Hay muchas soluciones, aquí una: si solo desea número borrar las letras y el método recibe un azar y la longitud del resultado.

public String GenerateRandom(Random oRandom, int iLongitudPin)
{
    String sCharacters = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
    int iLength = sCharacters.Length;
    char cCharacter;
    int iLongitudNuevaCadena = iLongitudPin; 
    String sRandomResult = "";
    for (int i = 0; i < iLongitudNuevaCadena; i++)
    {
        cCharacter = sCharacters[oRandom.Next(iLength)];
        sRandomResult += cCharacter.ToString();
    }
    return (sRandomResult);
}
 1
Author: Marztres,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-04 05:13:31