![]() |
Random(2) in schnell
Hi,
Ich habe ein Programm in dem ich sehr oft eine 1:1 wahrscheinlichkeit berechnen muss. nun stehe ich vor dem Problem das Random(2) vermutlich den größten Teil meiner Laufzeit in Anspruch nimmt....was etwas unbefriedigend ist. Das Problem ist dass ich nicht genau testen kann wieviel Random in anspruch nimmt, da die benötigten Durchläufe direkt vom ergebnis von Random abhängen. Wenn ich also immer 1 nehme ist mein Programm zwar sofort durch, hat aber auch nur einen winzigen Bruchteil an durchläufen gehabt, wenn ich 0 nehme ist es eine endlosschleife. Nun stellt sich natürlich die Frage nach alternativen... Ich habe es bereits mit GetTickCount (Auflösung zu gering), QueryPerformanceFrequency (Nicht zufällig genug??? jedenfalls ne endlosschleife) und Mouse (u.ä. auch net zufällig genug) versucht...reicht aber alles nicht. Was gäbe es noch an Alternativen? |
Re: Random(1) in schnell
Wenn du "Random(1)" machst kannst du auch gleich 0 schreiben. Schau dir am besten mal die hilfe zu random an.
|
Re: Random(1) in schnell
Zitat:
|
Re: Random(2) in schnell
Nur eine Idee... generiere eine Zufallszahl, mehrere Bytes, und speichere die Bitfolge in meinetwegen einem Array of Boolean. Das Array berechnest du dann halt alle 32, 64, whatever Durchläufe neu.
|
Re: Random(2) in schnell
Zitat:
Das hat mich immerhin schonmal von 43 auf 36 Sekunden gebracht bei einem 32Bit Integer als Zwischenspeicher... Nicht schlecht :thumb: Leider scheint Random kein Int64 zu unterstützen :? Sonst irgendwelche Ideen? |
Re: Random(2) in schnell
Benutz doch jeweils einen QPC-Ergebniswert als Quelle für 64 Bits. Oder benutz einen
![]() |
Re: Random(2) in schnell
Zitat:
Der Mersenne Twister hört sich interessant an... Gibt es von dem eine Implementation in Delphi? *zu negaH schiel :wink: * |
Re: Random(2) in schnell
QPC ist QueryPerformanceCounter.
|
Re: Random(2) in schnell
Zitat:
Was willst du überhaupt machen? |
Re: Random(2) in schnell
Zitat:
dafür unterstützt es als Ausgabe 80Bit-Extended Werte :wink: |
Re: Random(2) in schnell
Was machst du denn überhaupt mit den Zahlen? Vielleicht gibt es eine Möglichkeit, das Ganze anders anzusetzen.
|
Re: Random(2) in schnell
Vielleicht liege ich jetzt völlig falsch, aber ich versuche es mal.
Du bist der Auffassung, die Nutzung der Random Funktion nähme zuviel Zeit in Anspruch. Wenn das so sein sollte : Was macht Dein Programm, außer ausschließlich die Random-Funktion aufzurufen ? Ein Aufruf Random(2) benötigt 24 CPU-Ticks (GetTickCount=16 CPU-Ticks) QueryPerformanceFrequency ist als Alternative natürlich nicht tauglich, denn die liefert dir die Frequenz von QueryPerfomanceCounter, sollte also ein konstanter Wert sein. Das Ergebnis von QueryPerformanceCounter wäre eine Alternative, allerdings braucht das ca. 2900 Ticks also mehr als 100 mal so viel wir Random(2). Statt des lahmen QPC könntest Du den Time Stamp Counter direkt abfragen, aber auch das braucht 88 CPU-Ticks, also fast 4 mal so viel wie Random(2).
Delphi-Quellcode:
Wenn du, was das Beitrag vermuten läßt, eine Zufallszahl im Bereich 0..1 haben möchtest könntest du das so realisieren
FUNCTION TimeStamp:Int64;
asm rdtsc end;
Delphi-Quellcode:
Wenn ich das alles völllig falsch verstanden habe, dann ignoriere das Obige bitte.
FUNCTION Random2:integer;
asm rdtsc and eax,1 end; |
Re: Random(2) in schnell
Zitat:
|
Re: Random(2) in schnell
der Zufallsgenerator von Delphi mal in Assembler und für 1/0 optimiert:
Delphi-Quellcode:
asm @@nochma: imul ecx, ecx, $8088405 // vielleicht hier mal experimentieren inc ecx //mov edx, ecx js @@ist1 // wenn 0 mache: nop jmp @@nochma @@ist1: // wenn 1 mache: nop jmp @@nochma end; |
Re: Random(2) in schnell
Mersenne Twister kanste vergessen, zu langsam.
Aber zum Angang machst du mal was anders ;) Erzeuge eine 323Bit Zufallszahl. Diese global speichern. Bei der Berechnung in deinen Funktionen dann diese Funktion
Delphi-Quellcode:
Dies produziert zwar immer die gleiche Sequenz aus 32 Bits, aber dies spielt zur Einschätzung, um wieviel du durch die Optimierung deines RNGs an Zeit einsparen könntest, keine Rolle.
var
Zufall: Caridnal = 0; function RandomBit: Boolean; begin Zufall := Zufall shr 1 or Zufall shl 31; Result := Odd(Zufall); end; initialization Randomize; Zufall := Random(MaxInt); end. Im alten DEC war ein TRandom_LFSR mit Perioden von 2^32-1 bis 2^2032-1 bei dem der LFSR 128 mit Periode 2^128-1 ungefähr 43Mb/Sec auf einem P4 1.5GHz lief. Du könntest dir aus dem Source dieses LFSR extrahieren. Gruß Hagen |
Re: Random(2) in schnell
Ok, ich denke es ist an der Zeit mal genauer den "Sinn" des Projektes zu erläutern.
Im Prinzip geht es um ein OnlineGame. Bei diesem Spiel ist es möglich Waffen mithilfe von Edelsteinen zu veredeln. (Um Schaden zu erhöhen...) Hierbei gibt es Waffenstufen von 0 bis 15. Für jeden Up-Versuch wird ein Stein benötigt. Es besteht eine 50%-Chance dass der Versuch erfolgreich ist (Stufe wird um eins erhöht) Schlägt der Versuch fehl, wird die Stufe verringert (Allerdings nur falls die neue Stufe nicht unter 3 liegt...d.h. bei einem Fehlschlag bei Stufe 0,1,2 oder 3 bleibt alles wie es ist). Ziel ist es einen Simulator dazu zu schreiben, welcher die Steine zählt die man braucht um eine Waffe von 0 auf 15 zu bringen. Quelltext ist folgender:
Delphi-Quellcode:
Ich hoffe es ist klar dass ein "nicht zufälliger zufallsgenerator" mir nicht weiterhilft :-)
while Akt<>Config.Ziel do //Akt = Aktuelle Stufe
begin if Rnd>31 then begin R:=Random(MaxInt); Rnd:=0; end; //Letztes Bit meines 32Bit Random-Werts => Neue Zahl if (R and (1 shl Rnd))<>0 then //Aktuelles Bit true? Inc(Akt) //Eins Hoch else if Akt>3 then Dec(Akt); //Sonst wenn Stufe>3 dann runter Inc(C); //Versuch zählen Inc(Rnd); //Nächstes Bit end; Ein weiteres Problem, welches mir soeben aufgefallen ist ist der Array den ich für meine Ergebnisse nutze... insgesamt sieht das so aus:
Delphi-Quellcode:
in anbetracht der Tatsache dass Random nur 22 Cycles benötigt, liegt es vielleicht doch eher am SetLength?
TUpper = class
// ... FRes64: array of Int64; end; procedure TUpper.Generate(Cnt: Int64); var Akt, Rnd: Byte; C, Len: Int64; R: Integer; begin Len:=Length(FRes64); Rnd:=255; R:=0; while Cnt>0 do begin Akt:=Config.Start; C:=0; while Akt<>Config.Ziel do begin if Rnd>31 then begin R:=Random(MaxInt); Rnd:=0; end; if (R and (1 shl Rnd))<>0 then Inc(Akt) else if Akt>3 then Dec(Akt); Inc(C); Inc(Rnd); end; if C>=Len then begin Len:=C+1; SetLength(FRes64,Len); end; Inc(FRes64[C]); Dec(Cnt); end; end; (Wie) kann man das optimieren? Daher stellt sich mir nun die Frage ob man hier überhaupt noch groß optimieren kann...(Aber da vertrau ich ganz auf euch :wink: ) Im Moment braucht diese Funktion so wie sie da steht für 100.000.000 Durchläufe 386 Sekunden auf einem AMD 3000+ (seperater Thread, tpLower, Opera,Miranda usw im Hintergrund laufend) |
Re: Random(2) in schnell
Bei so einem Problem solltest du dich eher an einem Stochastiker wenden. Gut Möglich, dass du im Matheboard.de jemanden mit einer Lösung finden kannst. Du hast hier einen Random-Walk in einem beschränkten Intervall, vielleicht hat schon jemand eine analytische Lösung gefunden.
|
Re: Random(2) in schnell
Zitat:
Gruß Der Unwissende |
Re: Random(2) in schnell
Stimmt, wenn man das Sign-Bit des Exponenten rauslässt, könnte es gehen. Und man spart sich auch die Multiplikation, die man hat, wenn man das ganze in einen Integer umrechnet.
|
Re: Random(2) in schnell
sag mal, was machst du eigentlich da?
Delphi-Quellcode:
ich kann mir nicht vorstllen, dass du 2^32 ergebnisse auf dem bildschirm sehen möchtest. schränk das doch mal ein und bau dir eine vernünftige datenstruktur auf, anstatt immer den speicher umzumodeln...
if C>=Len then
begin Len:=C+1; SetLength(FRes64,Len); end; Inc(FRes64[C]); |
Re: Random(2) in schnell
Ich habe einen Array dieser Form:
Array[Anzahl an Steinen] = So oft vorgekommen Wie soll ich es sonst machen? |
Re: Random(2) in schnell
wie soll denn deine ausgabe aussehen resp. wie sieht sie derzeit aus?
PS: du hast doch nur 15 steine, oder hab ich mich da verhört? |
Re: Random(2) in schnell
Eine Liste
Anzahl Steine: So oft Vorgekommen bzw die liste als Balkendiagramm jaa, die anzahl an steinen die verbaut wurden... es gibt 15 stufen und jeder stein hat ja ne 50%-chance die zu erhöhen... im schnitt sind es ca. 162 steine für 15 stufen. |
Re: Random(2) in schnell
dann hast du doch hier 'n array[0..14] oder array[1..15], da brauchste doch kein setlength, das kannste dir doch auch schon zuvor ausrechnen und den compiler sagen, wie gross das wird. wenn du der sache nicht traust, kannste ja auch noch 'n paar byte sicherheitsreserve drauflegen.
pass auf, dein progy wird noch zum turbo :wink: ps: wenn du auch noch steine unterschiedlicher qualität hast, kannst 'n 2 dimensionales array erstellen, z.b. array[1..15, 0..300]; nach oben begrenzt, da du ja auch nicht unendlich spielen kannst ... |
Re: Random(2) in schnell
Zitat:
Ich will speichern wie oft ich wieviele steine insgesamt brauche... |
Re: Random(2) in schnell
wenn du für jeden level speichern willst, dann brauchste 'n array[1..15]. da kannste dann sagen inc(a[level]); und daraus kannste dann 'n wunderbares balkendiagramm zaubern :-)
PS: vermindern kannste den level dann mit dec(level); |
Re: Random(2) in schnell
hab mal 'n kleines demo zusammengebastelt:
Delphi-Quellcode:
optimierungen musste da noch selbst vornehmen, z.b. wenn 'de 'ne monte carlo methode mit implementieren möchtest....
program Project2;
{$APPTYPE CONSOLE} uses sysutils; var a: array[1..15] of integer; i, level: integer; begin level := 1; randomize; while level < 16 do begin case random(2) of 0: begin inc(a[level]); inc(level) end; 1: begin if level > 3 then dec(level); end; end; end; for i := 1 to 15 do writeln(i:3,#9,a[i]); readln; end. |
Re: Random(2) in schnell
Das ist aber nichts das was ich haben will!!! :wall:
|
Re: Random(2) in schnell
was willste denn dann haben? dachte 'n auswertung, wieviele versuche du brauchst, um durch die jeweiligen level durchzusteigen? genau, das macht es, kannst natürlich noch verfeinern, z.b. mit wievielen versuchen du vergeblich angerant bist... musste halt noch 'ne zweite dimension aufmachen. wie vorhin beschrieben.
ansonsten, zeig doch mal deine ausgabe her? so wie in deinem obigen beispiel, kannste es ja nicht ausgeben... wird ja viel zu unübersichtlich... PS: beim beispiel, hab ich keine balkengrafik gemacht, sondern halt 'ne ganz normale ausgabe, sollt eigentlich reichen... um das zu testen... |
Re: Random(2) in schnell
Ich habe mich mal etwas umgehört und
![]() |
Re: Random(2) in schnell
Das riecht einfach nach stinknormaler gaußscher Normalverteilung.
Aber die Frage ('Wie viele Versuche braucht man ...') ist schon falsch gestellt. Denn die Antwort kann man so geben: "Wenn man Glück hat, reichen 15 Versuche, hat man Pech, passiert es nie". Das ist das Wesen des Zufalls. Die korrekte Frage würde lauten: "Wie viele Versuche braucht man im Mittel, damit das Zielereginis mit einer 90%igen Wahrscheinlichkeit auftritt?". Da es sich um eine Gaußsche Glockenkurve handelt (meine Annahme), muß man 'nur' ausrechnen(!), bei welchem X die Fläche unter der Kurve 90 ergibt. Oder so ähnlich. Mathematiker vor! (Ich krieg komischerweise immer Denk- und Schreibstörungen :roteyes: , wenn eine Volltextrecherche in einem Umkreis von 15km 70%ige Treffer bei der Suche nach dem Wort 'Wahrscheinlichkeit' ergibt :mrgreen: ) |
Re: Random(2) in schnell
Das Ganze müsste eher eine Binomialverteilung sein. Das Problem ist aber, dass du hier nicht in beide Richtungen laufen darfst und das nach dem ersten erreichen des letzen Feldes abgebrochen wird. Diese beiden Einschränkungen machen alles etwas schwieriger :)
|
Re: Random(2) in schnell
Hallo,
hier noch ein Ansatz zur Beschleunigung der Simulation: loop unrolling. Bei dieser Technik werden mehrere Schleifendurchläufe ausprogrammiert, was im aktuellen Fall die Verwaltung des zusätzlichen Bitzählers erspart. Weiterhin sollte die Anzahl der Sprungbefehle auf ein Minimum reduziert werden:
Delphi-Quellcode:
Eine solche Optimierung verbessert zwar die Laufzeit, nicht aber die Lesbarkeit des Quelltextes...
var
[...] y : Byte; begin [...] Akt := Config.Start; if (Akt <> Config.Ziel) then repeat R := Random($10000); y := Ord(Odd(r)); Inc (Akt, y - Ord(Akt > 3) shr y); Inc (C); if (Akt = Config.Ziel) THEN Break; y := Ord(Odd(r shr 1)); Inc (Akt, y - Ord(Akt > 3) shr y); Inc (C); if (Akt = Config.Ziel) THEN Break; y := Ord(Odd(r shr 2)); Inc (Akt, y - Ord(Akt > 3) shr y); Inc (C); if (Akt = Config.Ziel) THEN Break; [...] y := Ord(Odd(r shr 14)); Inc (Akt, y - Ord(Akt > 3) shr y); Inc (C); if (Akt = Config.Ziel) THEN Break; y := Ord(Odd(r shr 15)); Inc (Akt, y - Ord(Akt > 3) shr y); Inc (C); if (Akt = Config.Ziel) THEN Break; until False; [...] end; Gruß Hawkeye |
Re: Random(2) in schnell
Jop, da fragt man die Stochastiker :)
Man kann das ganze als Bernoulli-Kette der Länge x sehen und dann die wahrscheinlcihkeit dafür ausrechnen, dass man mit diesen x Versuchen 15 Treffer landet. Das wäre dann B(x;0,5;15) = (15 über x)0,5^x*0,5^(15-x) hmm... das lässt sich vereinfachen... (15 über x)0,5^(x+15-x) = (15 über x)0,5^15 wenn ich mich nicht irre. meine tabelle enthält leider nix für p=0,5. |
Re: Random(2) in schnell
Das klappt so leider nicht. Wenn du einmal daneben liegst, brauchst du zwei Treffer mehr, da deine Stufe nach unten gesetzt wird. Mit 15 Treffern landest du nicht auf Stufe 15, sondern sonstwo (z.B. auf der Null, wenn du immer abwechselnd triffst).
Auch fehlt bei dem Ansatz die Einschränkung, dass du nicht ins negative gehen darfst. Ich habe schon einen Ansatz (siehe ein paar Posts oben), habe nur keine Zeit, das mal auszurechnen. |
Re: Random(2) in schnell
Liste der Anhänge anzeigen (Anzahl: 1)
Ich muss ehrlich zugeben dass ich diesen Ansatz nicht verstehe...
Mein Ziel ist es im Prinzip ganz am Ende eine Liste zuhaben: Anzahl Stein: Wahrscheinlichkeit Die Durchschnittliche Anzahl ist mehr ein Nebenprodukt was man direkt noch mit ausrechnen kann :-) [Edit] Zur Verteilung... Die sieht so aus wie im Anhang (X = so oft vorgekommen, Y = Anzahl der Steine). |
Re: Random(2) in schnell
hallo greedy,
ich hab zwar immer noch keinen plan davon, was du auswerten willst, hätt aber 'ne kleine änderung im quellcode vorzuschlagen..
Delphi-Quellcode:
PS: bei mir werden ca. 4'000'000'000 random aufrufe in ein paar wenigen sekunden abgearbeitet... also, an random kanns nicht liegen..
TUpper = class
// ... FRes64: array of cardinal; //<<-- reicht doch aus, und kann der rechner besser verarbeiten end; procedure TUpper.Generate(Cnt: Int64); var Akt, Rnd: Byte; C, Len: cardinal; //<<-- reicht doch aus, und kann der rechner besser verarbeiten R: Integer; begin Len:=Length(FRes64+100000); //<<-- hier gleich mehr byte reservieren Rnd:=255; R:=0; while Cnt>0 do begin Akt:=Config.Start; C:=0; while Akt<>Config.Ziel do begin if Rnd>31 then begin R:=Random(MaxInt); Rnd:=0; end; if (R and (1 shl Rnd))<>0 then Inc(Akt) else if Akt>3 then Dec(Akt); Inc(C); Inc(Rnd); end; if C>=Len then begin Len:=C+1; SetLength(FRes64,Len+100000); //<<-- hier gleich mehr byte reservieren end; Inc(FRes64[C]); Dec(Cnt); end; end; |
Re: Random(2) in schnell
Liste der Anhänge anzeigen (Anzahl: 2)
Also das mit 32Bit vs 64Bit habe ich auch schon ausprobiert.
Statt einem array of Int64 ein array of Integer zu nehmen bringt, es hat mich sehr erstaunt, keinen messbaren Performanceschub. Ich habe es mehrmals gemessen, die Differenz war <2 Sekunden auf 5 Minuten. Das mit dem SetLength ist eigentlich keine Schlechte Idee, auch wenn es hier an der falschen Stelle steht ;-) das ganze muss eine Zeile höher. Allerdings bringt auch dies keinen signifikanten Unterschied. Len:=C+1; 43s für 10mio Len:=C+100; 43s für 10mio Len:=C+10000; 42s für 10mio Übrigens ist mir eine Sache aufgefallen... während folgende Abfrage:
Delphi-Quellcode:
Eine ziemlich glatte Kurze hervorbringt, hat bei dieser Bedingung:
if Random(2)=1 then
Delphi-Quellcode:
Die Kurve deutlich ausgeprägte Spitzen!
if Rnd>31 then begin R:=Random(MaxInt); Rnd:=1; end;
if (R and (1 shl Rnd))<>0 then interessanterweise in 32er Abständen... :gruebel: So wie das im Moment ist scheint diese Optimierung die zufällige Verteilung etwas aus dem Gleichgewicht zu bringen. [Edit] Jetzt versteh ich gar nix mehr. :shock: Ist Cardinal schneller als Integer???
Delphi-Quellcode:
mit Integer auf 10mio: 35s
if Rnd>31 then begin R:=Random(High(R))+1; Rnd:=1; end;
mit Cardinal auf 10mio: 27s :wiejetzt: [Edit2] Das Array hingegen auf Cardinal downzugraden brachte mich lediglich von 27 auf 26s. Die sind wahrscheinlich auch eher im nur halb so kleinen Array begründet. [Edit3] Im Anhang mal ein ScreenShot von den "Zacken"... btw scheinen es nicht 32er Abstände zu sein sonderen 31er. |
Re: Random(2) in schnell
Gereon, hast du meinen Vorschlag aus Beitrag #33 einmal geprüft? Das Code-Stück ersetzt lediglich die innere WHILE-Schleife deines Codes.
Gruß Hawkeye |
Re: Random(2) in schnell
Zitat:
Aber ich probiere es gleich mal aus. [Edit] Die If-Anweisungen um Abzubrechen scheinen mehr Ticks zu brauchen als dei Auflösung der Schleife einbringt... Es dauert 29s statt nur 27s. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:23 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