Heute hat mir ein Kollege erzählt, dass die Random Klasse im .NET Framework keine vernünftigen Zufallszahlen erzeugen würde und dass zwei Instanzen der Random Klasse jeweils die gleichen Zufallszahlen erzeugen. Das entsprechende Beispiel hat er mir schnell anhand eines kleinen NUnit-Tests gezeigt, der in etwa so aussah:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var numbers1 = new List<int>(); var numbers2 = new List<int>(); var random1 = new Random(); for (int i = 0; i < RANDOMNUMBERS; i++) { numbers1.Add(random1.Next()); } var random2 = new Random(); for (int i = 0; i < RANDOMNUMBERS; i++) { numbers2.Add(random2.Next()); } |
Lässt man das Programm laufen und vergleicht die beiden erzeugten Listen stellt man fest, dass beide Listen die gleichen Zufallszahlen enthalten, was meinen Kollegen etwas überrascht hat. Der Grund ist dafür aber eigentlich recht einfach und auch in der Hilfe zum .NET Framework dokumentiert. Die Initialisierung des Pseudozufallszahlengenerator wird anhand der Systemzeit durchgeführt und da die Random Klasse nur einen recht einfachen Zufallszahlengenerator beinhaltet, liefern beide Klassen jetzt die gleichen Werte zurück.
Das Problem kann man auf unterschiedlichen Wegen lösen. Zum Einen kann man einfach nur ein Objekt verwenden, das die Pseudozufallszahlen zurückliefert. Benötigt man trotzdem zwei unterschiedliche Instanzen der Klasse Random könnte man mittels System.Environment.TickCount die Klassen unterschiedlich instantiieren, bspw. so:
1 2 |
var random1 = new Random(System.Environment.TickCount); var random2 = new Random(System.Environment.TickCount + 1); |
Eine letzte Möglichkeit ist es, einen Zufallszahlengenerator zu verwenden, der kryptographisch sichere Zufallszahlen erzeugt. So einen Zufallszahlengenerator bietet das .NET Framework unter System.Security.Cryptography.RandomNumberGenerator. Das Programm wird dadurch allerdings etwas aufwendiger, da ein solcher Zufallszahlengenerator immer in ein Byte-Array schreibt und man das Array dann erst in den benötigten Variablentyp umwandeln muss. Das Programm von oben würde unter Verwendung eines solchen Zufallszahlengenerator dann so aussehen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var byteBuffer = new byte[4]; var numbers1 = new List<int>(); var numbers2 = new List<int>(); var random1 = RandomNumberGenerator.Create(); for (int i = 0; i < RANDOMNUMBERS; i++) { random1.GetBytes(byteBuffer); numbers1.Add(BitConverter.ToInt32(byteBuffer, 0)); } var random2 = RandomNumberGenerator.Create(); for (int i = 0; i < RANDOMNUMBERS; i++) { random2.GetBytes(byteBuffer); numbers2.Add(BitConverter.ToInt32(byteBuffer, 0)); } |
Klingt doch eigentlich logisch, oder? Jedenfalls sollte man bei der Verwendung der Klasse Random etwas aufpassen und daran denken, dass bei Initialisierung mit gleichen Werten auch die gleichen Zufallszahlen zurückgegeben werden und sogar vorhersagbare Zufallszahlen zurückgeliefert werden (was in den meisten Fällen aber kein Problem sein sollte).