Forum: Compiler & IDEs rand() mit min max


von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Hi,
1
strobo_counterOFF = rand();
2
while(strobo_counterOFF < out_stroboRD_MIN || strobo_counterOFF > out_stroboRD_MAX)
3
     strobo_counterOFF = rand();

Das geht doch bestimmt noch eleganter, oder?
Möchte außer der stdlib nicht unnötig viel nur für diesen einen Fall 
einbinden.

Grüße Oekel

: Verschoben durch User
von Timmo H. (masterfx)


Lesenswert?


von D a v i d K. (oekel) Benutzerseite


Lesenswert?

danke, vielleicht habe ich den Baum im Wald nicht gefunden. ;-)
Selbstverständlich hatte ich vorher gesucht, doch immer mit "stdlib" und 
da kamen solch einfach Dinge wie

(rand()%(MAX-MIN))+MIN;

nicht vor. (Manchmal muss man wohl suchen lassen :P

von Karl H. (kbuchegg)


Lesenswert?

D a v i d K. schrieb:
> danke, vielleicht habe ich den Baum im Wald nicht gefunden. ;-)
> Selbstverständlich hatte ich vorher gesucht, doch immer mit "stdlib" und
> da kamen solch einfach Dinge wie
>
> (rand()%(MAX-MIN))+MIN;
>
> nicht vor. (Manchmal muss man wohl suchen lassen :P


Das Problem ist, dass dieses hier je nach Werten für MAX und MIN die 
Gleichverteilung von rand() zerstört. Bei hinreichend großen Werten für 
RANDMAX, und dazu vergleichsweise kleinen Werten für MIN und MAX) ist 
das für private Basteleien meistens akzeptabel, weil die Abweichung dann 
klein bleibt. Wenn man aber zb bei Spielen eine ordentliche 
Gleichverteilung haben will, so dass tatsächlich alle generierten Zahlen 
die gleiche Wahrscheinlichkeit haben, dann muss man eben mehr Aufwand 
treiben. Du hast ja auch keine Freude, wenn dein Gegenüber einen Würfel 
hat, der mit 28%-iger Wahrscheinlichkeit eine 6 würfelt, während die 
andere Augenzahlen nur eine 14%-ige Wahrscheinlichkeit haben. Oder? 
(Prozentzahlen krass überhöht um das Benutzerproblem zu zeigen)

Wobei man natürlich auch sagen muss, dass die Version vom 
Eröffnungsposting schon ausgesprochen schlecht implementiert ist.

von D a v i d K. (oekel) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:

> Wobei man natürlich auch sagen muss, dass die Version vom
> Eröffnungsposting schon ausgesprochen schlecht implementiert ist.

Wesshalb ich sie auch auf keinen Fall so stehen lassen wollte. War halt 
nur ein Workarround, damit ich schnell zu einer Ausgabe kam.

Wie "ungleichverteilt" ist denn diese MOD-Methode wirklich?
Wie man sicherlich erkennen kann soll es für eine RAND-Strobo funktion 
genutzt werden. Daher ist es mir nur wichtig, dass die Grenzwerte sich 
"sichtbar" ändern. Sprich ich möchte keine stufenweise Änderung 
erkennen, wenn ich am Poti für MIN MAX spiele.

Grüße Oekel

von Karl H. (kbuchegg)


Lesenswert?

D a v i d K. schrieb:
> Karl Heinz Buchegger schrieb:
>
>> Wobei man natürlich auch sagen muss, dass die Version vom
>> Eröffnungsposting schon ausgesprochen schlecht implementiert ist.
>
> Wesshalb ich sie auch auf keinen Fall so stehen lassen wollte. War halt
> nur ein Workarround, damit ich schnell zu einer Ausgabe kam.
>
> Wie "ungleichverteilt" ist denn diese MOD-Methode wirklich?

Das hängt von deinen Werten ab.

Nehmen wir mal an, dein Zufallszahlen Generator liefert Werte zwischen 0 
und 8 und die sind tatsächlich sauber Normalverteilt. D.h. alle Zahlen 
0, 1, 2, 3, 4, 5, 6, 7 haben die gleiche Wahrscheinlichkeit.

