Generatore di numeri casuali che genera solo un numero casuale

Ho la seguente funzione:

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

Come lo chiamo:

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

Se passo quel ciclo con il debugger durante il runtime ottengo diversi valori (che è quello che voglio). Tuttavia, se inserisco un breakpoint di due righe sotto quel codice, tutti i membri dell’array “mac” hanno lo stesso valore.

Perché succede?

Ogni volta che fai un new Random() viene inizializzato usando l’orologio. Ciò significa che in un ciclo stretto si ottiene lo stesso valore un sacco di volte. È necessario mantenere una singola istanza Random e continuare a utilizzare Next nella stessa istanza.

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

Modifica (vedi commenti): perché abbiamo bisogno di un lock qui?

Fondamentalmente, Next sta per cambiare lo stato interno dell’istanza Random . Se lo facciamo allo stesso tempo da più thread, potresti dire “abbiamo reso il risultato ancora più casuale”, ma ciò che stiamo facendo in realtà sta potenzialmente rompere l’implementazione interna, e potremmo anche iniziare a ricevere gli stessi numeri da diversi thread, che potrebbero essere un problema, e potrebbe non esserlo. La garanzia di ciò che accade internamente è il problema più grande, però; poiché Random non fornisce alcuna garanzia di sicurezza del thread. Quindi ci sono due approcci validi:

  • sincronizzare in modo da non accedervi contemporaneamente da thread diversi
  • utilizzare diverse istanze Random per thread

O può andare bene; ma il muting di una singola istanza da più chiamanti allo stesso tempo richiede solo problemi.

Il lock raggiunge il primo (e più semplice) di questi approcci; tuttavia, un altro approccio potrebbe essere:

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

questo è quindi per-thread, quindi non è necessario sincronizzarsi.

Per facilitare il riutilizzo durante l’applicazione, una class statica può essere d’aiuto.

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

È ansible utilizzare quindi utilizzare un’istanza casuale statica con codice come

 StaticRandom.Instance.Next(1, 100); 

La soluzione di Mark può essere piuttosto costosa dal momento che deve essere sincronizzata ogni volta.

Possiamo ovviare alla necessità di sincronizzazione utilizzando il modello di archiviazione specifico del thread:

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

Misura le due implementazioni e dovresti vedere una differenza significativa.

La mia risposta da qui :

Semplicemente reiterando la soluzione giusta :

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

Quindi puoi chiamare:

 var i = Util.GetRandom(); 

tutto in tutto

Se hai bisogno di un vero metodo stateless stateless per generare numeri casuali, puoi fare affidamento su un Guid .

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

Sarà un po ‘più lento, ma può essere molto più casuale di Random.Next . Random.Next , almeno dalla mia esperienza.

Ma non :

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

La creazione di oggetti non necessari lo renderà più lento specialmente in un ciclo.

E mai :

 new Random().Next(); 

Non solo è più lento (all’interno di un loop), la sua casualità è … beh, non proprio buono secondo me ..

Preferirei usare la seguente class per generare numeri casuali:

 byte[] random; System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider(); prov.GetBytes(random); 

1) Come diceva Marc Gravell, prova ad usare UN generatore casuale. È sempre bello aggiungerlo al costruttore: System.Environment.TickCount.

2) Un consiglio. Supponiamo di voler creare 100 oggetti e supporre che ognuno di essi debba avere un proprio generatore casuale (utile se si calcolano CARICHI di numeri casuali in un brevissimo periodo di tempo). Se dovessi fare questo in un ciclo (generazione di 100 oggetti), potresti farlo in questo modo (per assicurare la completa casualità):

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

Saluti.

Ci sono molte soluzioni, qui una: se vuoi solo il numero cancella le lettere e il metodo riceve una casualità e la lunghezza del risultato.

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