Forum: PC-Programmierung Random von 1 - 6


von Martin M. (ats3788)


Lesenswert?

Hallo (komme von der Pascal Ecke)
wie kann man mit C die
Zufallszahlen 1 - 6 Würfel erzeugen.

   srand(toc);

      int lol  = rand();
        char zahl = lol;
         zahl = zahl & 0b00000111;

So hatte ich mir das gedacht macht nur dummerweise
Zz. von 0 - 7

von foobar (Gast)


Lesenswert?

Modulo:
zahl = (zahl % 6 ) + 1;

von Karl H. (kbuchegg)


Lesenswert?

Martin Michael schrieb:

> So hatte ich mir das gedacht macht nur dummerweise
> Zz. von 0 - 7


Wenn du den Rest einer Division willst, dann schreib auch Rest. Das ist 
der % Operator und nicht eine Verundung &.

Wenn man das bei bestimmten Zahlen und Datentypen optimieren kann (zb 
bei 7, 15, 31, etc. und unsigned Werten), dann ist dein Compiler schon 
clever genug, die Modulo Operation gegen eine Verundung auszutauschen. 
Das macht dem Compiler gar nichts aus. Compiler beherrschen sowas seit 
mehr als 40 Jahren. Angeblich gab es mal einen Compiler der das nicht 
konnte - irgendwann in grauer Vorzeit, die Menschheit war 
copmutertechnisch gerade von den Bäumen runtergekommen (sozusagen), das 
muss so um 1965 rum gewesen sein - aber dieses Gerücht konnte nie 
richtig bestätigt werden.

: Bearbeitet durch User
von Wilhelm F. (Gast)


Lesenswert?

Ist es nicht so, daß eine Random-Funktion in C einen Float-Wert zwischen 
0 und 1 zurück gibt? Den multipliziert man mit 5, und addiert 1 dazu. In 
C verwendete ich sowas noch nicht, zumindest an µC, aber machte da 
Zufallszahlen in Assembler auch für Würfelspiele.

Man kann aber auch mal einen Blick in die Compiler-Libraries werfen, wie 
die Zahl erzeugt wird. Bei meinem SDCC-Compiler für 8051 stehen dort 
zwei komische Konstanten drin, die als Ausgangszahl verwendet werden.

von Karl H. (kbuchegg)


Lesenswert?

Wilhelm F. schrieb:
> Ist es nicht so, daß eine Random-Funktion in C einen Float-Wert zwischen
> 0 und 1 zurück gibt?

Nein.
rand liefert einen int

Einige Bibliotheken haben eine Erweiterung, die dann meistens random() 
heißt. Ist aber im Grunde auch nichts anderes als
1
    rand() / (double)RAND_MAX

die Floating Point Division kann man sich daher auch sparen, wenn man 
sowieso nur an ganzen Zahlen interessiert ist.

: Bearbeitet durch User
von Martin M. (ats3788)


Lesenswert?

Modulo:
zahl = (zahl % 6 ) + 1;

danke dafür

Wilhelm F so macht es Delphi Pascal.

c gibt nach help einen halben 0xffff als integer
also 0x7fff oder 32767 dezimal

von muup (Gast)


Lesenswert?

Wird mit Modulo 6 nicht die Gleichverteilung der Ergebnisse beeinflusst?

Weil 2^8 / 6 = 42 Rest 4, d.h. die ersten vier Zahlen kommen häufiger 
vor also die restlichen.

Oder ist das ein Denkfehler meinerseits?

von Yalu X. (yalu) (Moderator)


Lesenswert?

muup schrieb:
> Wird mit Modulo 6 nicht die Gleichverteilung der Ergebnisse beeinflusst?

Ja.

Man kann das Problem umgehen, indem man die Größe des genutzten 
Wertebereichs des Zufallszahlengenerator auf die nächstkleinere durch 6 
teilbare Zahl einschränkt. Der Zufallszahlengenerators wird einfach so 
lange wiederholt aufgerufen, bis die Zahl in diesem eingeschränkten 
Wertebereich liegt. Dann wird der Sechserrest gebildet, und die 
Ergebnisse sind perfekt gleichverteilt.

von Eddy C. (chrisi)


Lesenswert?

Yalu X. schrieb:
> Dann wird der Sechserrest gebildet, und die
> Ergebnisse sind perfekt gleichverteilt.

Richtig, aber wenn Du Pech hast, wird es eine Endlosschleife ;-)

Man könnte auch so viele Zufallszahlen addieren, dass sich ein exaktes 
Vielfaches des Zielbereichs ergibt. Und dann wieder die Modulo-Operation 
und + 1

von muup (Gast)


Lesenswert?

