![]() |
Delphi-Version: XE2
Randomize-Chaos
Es heißt ja, daß man den (Pseudo)Zufallsgenerator nicht ständig neu initialisieren soll.......
Wieso ist noch keiner Auf die Idee gekommen den Generator gleich in der Inititalization der System.pas zu initialisieren? Mal im Delphi-Quellcode etwas umgesehn, wird es überall gefunden. OK, diese Stellen sind nicht optimal, aber wenigstens wird es jeweils nur einmal ausgeführt. Allerdings gibt es immernoch ein winziges Problemchen, wenn irgendwo bei der Initialisierung schon Zufallszahlen verwendet werden.
Delphi-Quellcode:
Das wird etwa jedes 4-milliardste Mal bei Aufruf der Funktion neu initialisiert, immernoch nicht ideal, aber besser als nichts.
unit System.IOUtils;
class constructor TPath.Create; unit IdHMAC; initialization unit SyncController; initialization
Delphi-Quellcode:
Die nun folgenden Stellen sind demnach also ganz schlimm, da bei jedem Aufruf am Generator rumgespielt wird.
unit IdMessageCoderMIME;
class function TIdMIMEBoundaryStrings.GenerateRandomChar: Char; begin if RandSeed = 0 then begin Randomize; end;
Delphi-Quellcode:
unit SessColn;
constructor TSessionIDGen.Create; unit IdFTPCommon; function MakeXAUTKey : Cardinal; unit IdDNSCommon; constructor TDNSHeader.Create; unit IdCustomHTTPServer; //function GetRandomString(NumChar: Cardinal): string; // nicht öffentlich, aber in Folgendem verwendet function TIdHTTPDefaultSessionList.CreateUniqueSession(const RemoteIP: String): TIdHTTPSession; unit Datasnap.Win.ObjBrkr; function TServerCollection.GetBalancedName: string; unit Data.DBXEncryption; //function GenerateKey(KeyLength: Integer): string; // nicht öffentlich, aber in Folgendem verwendet constructor TPC1Cypher.Create; Am Schönten wäre es ja, wenn man den Generator in einen Record verpacken würde, also den Zufallswert als Feld und dazu ein paar Record-Methoden und Property für den Zugriff. Eine Instannz dann als Singleton (globale Variable) und im Class-Constructor initialisiert. Und jeder der glaubt das ständig neu initialisieren zu müssen, kann sich gerne eine eigene Instanz zulegen. Über ein Event und/oder eine überschreibbare Methode mit einer anderen Berechnungsroutine ausrüstbar. |
AW: Randomize-Chaos
Das habe ich mir auch schon oft gewünscht... aber ich denke es ist einfach aus Gründen der Abwärtskompatiblität so. Genau wie dass bei Strings der erste Index 1 ist und vieles andere...
Manchmal denke ich mir, es wäre doch schön, die Sprache mal zu entrümpeln bzw. einen Neuanfang zu machen. Aber das wird wohl nicht passieren, denn dann würde Delphi wohl völlig obsolet, da es ja ohnehin schon einige moderne, schöne Sprachen gibt, die noch nicht so viel Ballast haben, und weiter verbreitet sind. Btw: Wieso ein Record und keine Klasse? Muss heutzutage denn immer alles ein Record sein? |
AW: Randomize-Chaos
Na ja, ich muß allerdings auch zugeben, dass ich nicht selten in einem Programm mit Zufallszahlen gearbeitet habe, wo es mir durchaus in den Kram paßte, dass es immer dir gleichen Zufallszahlen waren, da ich auf diese Weise den gleichen Ablauf hatte und so bewußt auf Fehler reagieren konnte. Wenn ich z. B. bei einem Spiel einen Fehler bei einem bestimmten Ablauf habe, dann habe ich ihn immer, auch wenn ich das Programm 50 mal starte. Ich weiß dann genau wann der Fehler kommt. Stimmt alles und es gibt keine Fehler, wird dann mit richtigen Zufallszahlen gearbeitet.
Ich glaube also nicht, dass es ein alter Bug ist. Und die 0 bei der Index, warum nicht, warum ein Wert verschwenden. |
AW: Randomize-Chaos
Zitat:
Dann gibt es für Records noch die Operatoren, womit man eine implizite und explizite Integerzuweisung hinbekommt. RandSeed wird dann zum Record und man kann immernoch
Delphi-Quellcode:
machen.
RandSeed := 123456;
Und in der Unit System darf noch kein Speicher über den MemoryManager reserviert werden, denn sonst könnte der Programmierer später keinen eigenen MM laden, womit eine Klasse also wegfällt. :zwinker: Für die Abwärtskompatibilität könnte man die "alten" Funktionen ja auf die Record-Methoden umleiten und die Randomize-Prozedur einfach leer (ohne Funktion) lassen. |
AW: Randomize-Chaos
Also das der PRNG ohne Zutun immer die gleichen Zahlen liefert, ist so gewollt.
Bei Strings würde ich mal sagen, das es irgendwie logisch ist, wenn ich auf S[1] zugreife, um das 1.Element einer Zeichenkette zu bekommen. Bei Arrarys ist es etwas anders, denn dort ist die Nomenklatur aus der Mathematik i.a. so, das x_0 das erste Element einer Reihe ist. In den 70er Jahren war das so üblich. Meine ersten Programmiererfahrungen habe ich auch mit Arrays gemacht, die bei 1 anfangen. Wobei die Programmiersprache schon die Möglichkeit bot, 0-basierte Arrays zu deklarieren. Das war eine Option, die für das gesamte Programm galt und in der ersten zeile stehen musste. So, und wer denn per se immer andere Pseudozufallszahlen haben will, der nimmt i.a. sowieso einen richtigen PRNG und nicht dieses G'lump von Delphi. |
AW: Randomize-Chaos
Zitat:
Wobei, ich hatte mal zwei Probleme mit Zufallszahlen. Einmal wollte ich eine Explosion darstellen. Das Objekt zerfiel in über 1000 Teile, aber immer mit einem schönen Muster. Das ergab sich aus der gleichen Verteilung der Zufallszahlen. Die Explosion hatte eine in der Natur unnatürliche gleiche Verteilung. Für eine andere Gelegenheit hatte ich einen eigenen Zufallsgenerator geprogt, der sehr zufällig war. Der neigte wiederum dazu so unzuverlässig alles zu verteilen, dass ich wieder die Delphifunktion nutzte. Was sind also richtige Zufallszahlen? |
AW: Randomize-Chaos
Richtige Zufallszahlen (und die meine ich nicht) entstehen durch ein stochastisches Muster aus der Natur, siehe
![]() PRN, also Pseudozufallszahlen, sind Ergebnisse einer mathematischen Gleichung bzw. Funktion und haben den Anschein, als seien sie zufällig. In Wirklichkeit wiederholen sie sich aber, was deine Muster erklären könnte ( Gute PRNG haben neben einer sehr langen Periode noch andere Eigenschaften, die sie für statistische Verfahren geeigneter erscheinen lassen, als der doch recht einfache Delphi-PRNG. Nachzulesen bei ![]() |
AW: Randomize-Chaos
Zitat:
Du meinst ja sowas, oder?
Delphi-Quellcode:
Eine Klasse hätte außerdem den Vorteil, dass man den RNG überschreiben könnte.
Random := 123456;
writeln(Random); // nicht 123456 sondern irgendwas anderes
Delphi-Quellcode:
Wenn man z.B. deterministische Zufallszahlen braucht, kann man einfach eine eigene Klasse ableiten, und die dann entweder mit SetRNG() global setzen, oder – besser – eine eigene Instanz davon anlegen (da man ja bei so globalem Zeug nicht sicher sein kann, dass es nicht noch irgendwo anders verwendet wird).
type
TRNG = class function Random: integer; virtual; abstract; end; TRNGClass = class of TRNG; TRNGDefault = class(TRNG) protected RandSeed: integer; public function Random: integer; override; constructor Create; end; function Random: integer; procedure SetRNG(NewRNGClass: TRNGClass); implementation var RNGInstance: TRNG; function Random: integer; begin Result := RNGInstance.Random; end; procedure SetRNG(NewRNGClass: TRNGClass); begin // So wird der RandSeed nicht jedes mal zurückgesetzt wenn irgendein Dödel // SetRNG mehrfach aufruft if RNGInstance.ClassType <> NewRNGClass then begin RNGInstance.Free; RNGInstance := NewRNGClass.Create; end; end; constructor TRNGDefault.Create; begin // RandSeed initialisieren end; initialization SetRNG(TRNGDefault); @Popov: Nach meiner Erfahrung liefert Random schlechte Ergebnisse, wenn man mit einem RandSeed von 0 anfängt. Man kann natürlich irgendeinen anderen konstanten Wert zur Initialisierung nehmen, aber initialisieren sollte man auf jeden Fall. |
AW: Randomize-Chaos
Zitat:
|
AW: Randomize-Chaos
Zitat:
ich dachte, das mit den Zufalls- und Pseudzufallszahlen hätten Hagen und andere schon vor Jahren erklärt. Zufallszahlen = nur durch etwa gemessenen radioaktiven Zerfall Pseudozufallszahlen = durch eine Formel; immer wieder reproduzierbar; z.B: kann man auch RC4 zur Erzeugung von Pseudozufallszahlen verwenden. Aber dies ist sicherlich auch vielen schon bekannt. Entschuldigung. |
AW: Randomize-Chaos
Zitat:
|
AW: Randomize-Chaos
Zitat:
Delphi-Quellcode:
Trotzdem ist die Verteilung gleichmäßig und ohne Tendenz. Finde ich.
var
x, y: Integer; begin with Canvas do for x := 0 to ClientWidth - 1 do for y := 0 to ClientHeight - 1 do Pixels[Random(ClientWidth), Random(ClientHeight)] := clBlack; end; |
AW: Randomize-Chaos
Liste der Anhänge anzeigen (Anzahl: 1)
Popov, ich hab Deinen Code mal in ein Programm umgesetzt.
angehängt das Programm und hier ein Video nach dem 15.ten Durchgang sind dann in einem kleinen fast 800x600 Feld alle Pixel besetzt. Tatsächlich sind mir aber Bereiche aufgefallen, die immer wieder bevorzugt besetzt werden. Video, 1280x800, h264, avi (hab momentan kein avidemux, zum in anderen Container [z.B. mp4] reinpacken, sorry) ![]() 2,6 MB Wäre natürlich klasse, wenn das jemand mit Scanline umsetzen könnte ? (schneller?) |
AW: Randomize-Chaos
Zitat:
|
AW: Randomize-Chaos
Zitat:
Zitat:
|
AW: Randomize-Chaos
Zitat:
Zitat:
|
AW: Randomize-Chaos
Zitat:
Delphi-Quellcode:
Die Verteilung ist immer noch fast gleichmäßig. Nur sind hier doch tatsächlich Rechtecke zu erkennen :roll:
var
x, y: Integer; begin with Canvas do for x := 0 to ClientWidth - 1 do for y := 0 to ClientHeight - 1 do begin Randomize; Pixels[Random(ClientWidth), Random(ClientHeight)] := clBlack; end; end; |
AW: Randomize-Chaos
Mit diesem beeindruckenden Ergebnis läßt sich schlecht diskutieren.
Soweit ich weiß, ist die Quelle des Randomizes die Systemzeit. Anscheinend ist die abgerufene Auflösung nicht allzu hoch. Die Struktur ist viel zu regelmäßig, was vermutlich darauf zurückzuführen ist, daß die Schleifendauer mit der eingestellten Auflösung als ziemlich regelmäßig bzw. konstant erkannt wird. |
AW: Randomize-Chaos
Zitat:
Wenn man da ständig den Generator neu startet, kann dieses Verhalten negativ beeinflußt werden, da die Gleichverteilung über einen größeren Bereich sichergestellt wird. Nutzt man aber immer wieder nur "kleinere" Auschnitte, dann kann es passieren, daß Diese doch eine Tendenz erzeugen. PS: Delphi nutzt einen Zufallsgenerator, mit einer Sequenz von theoretisch bis zu 4 Milliarden Werten ... da kann ein Ausschnitt von nur 1000 Werten, in sich auch noch eine größere Tendenz aufweisen. Und das war besonsers zu merken, als Randomize noch nur GetTickCount verwendete (inzwischen, wenn mögich, QueryPerformanceCounter). Hat man da sehr schnell hinterienander Randomize ausgeführt und einen/mehrere Werte abgerufen, dann fiehl auf, daß sich sehr oft Wiederholungen einschlichen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:27 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz