Random errechnet nur mehr den Wert 0 nach sehr vielen Berechnungen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 22 Antworten in diesem Thema. Der letzte Beitrag () ist von Suter.

    Random errechnet nur mehr den Wert 0 nach sehr vielen Berechnungen

    Hi Leute,

    bin gerade auf ein sehr schwerwiegendes Problem gestoßen, habe mir gerade ein Kartenspiel programmiert, in dem eine Variable als Random deklariert ist und mithilfe dieser immer wieder ein Zufallswert erzeugt wird.
    Dies funktioniert prinzipiell auch ohne Probleme, zu Testzwecken habe nun jedoch einen Dauertestlauf hinzugefügt und nun zum großen Problem:

    Nach sehr vielen generierten Zahlen mit der gleichen Random Variable, bekommt man egal ob mit .Next(minWert, maxWert), nur mit .Next() oder anderen Varianten immer nur mehr 0 bzw. den minWert selbst zurück (Kommt nach ca. 50000000 Berechneten Zahlen vor)
    Dieser Fehler verschwindet auch nicht mehr sobald man einmal in diesem Fehlerzustand ist, egal wie oft man mit dieser Random-Variable rechnet.

    Hat jemand eine Idee woher dieses Problem kommen könnte?

    MfG, Suter

    VB.NET-Quellcode

    1. Public Function GetNextCard(ByRef tempRandom As Random)
    2. Dim tempCardIndex As Short = tempRandom.Next(0, totalCount)


    Die Random-Variable die übergeben wird, ist immer dieselbe und wird beim Programmstart initialisiert und ansonsten nie aufgerufen, auser in dieser Funktion

    EDIT: totalCount ist nicht 0 bzw. 1 sondern immer > 200!, falls das jemand gleich vermuten sollte :P
    Auch tempRandom.Next(0, 99999999) ergibt immer 0, sobald der Fehlerzustand erreicht ist, genauso wie tempRandom.Next() immer 0 ergibt
    Und wie gesagt, es geht einige Millionen mal korrekt, bis es eben in diesen Fehlerzustand kommt :(

    MfG Suter
    Wenn du schon eine Function verwendest, dann lass das ByRef beim Random-Objekt weg und füge einen Return-Wert hinzu.

    Zu deinem Problem: Short ist ein Ganzzahl-Datentyp (kann 0,9 oder kleiner ned speichern) nimm double oder float her
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    ByRef sowie Short sind aus Performance-Gründen so drin, natürlich ist es Ganzzahl, ich will ja auch nicht die 0,5te Karte aus dem Stapel ziehen^^

    Random.Next(minValue, maxValue) gibt ja auch Ganzzahlen zurück ;)

    Function gibt mir die gezogene Karte zurück
    @Suter Gib dieser Klasse eine Klassen-Variable vom Typ Random und greif dann einfach drauf zu.
    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!
    Geht in diesem Falle nicht, ich müsste ja die Random-Variable instanzieren, jedoch rufe ich die Klasse öfters auf (Es werden viele Decks gleichzeitig gespielt um schnellere Testergebnisse des Dauerlaufs zu erhalten).
    Hierbei ist dann der Fall, das zwei Klassen fast zeitgleich instanziert werden, was zum Fehler führt, dass die instanzierten Random-Variablen auch den gleich Startwert und somit auch die komplett gleichen folgenden Werte haben.
    Darum auch eine Random-Variable, die bei Programmstart instanziert und per Ref immer übergeben wird, um gleiche Szenarien zu verhindern

    Suter schrieb:

    ich müsste ja die Random-Variable instanzieren
    Genau ein Mal!
    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!
    @yeti2078

    Ok, Problem nur dass die Thread auch wieder zeitgleich starten, somit diese Random-Variablen für jeden Thread auch wieder den gleichen Startwert (gleich Zeit) hätten, aber auch wenn Random nicht threadsafe ist, was passiert mit Random damit es in diesen Zustand kommt?
    Ich meine, danach ist die Variable zum wegwerfen, ab dann hilft es auch nichts, auch wenn keine Threads laufen die darauf zugreifen, einen Wert in der main-class über diese Variable zu berechnen, es kommt immer 0, bzw. der minWert.

    Trotzdem schonmal danke für die Antwort

    @RodFromGermany
    Einaml Pro Aufruf der Klasse, wenn ich jetzt mehrere Instanzen dieser Klasse (Eine Klasse simuliert immer ein komplettes "Spiel" bzw. einen gespielten Kartenstapel) erzeuge, habe ich wieder das Problem, dass auch die Random mit dem gleichen Startwert initialisiert werden und somit immer komplett gleiche Spiele die folge sind

    Suter schrieb:

    Problem
    Dann pack die eine einzige Random-Instanz eine Klasse höher.
    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!
    Du kannst statt Random das ThreadSafeRandom von hier benutzen: stackoverflow.com/a/11109361/2715732

    Und füge dann die Next-Funktionen noch hinzu die du brauchst, zB.:

    VB.NET-Quellcode

    1. Public Function Next(maxValue As Integer) As Integer
    2. Return _local.Next(maxValue)
    3. End Function
    4. Public Function Next(minValue As Integer, maxValue As Integer) As Integer
    5. Return _local.Next(minValue, maxValue)
    6. End Function
    @RodFromGermany
    Ähm... genau das hab ich ja auch, darum wird ja auch ein Random als Referenz in die Funktion dieser Klasse übergeben, sonst wäre das mit dem übergeben von Random ja totaler Schwachsinn (Eine Klasse darüber ruft die Funktion einer Instanz dieser Klasse auf und übergibt eben diese eine Random Variable immer als Referenz)^^

    Habs auch versucht die Referenz beim Instanzieren der Klasse zu übergeben, brachte aber das gleich Ergebenis :(

    Nur nochmal zur Verdeutlichung: Ich kenne genug Alternativmöglichkeiten um dieses Problem zu umgehen, jedoch würde ich gerne wissen woher so ein Problem kommen könnte und ob man es prinzipiell verhindern kann, ohne drumherum zu programmieren!

    Also bitte nur Antworten zu diesem Problem geben, danke.


    @yeti2078
    Vielen Dank, sieht genau nach dem aus was hier der Fall ist

    Wobei diese Lösung eigentlich genau der Alternativmöglichkeit entspricht die ich mir bereits als Notfalllösung angedacht hatte.

    Wusste gar nicht dass so schwere Fehler passieren können, wenn eine Random-Variable in mehreren Threads aufgerufen wird, dann muss ich wohl einfach damit Leben dass es anders nicht geht ;(

    Vielen Dank an alle Antwortgeber!

    MfG Suter

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Suter“ ()

    Nicht

    Suter schrieb:

    als Referenz
    , sondern ByVal, wenn schon.
    Du kannst die Random-Variable auch Shared machen, da musst Du sie nicht übergeben.
    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!
    ich kanns Problem nicht nachstellen, also kann der Fehler wohl überall liegen.

    Falls es tatsächlich am Threading liegen sollte, habich hier eine threadsichere Random-klasse gebastelt:

    VB.NET-Quellcode

    1. Public Class ThreadsaveIntegerRandom : Inherits Random
    2. Public Overrides Function [Next](minValue As Integer, maxValue As Integer) As Integer
    3. SyncLock Me
    4. Return MyBase.[Next](minValue, maxValue)
    5. End SyncLock
    6. End Function
    7. End Class
    Zumindest die .Next(minValue, maxValue) - Methode ist threadsafe - die anderen Methoden wären gleichartig zu überschreiben.

    Ein anderes Thema ist, dass ich mir garnet vorstellen kann, wie ein Kartenspiel dermassen performancekritisch sein kann, dass man alle kernels ausnutzen muss. 8|
    @ErfinderDesRades
    Vielen Dank, deine Variante sieht mir noch effizienter aus als die andere.

    ErfinderDesRades schrieb:


    Ein anderes Thema ist, dass ich mir garnet vorstellen kann, wie ein Kartenspiel dermassen performancekritisch sein kann, dass man alle kernels ausnutzen muss. 8|

    Komplexere K.I. auf schwerstem Schwierigkeitsgrad des Spiels verursacht eine doch recht große Anzahl an Berechnungen.

    MfG Suter
    Fehler tritt immer nach ~1-2h Dauertest-Laufzeit auf, melde mich sobald er auftritt bzw. wenn er nicht mehr auftritt, Performancemäßig --> keine Verschlechterung zu sehen im Vergleich zur unveränderten Random-Klasse, jeweils ~30000 gespielte Spiele pro Minute