Und jetzt versuchst du daraus Zahlen zwischen 0 und 6 zu generieren, 
indem du die ursprünglichen 0 .. 8[ mittels MOD 6 in diesen Bereich 
'mappst'. Was passiert?
Na schauen wir uns das mal für jede Zahl an, welches Ergebnis entsteht 
aus jeder gelieferten Zufallszahl
1
   0    wird zu     0
2
   1                1
3
   2                2
4
   3                3
5
   4                4
6
   5                5
7
   6                0
8
   7                1
D.h. obwohl du in den Ausgangsdaten (auf statistischen Mengen) gleich 
viele 2-er wie 1-er hast, hast du noch der Mod-Rechnung mehr 1-er als 
2-er. Denn eine 7 aus den Werten vom Generator wird ja ebenfalls zu 
einer 1

Aus dem Generator hatten alle Werte noch die gleiche Wahrscheinlichkeit 
von 1/8 = 12.5%
Nach deiner Mod-Umrechnung gibt es immer noch 8 'mögliche' Werte. Aber 2 
davon sind jeweils gleich. Von 8 möglichen Ergebnissen sind 2 Stück 
davon 1-er. D.h du hast dafür eine Wahrscheinlichkeit von 2/8 = 1/4 = 
25%
1
d.h. 0 hat eine Wahrscheinlichkeit von 1/4 oder 25%
2
     1 hat eine Wahrscheinlichkeit von 1/4 oder 25%
3
     2 hat eine Wahrscheinlichkeit von 1/8 oder 12.5%
4
     3                                          12.5%
5
     4                                          12.5%
6
     5                                          12.5%
Worauf ich bei so einem Würfel mein Geld setzen würde, weiß ich :-)


Jetzt liefert dein Zufallszahlen-Generator natürlich nicht Werte 
zwischen 0 und 8 sondern zwischen 0 und RAND_MAX. Und RAND_MAX ist 
normalerweise mindestens 32767, was die prozentuale Verschiebung 
natürlich um einiges geringer ausfallen lässt. Für den Hausgebrauch 
völlig unerheblich, solange MIN und MAX klein sind gegenüber den 32767. 
Für eine Spielbank allerdings nicht brauchbar. Denn bei Millionen von 
Spielen machen sich auch kleine 'Vorteile' bemerkbar.

(PS: Die Mathematiker nennen diese Problematik das 'Schubladenargument'. 
Wenn du n Socken hast, die du in m Schubladen aufteilen musst und n kein 
ganzzahliges Vielfaches von m ist, dann bleibt es nicht aus, dass in 
manchen Schubladen mehr Socken drinnen sind, als in anderen. Es ist eben 
nicht möglich 7 Paar Socken so auf 5 Schubladen zu verteilen, dass in 
allen Schubladen gleich viele Paar Socken landen. Mit 15 Paar Socken 
würde es gehen. Denn 15 ist ja auch ein ganzzahliges Vielfaches von 5)

von Εrnst B. (ernst)


Lesenswert?

nebenbei:
"man rand" liefert folgenden Textabschnitt dazu:

...Bei älteren Implementationen von rand() sind niederwertige Bits 
jedoch viel weniger zufällig als höherwertige Bits

In Numerical Recipes in C: The Art of Scientific Computing (William H. 
Press, Brian P. Flannery, Saul A. Teukolsky, William T. Vetterling; New 
York: Cambridge University Press, 1990 (1st ed, p. 207)), finden sich 
die folgenden Kommentare:
"Wenn Sie Zufalls-Ganzzahlen zwischen 1 und 10 erzeugen möchten, sollten 
Sie dies immer wie folgt tun:


j=1+(int) (10.0*rand()/(RAND_MAX+1.0));

und niemals auf folgene oder ähnliche Weise:


j=1+((int) (1000000.0*rand()) % 10);

(wodurch niederwertige Bits benutzt würden)."


Zufallszahlenerzeugung ist ein kompliziertes Thema. Das Buch Numerical 
Recipes in C (siehe oben) liefert eine exzellente Diskussion über 
praktische Zufallszahlenerzeugung in Kapitel 7 (Zufallszahlen).

von deathfun (Gast)


Lesenswert?

Hallo,

ich bin gerade unterwegs und habe die Random Funktion eine ewigkeit 
nicht mehr verwendet, aber geht es nicht deutlich einfacher so:
1
int MIN = 10;
2
int MAX = 50;
3
int ZUFALL;
4
5
ZUFALL = MIN + (Random() * (MAX - MIN) + 1);

ZUFALL sollte dan irgendetwas zwischen 10 und 50 sein.




Random sollte doch eine Zahl >1 zurückliefern die man mit dem 
gewünschten Gültigkeitsbereich Multipliziert oder etwa nicht?



Gruß
Deathfun

von Rolf M. (rmagnus)


Lesenswert?