Eine bessere Alternative wäre wohl:

[rand() + rand() + rand()] mod 6 + 1

weil 3*2^8 / 6 = 128 Rest 0

deterministische Ausführung

Viele Grüße!

von muup (Gast)


Lesenswert?

Oder noch besser:

[rand() / 4 + rand () / 4 + rand() / 4] mod 6 + 1

dann kann man mit uint8 rechnen und muss nicht erst über uint16 beim 
Addieren gehen.

von Martin M. (ats3788)


Lesenswert?

wow ihr seit echt super danke

von Yalu X. (yalu) (Moderator)


Lesenswert?

Eddy Current schrieb:
> Richtig, aber wenn Du Pech hast, wird es eine Endlosschleife ;-)

Theoretisch ja. Praktisch:

Wir sind hier im PC-Forum, auf 32-Bit-PCs ist RAND_MAX typischerweise
2**31-1, die rand-Funktion liefert also 2**31 unterschiedliche Werte. Da
2**31 ≡ 2 (mod 6), muss in 2 von 2**31 Fällen ein zweiter Wert, in 4 von
(2**31)**2 ein dritter Wert und in 8 von (2**31)**3 ein vierter Wert
generiert werden.

Angenommen du möchtest den PC jede Nanosekunde würfeln lassen (dazu muss
der PC schon ziemlich flott sein). Dann werden nach dem obigen Verfahren
im Mittel nur alle 40 Milliarden Jahre mehr als 3 rand-Aufrufe pro Wurf
benötigt. Man kann deswegen die Anzahl der Schleifendurchläufe guten
Gewissens auf 3 begrenzen.

> Man könnte auch so viele Zufallszahlen addieren, dass sich ein exaktes
> Vielfaches des Zielbereichs ergibt. Und dann wieder die
> Modulo-Operation und + 1

Das geht nicht, da die Summe mehrerer gleichverteilter Zufallszahlen
nicht mehr gleichverteilt ist, und zwar nicht einmal näherungsweise.

von Eddy C. (chrisi)


Lesenswert?

Yalu X. schrieb:
> Dann werden nach dem obigen Verfahren
> im Mittel nur alle 40 Milliarden Jahre mehr als 3 rand-Aufrufe pro Wurf
> benötigt.

Sag ich doch. Endlosschleife. Mit ein "wenig" Pech. Wobei Dein 
beschriebenes Verfahren schon recht effektiv arbeitet. Sprich: 
Neuwürfeln, wenn der Wert des Zufallsgenerators jenseits des höchsten 
Vielfachen der Modulooperation und dem Ende des Wertebereichs des 
Zufallsgenerators liegt.

Yalu X. schrieb:
> Das geht nicht, da die Summe mehrerer gleichverteilter Zufallszahlen
> nicht mehr gleichverteilt ist, und zwar nicht einmal näherungsweise.

Interessant. Nach ein wenig Nachdenken stimme ich Dir voll und ganz zu! 
Alleine die Betrachtung der Wahrscheinlichkeit für den Wert 0 als 
summiertes Zufallsergebnis im Vergleich zu 1 beweist schon, dass da ein 
Schieflage sein muss. Es sieht nach einer höheren Wahrscheinlichkeit für 
Zahlen im mittleren Wertebereich aus, weil hier mehr Kombinationen zum 
Zielwert führen. Welche Verteilung konkret herauskommt, kann ich aber 
auf die Schnelle auch nicht sagen. Danke für's Augenöffnen.

von Wilhelm F. (Gast)


Lesenswert?

Martin Michael schrieb:

> Wilhelm F so macht es Delphi Pascal.
>
> c gibt nach help einen halben 0xffff als integer
> also 0x7fff oder 32767 dezimal

Danke.

Sorry, ich lag da etwas falsch, aber mein Taschenrechner HP48G macht es 
so, gibt einen Float-Wert zwischen 0 und 1 zurück. Damit kann man auf 
jeden Fall was anfangen.

Bei meinem SDCC-Compiler schaute ich mal in rand.c hinein. Es stehen 
dort zwei Float-Konstanten drin, die mit irgendwas verwurstelt werden, 
das muß ich noch mal weiter verfolgen. Selbstverständlich wird dort aber 
ein long-Wert zurück gegeben. Es kann sein, daß ein System-Timer des 
8051 dafür betrieben werden muß, im bestimmten Mode, weiß ich auch noch 
nicht exakt genau.

