Generador de números aleatorios que solo genera 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 ciclo con el depurador durante el tiempo de ejecución, obtengo valores diferentes (que es lo que quiero). Sin embargo, si pongo un punto de corte dos líneas debajo de ese código, todos los miembros de la matriz “mac” tienen el mismo valor.

¿Por qué sucede eso?

Cada vez que haces un new Random() se inicializa usando el reloj. Esto significa que, en un ciclo cerrado, obtienes el mismo valor muchas veces. Deberías mantener una sola 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íamos argumentar “acabamos de hacer que el resultado sea aún más aleatorio”, pero lo que realmente 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 ​​podría no serlo. Sin embargo, la garantía de lo que sucede internamente es el problema mayor; ya que Random no ofrece ninguna garantía de seguridad de hilos. Por lo tanto, hay dos enfoques válidos:

  • sincronizar para que no accedamos al mismo tiempo desde diferentes hilos
  • utilizar diferentes instancias Random por hilo

Cualquiera puede estar bien; pero silenciar una sola instancia de varias personas que llaman 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 appRandom = new ThreadLocal(() => new Random()); 

esto es por subproceso, por lo que no necesita sincronizar.

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

 public static class StaticRandom { private static int seed; private static ThreadLocal threadLocal = new ThreadLocal (() => 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 tal como

 StaticRandom.Instance.Next(1, 100); 

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

Podemos solucionar la necesidad de sincronización utilizando el patrón de almacenamiento específico de subprocesos:

 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.

Mi respuesta desde aquí :

Solo reiterando la solución correcta :

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

Entonces puedes llamar:

 var i = Util.GetRandom(); 

todo a través de.

Si estrictamente necesita 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 poquito más lento, pero puede ser mucho más aleatorio que Random.Next . Random.Next , al menos según mi experiencia.

Pero no

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

La creación de objetos innecesarios lo hará más lento, especialmente debajo de 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 yo …

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

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

2) Un consejo. Digamos 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 hacerlo así (para asegurar la aleatoriedad total):

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

Aclamaciones.

Hay muchas soluciones, aquí una: si solo quiere borrar números, las letras y el método reciben un resultado aleatorio 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); }