Generador de números aleatorios que solo genera un número aleatorio

Resuelto Ivan Prodanov asked hace 15 años • 15 respuestas

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

Como 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 valores diferentes (que es lo que quiero). Sin embargo, si coloco un punto de interrupción dos líneas debajo de ese código, todos los miembros de la macmatriz tienen el mismo valor.

¿Por qué sucede eso?

Ivan Prodanov avatar Apr 20 '09 19:04 Ivan Prodanov
Aceptado

Cada vez que lo haces new Random()se inicializa usando el reloj. Esto significa que en un circuito cerrado se obtiene el mismo valor muchas veces. Debes mantener una única instancia aleatoria 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 lockaquí?

Básicamente, Nextva a cambiar el estado interno de la Randominstancia. Si hacemos eso al mismo tiempo desde varios subprocesos, se podría argumentar "acabamos de hacer que el resultado sea aún más aleatorio", pero lo que en realidad estamos haciendo 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 ​​puede que no. Sin embargo, la garantía de lo que sucede internamente es el problema más importante; ya que Randomno ofrece ninguna garantía de seguridad de subprocesos. Por tanto, existen dos enfoques válidos:

  • Sincronizar para que no accedamos al mismo tiempo desde diferentes hilos
  • Utilice diferentes Randominstancias por hilo

Cualquiera de los dos puede estar bien; pero silenciar una sola instancia de varias personas que llaman al mismo tiempo es simplemente buscar problemas.

El locklogra 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 por subproceso, por lo que no es necesario sincronizar.

Marc Gravell avatar Apr 20 '2009 12:04 Marc Gravell

Para facilitar la reutilización en toda su aplicación, una clase estática puede resultar útil.

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 y luego usar una instancia aleatoria estática con código como

StaticRandom.Instance.Next(1, 100);
Phil avatar Jul 13 '2012 15:07 Phil

La solución de Mark puede resultar 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 debería ver una diferencia significativa.

Hans Malherbe avatar Jun 23 '2009 11:06 Hans Malherbe

Mi respuesta desde aquí :

Simplemente reiterando la solución correcta :

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

Entonces puedes llamar:

var i = Util.GetRandom();

todo a través de.

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

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

Será un poquito más lento, pero puede ser mucho más aleatorio que Random.Next, al menos según mi experiencia.

Pero no :

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

La creación innecesaria de objetos la hará más lenta, especialmente en un bucle.

Y nunca :

new Random().Next();

No solo es más lento (dentro de un bucle), su aleatoriedad es... bueno, no es muy buena en mi opinión...

nawfal avatar Mar 31 '2013 12:03 nawfal

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);
fARcRY avatar Apr 20 '2009 12:04 fARcRY