In Assembler machte ich auch schon mal Zufallszahlen für ein 
Würfelspiel, verwendete dort z.B. auch einen 32-bit-Rauschgenerator mit, 
bei dem bit 15 und bit 31 über XOR auf den Input zurück gekoppelt sind, 
und ein paar andere Dinge wie z.B. den Timerwert und einen endlos mit 
laufenden Timer-Tick mit der Laufzeit in ms. Ich hab da einfach mal nur 
ein wenig probiert, verschiedene Dinge da miteinander zu vermengen, 
verrechnen, wie ein neues Essensrezept aus einem Wochen-Werbeblatt, bin 
ja kein Mathematik-Wissenschaftler. Da habe ich oft auch den Eindruck, 
daß Rezepte per Zufallsgenerator zusammen gemengt werden. Die Werte 
wurden getestet, hab von der Software generierte Zahlenreihen in Excel 
hinein geladen, und statistisch ausgewertet. Dafür ist Excel (oder 
OpenOffice Calc) schon gut genug. Die Verteilung war ausgezeichnet, mehr 
will man ja nicht.

Wenn ich noch mal an ein industrielles Erzeugnis heran gesetzt würde, da 
würde ich natürlich durchaus auch nach professionelleren Algorithmen, 
Methoden und Auswertung suchen.

von Hans (Gast)


Lesenswert?

Yalu X. schrieb:
> Theoretisch ja. Praktisch:
>
> Wir sind hier im PC-Forum, auf 32-Bit-PCs ist RAND_MAX typischerweise
> 2**31-1, die rand-Funktion liefert also 2**31 unterschiedliche Werte. Da
> 2**31 ≡ 2 (mod 6), muss in 2 von 2**31 Fällen ein zweiter Wert, in 4 von
> (2**31)**2 ein dritter Wert und in 8 von (2**31)**3 ein vierter Wert
> generiert werden.
>
> Angenommen du möchtest den PC jede Nanosekunde würfeln lassen (dazu muss
> der PC schon ziemlich flott sein). Dann werden nach dem obigen Verfahren
> im Mittel nur alle 40 Milliarden Jahre mehr als 3 rand-Aufrufe pro Wurf
> benötigt. Man kann deswegen die Anzahl der Schleifendurchläufe guten
> Gewissens auf 3 begrenzen.

Praktisch verbirgt sich hinter rand() eh nur ein Pseudozufallsgenerator 
mit in dem Fall maximaler Periodenlänge 2**32. D.h. nach vier Milliarden 
abgerufenen Zufallszahlen treten wieder exakt die gleichen Zahlen in der 
gleichen Reihenfolge auf.

Im Worst-Case kommen die vier unerwünschten Werte jedes Mal 
hintereinander vor, danach kommt dann aber garantiert ein anderer ... :)

von Rolf Magnus (Gast)


Lesenswert?

Eddy Current schrieb:
> Es sieht nach einer höheren Wahrscheinlichkeit für Zahlen im mittleren
> Wertebereich aus, weil hier mehr Kombinationen zum Zielwert führen.
> Welche Verteilung konkret herauskommt, kann ich aber auf die Schnelle
> auch nicht sagen.

http://de.wikipedia.org/wiki/Zentraler_Grenzwertsatz

von Martin M. (ats3788)


Lesenswert?

MMMh wow, so viel zum Thema Würfeln.
Yalu das stimmt nicht, rein Theoretisch kann es passieren.......

Man nehme eine nicht mögliche abstrakte infinity Schleife
und Würfelst, da kann es passieren das Du 100 mal hintereinander sechs 
würfelst und wenn danach gefragt wird wie groß ist die chanche als 
nächstes eine sechs zu würfen. 1:6. Ist aber rein Theortisch also irgend 
wie wenn man das wie mit Primzahlen mit einen super duper Computer 
.......

von Karl H. (kbuchegg)


Lesenswert?

Ich mag mich irren, aber da die Periodenlänge von rand() typischerweise 
bei 2*32 liegt, ist das ganze doch gleichbedeutend mit der Frage: gibt 
es in so einer Periode eine Folge von 4 oder mehr Zahlen, auf die die 
Eigenschaft "größer als RAND_MAX - 2" zutrifft. Wenn nicht, dann ist die 
ganze Fragestellung für diesen konkreten Generator sowieso hinfällig. 
Praktisch gesehen.

Praktisch gesehen ist auch rand() % 6 für den Hausgebrauch gut genug. 
Bei einer professionellen Glücksspiel-Programmierung sieht das natürlich 
anders aus. Aber zur Würfel-Simulation beim Mensch-ärgere-dich-nicht 
reicht es allemal. Die Profis hingegen benutzen ganz sicher nicht rand() 
:-)

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Martin Michael schrieb:
> wie kann man mit C die Zufallszahlen 1 - 6 Würfel erzeugen.

Nimm 4 :-)

http://xkcd.com/221/

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.