Zur Frage warum Random nur einmal instanziiert werden darf

  • C#

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von exc-jdbi.

    Zur Frage warum Random nur einmal instanziiert werden darf

    Hallo,

    ich bin hier im Forum über einen Beitrag gestoßen, in dem es um die Erzeugung von Zufallszahlen mit Random() ging. Das Problem des Fragenstellers war, dass Random immer die gleiche Zahl geliefert hat. Verschuldet wurde dies, weil Random rnd = new Random(); in dem Aufruf immer wieder instanziiert wurde. Erst nachdem man Random() ... aus dem Aufruf z.b. einer For Schleife herausgenommen hat, wurden unterschiedliche Zufallszahlen erzeugt.

    Letztlich geht es ja bei Random rnd = new Random(); um die Erzeugung eines neuen Objektes, daher würde ich von dem Standpunkt aus argumentieren, dass es egal sein sollte ob ich das jetzt in eine For Schleife packe oder nicht. In der Praxis scheint aber nur der Aufruf außerhalb einer For Schleife zu funktionieren. Meine Frage ist daher, warum ist das so?
    Die Sache ist, dass man nicht zu schnell instanziieren darf:

    VB.NET-Quellcode

    1. For i = 1 To 10
    2. Dim Random = New Random()
    3. ListBox1.Items.Add(Random.NextDouble().ToString)
    4. 'Threading.Thread.Sleep(10)
    5. Next

    Ohne aktivierte Zeile#4 kommen meist die gleichen Zahlen raus, weil in Z#2 eine Randominstanz mit dem Seed »Millisekunden nach Mitternacht« (oder so ähnlich) erstellt wird. Und davon ausgehend wird in Z#3 eine berechnete Zufallszahl ermittelt. Da die heutigen PCs recht flink sind (zzgl. Codeoptimierung durch den Compiler, vermute ich), gehen die 10 Durchgänge sehr schnell, es wird also immer eine neue Randominstanz mit dem selben Seed erstellt. Und die daraufhin erstellte/berechnete Zufallszahl ist deshalb gleich. Aktiviert man aber Z#4, kommen andere Seeds raus (da mehr Zeit seit Mitternacht vergangen ist). Und dann kommen andere Erstzufallszahlen raus.
    Zieht man aber Z#2 aus der For-Schleife, werden in Z#3 nicht mehr immer Erstzufallszahlen berechnet, sondern eben die 1., dann die 2, …; also kommen da definitiv immer andere Zufallszahlen raus.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VB.neter0101 schrieb:

    Meine Frage ist daher, warum ist das so?
    Diese eine Instanz garantiert die Zufälligkeit der resultierenden Zahlenfolge.
    Zwei Random-Instanzen liefern (ggf.) zueinander nicht ganz zufällige Sequenzen.
    Die Frage, ob genau eine oder mehrere Instanzen von Random musst Du dahingehend klären, ob die beiden oder die mehreren Verläufe, die sie beschreiben, voneinander unabhängig sind oder nicht.
    Sind sie voneinander abhängig, solltest Du nur eine Instanz nehmen.
    Mehrere Instanzen können sinnvoll sein, wenn in mehreren Threads Zufallszahlen benötigt werden, denn die Random-Klasse ist nicht Thread-save.
    Wenn Du das Abholen der Werte lockst, kommst Du zu einer relativ hohen Lock-bedingten Verzögerung.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Deine Ausführungen finde ich stimmig, denn ich habe zuvor mit dem Debugger gearbeitet und mich dann via F10 durch ein von mir geschriebenes Beispiel Programm debuggt; und ich konnte nicht feststellen, dass gleiche Zahlen erzeugt werden! Verzichtet man aber auf die Einzelschritte, dann erscheint tatsächlich die gleiche Zahl! Schon interessant! Ansonsten ist Random ... eben außerhalb einer Schliefe zu definieren. Kann man davon ausgehen, dass solche (ich nenne es mal) Bugs behoben werden, weil ich finde, man muss sich schon mit Random auseinander setzen, um sowas zu sehen. Und den Code künstlich zu bremsen ist gut, aber irgendwie auch schade...
    Das ganze ist kein Bug sondern absichtlich so implementiert und hier auch gut dokumentiert: docs.microsoft.com/en-us/dotne…e-random-number-generator

    However, on the .NET Framework only, because the clock has finite resolution, using the parameterless constructor to create different Random objects in close succession creates random number generators that produce identical sequences of random numbers. [...] To avoid this problem, create a single Random object instead of multiple objects.


    Sogar mit Codebeispiel. Aber auch der Hinweis, dass das in .NET-Core nicht mehr so ist:
    Note that the Random class in .NET Core does not have this limitation.

    Twitch Viewer Display Chat-, Zuschauer- und Statistiktool für Streamer
    Soviel es mir recht ist, wird bei der parameterlosen Instanzierung von Random Environment.TickCount verwendet, was die Millisekunden anzeigt seit Beginn des Computerstarts.

    In 1 Millisekunde können mehrere Random-Instanzen erstellt werden, was soviel bedeutet, dass alle diese Instanzen den gleichen Seedwert besitzen. Somit geben auch alle Instanzen die gleichen Zahlen aus. Da nützt es auch nichts wenn ein Thread.Sleep verwendet wird, ausser Thread.Sleep wurde vor der Random Instanziierung verwendet.

    Wie Random in .NET-Core aufgebaut ist, weiss ich nicht. Aber ich vermute schwer, dass auch dort das gleiche Verhalten besteht.

    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „exc-jdbi“ ()

    exc-jdbi schrieb:

    Wie Random in .NET-Core aufgebaut ist, weiss ich nicht. Aber ich vermute schwer, dass auch dort das gleiche Verhalten besteht.


    Ne, wie newcat schon geschrieben hat:
    .Net Framework:

    C#-Quellcode

    1. public Random() : this(Environment.TickCount) {
    2. }
    Environment.TickCount hat auf den meisten Rechner, wie z.B. bei mir auch, gerade mal eine Auflösung von 16ms.

    .Net Core:

    C#-Quellcode

    1. public Random() : this(GenerateSeed()) {
    2. }
    3. private static int GenerateSeed()
    4. {
    5. Random? rnd = t_threadRandom;
    6. if (rnd == null)
    7. {
    8. int seed;
    9. lock (s_globalRandom)
    10. {
    11. seed = s_globalRandom.Next();
    12. }
    13. rnd = new Random(seed);
    14. t_threadRandom = rnd;
    15. }
    16. return rnd.Next();
    17. }

    exc-jdbi schrieb:

    und stellt immer sicher, dass nie der gleiche Seed genommen werden kann.
    Das ist aber eine sehr schlechte Eigenschaft, wenn das tatsächlich so ist.
    Wenn Du Testcode schreibst, in dem Zufallszahlen verwendet werden, solltest Du sicherstellen, dass identische Zufallssequenzen verwendet werden (können).
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!