deathfun schrieb:
> ZUFALL = MIN + (Random() * (MAX - MIN) + 1);

Ich weiß nicht, was Random() ist oder wo du das her hast, aber rand() 
liefert eine Integer-Zahl im Bereich 0 bis RAND_MAX.

von Εrnst B. (ernst)


Lesenswert?

deathfun schrieb:
> aber geht es nicht deutlich einfacher so:

naja, "rand()" ist halt seit ewigkeiten in der C-Stdlib (mindestens C89) 
und gibt nunmal einen int zurück.
Du hast eine "Random()"-Funktion irgendwoher, die wohl ein float 0..1 
o.Ä. ausspucken soll.

Wenn wir also die Standard-Funktionen verlassen, geht es noch einfacher 
so:
1
int ergebniss=erzeugeGleichverteilteZufallszahlAlsInteger(MIN,MAX);

Implementation der Funktion als Übungsaufgabe für den geneigten Leser ;)

in C++:
1
std::default_random_engine generator;
2
std::uniform_int_distribution<int> distribution(1,6);
3
int dice_roll = distribution(generator);  // generates number in the range 1..6

von deathfun (Gast)


Lesenswert?

OH,

Mein Fehler! - könnte (bin mir relativ sicher) Java sein!
Entschuldigt konnte nichts nachschauen, bin unterwegs :(


Dort liefert Random() einen Wert zuwischen 0 und <1 zurück mit dem man 
arbeiten kann.


Gruß
Deathfun

von Karl H. (kbuchegg)


Lesenswert?

deathfun schrieb:

> Dort liefert Random() einen Wert zuwischen 0 und <1 zurück mit dem man
> arbeiten kann.

.... der in den meisten Implementierungen so entsteht

    rand() / (double)RAND_MAX

d.h. du kriegst dort auch nicht mehr als RAND_MAX verschiedene 
Zufallszahlen. Nur eben im Bereich 0 bis 1 gelegen und nicht als ganze 
Zahlen.
Das Problem ist dadurch immer noch das gleiche. Auch hier greift das 
Schubladenargument.
Zufallsverteilungen so umzuformen, dass die Charakteristik der Folge 
erhalten bleibt (oder sich gezielt in eine andere verwandelt), ist nicht 
so einfach wie es auf den ersten Blick aussieht, wenn man exakt sein 
muss.

von Tom (Gast)


Lesenswert?

Hallo,

also ich habe auch das Problem, das ich in reinem C eine gleichverteilte 
Zufallszahl (Ganzzahl) benötige, die im Bereich von 1 - 1000 liegen 
muss.
Irgendwo habe ich gelesen das man einfach die Rand-Funktion benutzen 
kann und Modulo 1024 verwendet. Ziehungen über 1000 werden wiederholt, 
was die Laufzeit etwas erhöht, aber kein Problem wäre.

Ist das so korrekt? Und kann jemand kurz einen Codestück posten..

DANKE

Tom

von Karl H. (kbuchegg)


Lesenswert?

Tom schrieb:
> Hallo,
>
> also ich habe auch das Problem, das ich in reinem C eine gleichverteilte
> Zufallszahl (Ganzzahl) benötige, die im Bereich von 1 - 1000 liegen
> muss.
> Irgendwo habe ich gelesen das man einfach die Rand-Funktion benutzen
> kann und Modulo 1024 verwendet. Ziehungen über 1000 werden wiederholt,
> was die Laufzeit etwas erhöht, aber kein Problem wäre.

Die Annahme ist hier, dass RAND_MAX ein ganzzahliges Vielfaches von 1024 
ist.

> Ist das so korrekt? Und kann jemand kurz einen Codestück posten..

Na komm. Deine textuelle Beschreibung in ein Codestück umzusetzen ist 
jetzt aber wirklich nicht das große Problem.

Und nochmal:
Für den Hausgebrauch, für ein kleines Spiel welches unter Freunden 
gespielt wird, ist das sehr wahrscheinlich alles ziemlich uninteressant. 
Was anderes ist es, wenn es um eine Spielbank geht, bei der Geld im 
Spiel ist. Allerdings wird dann höchst wahrscheinlich die Qualität eines 
rand() sowieso nicht ausreichend sein.

Also mach dir da jetzt deswegen keine Kopf und nimm einfach

    min + rand() % ( max - min )

und gut ists.


PS: Das rand() in den kleineren Bitzahlen keine so guten Werte bringt, 
war richtig, wurde meines Wissens aber schon lange korregiert, ist also 
heute kein echtes Thema mehr.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.