Generatore di numeri casuali che genera gli stessi numeri ogni volta che viene eseguita l’applicazione

So che ci sono più volte che questa domanda è stata presentata ma nessuna di quelle soluzioni ha funzionato per me.

Per prima cosa ho fatto questo nel mio metodo chiamato RandomNumGenerator(items)

 List randNum = new List(); foreach (var item in items) { randNum.Add(new Random(1000).Next()); } 

Questo mi ha sempre dato lo stesso numero, e dopo aver visto questa risposta ho fatto questo:

 Random rnd = new Random(1000); foreach (var item in items) { randNum.Add(rnd.Next()); } 

Questo mi ha dato i numeri come sotto

 325467165 506683626 1623525913 2344573 1485571032 

Ora, mentre ciò va bene per ogni iterazione del ciclo, il problema qui è che, quando mi fermo e rieseguo l’applicazione, ottengo gli stessi numeri che ho avuto prima tutto da capo.

 325467165 506683626 1623525913 2344573 1485571032 

Questo comportamento si verifica solo durante il debug o avrò lo stesso problema ogni volta che chiamerò RandomNumGenerator ?

Stai seminando l’istanza Random sempre con lo stesso seme 1000 qui:

 Random rnd = new Random(1000); 

questo non lo farà poiché l’ora corrente è usata come seme:

 Random rnd = new Random(); 

Dai un’occhiata al costruttore che prende un int .

Fornendo un valore di seme identico a diversi oggetti Random, ciascuna istanza produce sequenze identiche di numeri casuali.

Come da MSDN .

 public Random( int Seed ) 

seme

Un numero usato per calcolare un valore iniziale per la sequenza numerica pseudo-casuale. Se viene specificato un numero negativo, viene utilizzato il valore assoluto del numero.

La ragione per la maggior parte degli errori dei principianti che coinvolgono gli RNG (generatori di numeri casuali) è la mancanza di comprensione di cosa sia il “seme” e di cosa faccia.


Quindi cos’è un “seme”?

La class Random è una class per generare numeri pseudo-casuali o numeri che sembrano casuali. Di solito sono una funzione matematica, che utilizza un parametro – il “seme” – per generare una sequenza di numeri che sembrano essere casuali.

Nel caso del new Random(1000) , i primi 5 numeri interi non negativi sono

325467165
506683626
1623525913
2344573
1485571032

Nel tuo primo codice, crei una nuova sequenza di numeri pseudo-casuali con lo stesso seme ogni volta che hai bisogno di un numero casuale, quindi ovviamente il tuo array è riempito con lo stesso numero: 325467165 , che risulta essere il primo intero non negativo generato da new Random(1000) .

Questo spiega anche perché il tuo secondo codice genera sempre la stessa sequenza di numeri pseudo-casuali ogni volta che viene lanciata la tua applicazione.

Per garantire che la tua app generi sempre sequenze pseudo-casuali diverse, devi utilizzare ogni volta un seme diverso. Di gran lunga il modo più semplice per garantire che , è quello di prendere il tempo , letteralmente.

 Random rnd = new Random(DateTime.UtcNow.Millisecond); // Taking the millisecond component, because it changes quickly 

Fortunatamente, non è necessario digitare molto, perché il costruttore predefinito per la class Random fa già qualcosa di simile.

 Random rnd = new Random(); // Much simpler, isn't it? 

Tieni presente che la class Random non è thread-safe; se più thread tentano di accedere contemporaneamente allo stesso object Random , il tuo RNG restituirà solo 0 per il resto della sua durata.

Un’altra cosa da notare, è che la creazione di più oggetti Random uno dopo l’altro – anche quando si usa il tempo come seme – può portare alla stessa sequenza di numeri pseudo-casuali.

 Random r1 = new Random(); Random r2 = new Random(); Random r3 = new Random(); Random r4 = new Random(); 

Nel codice precedente, le probabilità sono molto alte , che r1 , r2 , r3 e r4 genereranno tutti la stessa sequenza.

Come è ansible?
Bene, (un) per fortuna, le CPU sono incredibilmente veloci. Una CPU da 1 GHz può eseguire circa 1 miliardo di istruzioni al secondo (dare o avere); questa è 1 istruzione ogni 1 nanosecondo o 1 istruzione ogni 1 milionesimo di millisecondo.
Creare un nuovo object Random potrebbe richiedere un sacco di istruzioni, ma sicuramente meno di un milione di esse.


Quindi, perché abbiamo bisogno di definire manualmente un seme, se l’attuale conteggio del millisecondo dell’orologio è ciò che tutti “vogliamo” ed è già il valore predefinito?

Perché può essere molto utile per mantenere sincronizzati più terminali.

Immagina un gioco, in cui appaiono in modo casuale fenomeni importanti, come un cambiamento del tempo che potrebbe stravolgere completamente il gioco. Non vorresti che solo una parte soffrisse di nebbia, mentre il resto beneficia ancora del tempo sereno, giusto?

Ovviamente, potresti avere il server o l’host generare cambiamenti meteorologici casuali e informare i giocatori su di esso; oppure potresti definire un seme prima dell’inizio del gioco, e usare quel seme per assicurare la stessa “casualità” tra tutti i giocatori durante il gioco.

Non è divertente la programmazione?

Devi cambiare questo:

 Random rnd = new Random(1000); 

a

 Random rnd = new Random(); 

Dai documenti di Random Constructor :

Il valore di inizializzazione predefinito deriva dall’orologio di sistema e ha una risoluzione finita. Di conseguenza, diversi oggetti casuali creati in stretta successione da una chiamata al costruttore predefinito avranno identici valori di inizializzazione predefiniti e, pertanto, genereranno serie identiche di numeri casuali. Questo problema può essere evitato usando un singolo object casuale per generare tutti i numeri casuali. È inoltre ansible aggirare il problema modificando il valore di inizializzazione restituito dall’orologio di sistema e quindi fornendo esplicitamente questo nuovo valore di inizializzazione al costruttore Random (Int32). Per ulteriori informazioni, consultare il costruttore Random (Int32).

Il concetto chiave è seme casuale: il primo dato da cui il Random deriva tutto il resto. Se il seme è lo stesso, la sequenza “casuale” sarà la stessa.

Di default il seme è impostato su zero, il che ovviamente porta a ripetere sequenze tra le esecuzioni del programma.

Per evitare ciò, puoi build il tuo casuale in questo modo:

 Random rnd = new Random(); 

… che è, sotto il cofano, è:

 Random rnd = new Random(Environment.TickCount); 

Questo avvierà l’object casuale con una quantità di millisecondi dall’inizio del sistema operativo. Questo sarà diverso ogni volta che inizia il tuo programma, così avrai ogni volta sequenze casuali diverse.

Il metodo .Next () casuale genera un numero pseudo-casuale. Dovresti dichiarare e inizializzare un object casuale invece di creare ogni volta un nuovo object. E non c’è bisogno di usare alcuna Cryctography .. 🙂

Dovresti usare una variabile casuale a livello di class. Se hai usato un nuovo casuale a livello di metodo come locale, il seme dipendente dal tempo si ripeterà generando una sequenza identica di numeri casuali.

 class Program { static Random _r = new Random(); static void Main() { // use _r variable to generate random number } }