Dies ist eine Fortsetzung einer zuvor gestellten Frage:
Wie generiere ich eine Zufallszahl in C?
Ich möchte in der Lage sein, eine Zufallszahl aus einem bestimmten Bereich wie 1 bis 6 zu generieren, um die Seiten eines Würfels nachzuahmen.
Wie würde ich das machen?
Antworten:
Alle bisherigen Antworten sind mathematisch falsch. Die Rückgabe
rand() % N
gibt nicht einheitlich eine Zahl im Bereich an, es[0, N)
sei denn,N
die Länge des Intervalls, in das dierand()
Rückgabe erfolgt (dh eine Potenz von 2). Außerdem hat man keine Ahnung, ob die Module vonrand()
unabhängig sind: Es ist möglich, dass sie gehen0, 1, 2, ...
, was einheitlich, aber nicht sehr zufällig ist. Die einzige Annahme, die vernünftig erscheint, ist die, dassrand()
eine Poisson-Verteilung ausgegeben wird: Zwei beliebige nicht überlappende Teilintervalle derselben Größe sind gleich wahrscheinlich und unabhängig. Für eine endliche Menge von Werten impliziert dies eine gleichmäßige Verteilung und stellt auch sicher, dass die Werte vonrand()
gut verteilt sind.Dies bedeutet, dass der einzig richtige Weg, den Bereich von
rand()
zu ändern, darin besteht, ihn in Kästchen zu unterteilen. WennRAND_MAX == 11
Sie beispielsweise einen Bereich von möchten1..6
, sollten Sie{0,1}
1,{2,3}
2 usw. zuweisen . Dies sind disjunkte, gleich große Intervalle und somit gleichmäßig und unabhängig verteilt.Der Vorschlag, eine Gleitkommadivision zu verwenden, ist mathematisch plausibel, weist jedoch im Prinzip Rundungsprobleme auf. Vielleicht
double
ist die Präzision hoch genug, damit es funktioniert. vielleicht nicht. Ich weiß es nicht und ich möchte es nicht herausfinden müssen; In jedem Fall ist die Antwort systemabhängig.Der richtige Weg ist die Verwendung von Ganzzahlarithmetik. Das heißt, Sie möchten so etwas wie das Folgende:
Die Schleife ist notwendig, um eine perfekt gleichmäßige Verteilung zu erhalten. Wenn Sie beispielsweise Zufallszahlen von 0 bis 2 erhalten und nur solche von 0 bis 1 möchten, ziehen Sie einfach weiter, bis Sie keine 2 mehr erhalten. Es ist nicht schwer zu überprüfen, ob dies mit gleicher Wahrscheinlichkeit 0 oder 1 ergibt. Diese Methode wird auch in dem Link beschrieben, den nos in ihrer Antwort angegeben haben, obwohl sie unterschiedlich codiert sind. Ich verwende
random()
eher alsrand()
weil es eine bessere Verteilung hat (wie auf der Manpage für angegebenrand()
).Wenn Sie zufällige Werte außerhalb des Standardbereichs erhalten möchten,
[0, RAND_MAX]
müssen Sie etwas Schwieriges tun. Vielleicht ist die zweckmäßigste ist eine Funktion zu definieren ,random_extended()
die ziehtn
Bits (mitrandom_at_most()
) und kehrt in[0, 2**n)
, und wenden Sie dannrandom_at_most()
mitrandom_extended()
anstelle vonrandom()
(und2**n - 1
anstelle vonRAND_MAX
als) zu ziehen , um einen zufälligen Wert weniger2**n
, vorausgesetzt , Sie einen numerischen Typen haben, so halten kann , ein Wert. Schließlich, natürlich können Sie Werte erhalten in[min, max]
Verwendungmin + random_at_most(max - min)
, einschließlich negativer Werte.quelle
max - min > RAND_MAX
, was schwerwiegender ist als das oben angegebene Problem (z. B. hat VC ++RAND_MAX
nur 32767).do {} while()
.Nach der Antwort von @Ryan Reich dachte ich, ich würde meine bereinigte Version anbieten. Die Prüfung der ersten Grenzen ist angesichts der Prüfung der zweiten Grenzen nicht erforderlich, und ich habe sie eher iterativ als rekursiv gemacht. Es gibt Werte im Bereich [min, max] zurück, wobei
max >= min
und1+max-min < RAND_MAX
.quelle
limit
ein int (und optionalbucket
auch) seitRAND_MAX / range
<INT_MAX
undbuckets * range
<= erstellt wirdRAND_MAX
. BEARBEITEN: Ich habe einen Vorschlag eingereicht und bearbeitet.Hier ist eine Formel, wenn Sie die Max- und Min-Werte eines Bereichs kennen und Zahlen einschließlich zwischen den Bereichen generieren möchten:
quelle
int
Überlauf mitmax+1-min
.Sehen Sie hier für weitere Optionen.
quelle
(((max-min+1)*rand())/RAND_MAX)+min
genau dieselbe Verteilung verwenden und wahrscheinlich erhalten (vorausgesetzt, RAND_MAX ist relativ zu int klein genug, um nicht überzulaufen).max + 1
, wenn entwederrand() == RAND_MAX
oderrand()
ist ganz in der NäheRAND_MAX
und Fließkommafehler schieben die Endergebnis Vergangenheitmax + 1
. Um sicher zu gehen, sollten Sie überprüfen, ob das Ergebnis innerhalb des Bereichs liegt, bevor Sie es zurücksenden.RAND_MAX + 1.0
. Ich bin mir immer noch nicht sicher, ob das gut genug ist, um einemax + 1
Rückkehr zu verhindern : Insbesondere beinhaltet das+ min
am Ende eine Runde, diemax + 1
für große Werte von rand () produzieren könnte. Es ist sicherer, diesen Ansatz ganz aufzugeben und ganzzahlige Arithmetik zu verwenden.RAND_MAX
durch ersetzt wird,RAND_MAX+1.0
wie Christoph vorschlägt, dann glaube ich, dass dies sicher ist, vorausgesetzt, dass+ min
dies mit ganzzahliger Arithmetik erfolgt :return (unsigned int)((max - min + 1) * scaled) + min
. Der (nicht offensichtliche) Grund ist, dass unter der Annahme von IEEE 754-Arithmetik und Rund-Halb-zu-Gerade (und auch dasmax - min + 1
ist genau als Doppel darstellbar, aber das wird auf einer typischen Maschine zutreffen) es immer wahr ist, dassx * scaled < x
für jedes positive Doppelx
und jedes doppeltescaled
befriedigend0.0 <= scaled && scaled < 1.0
.randr(0, UINT_MAX)
Würden Sie nicht einfach tun:
%
ist der Moduloperator. Im Wesentlichen wird es nur durch 6 geteilt und der Rest ... von 0 - 5 zurückgegebenquelle
rand()
die niederwertigen Bits des Generatorzustands enthalten sind (wenn ein LCG verwendet wird). Ich habe bisher noch keine gesehen - alle (ja, einschließlich MSVC mit RAND_MAX nur 32767) entfernen die niederwertigen Bits. Die Verwendung des Moduls wird aus anderen Gründen nicht empfohlen, da die Verteilung zugunsten kleinerer Zahlen verzerrt wird.Für diejenigen, die das Bias-Problem verstehen, aber die unvorhersehbare Laufzeit von auf Zurückweisung basierenden Methoden nicht ertragen können, erzeugt diese Reihe eine zunehmend weniger voreingenommene zufällige Ganzzahl im
[0, n-1]
Intervall:Dies geschieht durch Synthetisieren einer hochpräzisen Festpunkt-Zufallszahl von
i * log_2(RAND_MAX + 1)
Bits (wobeii
die Anzahl der Iterationen ist) und Durchführen einer langen Multiplikation mitn
.Wenn die Anzahl der Bits im Vergleich zu ausreichend groß ist,
n
wird die Vorspannung unermesslich klein.Es spielt keine Rolle, ob
RAND_MAX + 1
es kleiner alsn
(wie in dieser Frage ) ist oder ob es keine Zweierpotenz ist, aber es muss darauf geachtet werden, einen ganzzahligen Überlauf zu vermeiden, wenn erRAND_MAX * n
groß ist.quelle
RAND_MAX
ist oftINT_MAX
soRAND_MAX + 1
-> UB (wie INT_MIN)RAND_MAX * n
groß ist". Sie müssen festlegen, dass für Ihre Anforderungen geeignete Typen verwendet werden.RAND_MAX
ist oftINT_MAX
" Ja, aber nur auf 16-Bit-Systemen! Jede einigermaßen moderne Architektur wirdINT_MAX
auf 2 ^ 32/2 undRAND_MAX
auf 2 ^ 16/2 gesetzt. Ist dies eine falsche Annahme?int
Compiler getestet , fand ichRAND_MAX == 32767
auf einem undRAND_MAX == 2147483647
auf einem anderen. Meine allgemeine Erfahrung (Jahrzehnte) ist dasRAND_MAX == INT_MAX
öfter. Seien Sie also anderer Meinung, dass eine einigermaßen moderne 32-Bit-Architektur mit Sicherheit eineRAND_MAX
Rolle spielen wird2^16 / 2
. Da die C-Spezifikation dies zulässt32767 <= RAND_MAX <= INT_MAX
, codiere ich sowieso eher darauf als auf eine Tendenz.Um die in anderen Antworten vorgeschlagene Modulo-Verzerrung zu vermeiden, können Sie immer Folgendes verwenden:
Wobei "MAX" die Obergrenze und "MIN" die Untergrenze ist. Zum Beispiel für Zahlen zwischen 10 und 20:
Einfache Lösung und besser als "rand ()% N".
quelle
#include <bsd/stdlib.h>
zuerst müssen. Haben Sie auch eine Idee, wie Sie dies unter Windows ohne MinGW oder CygWin erreichen können?Hier ist ein etwas einfacherer Algorithmus als die Lösung von Ryan Reich:
quelle
RAND_MAX + 1
kann leicht überlaufenint
. In diesem Fall(RAND_MAX + 1) % range
werden fragwürdige Ergebnisse generiert. Betrachten Sie(RAND_MAX + (uint32_t)1)
Während Ryan richtig ist, kann die Lösung viel einfacher sein, basierend auf dem, was über die Quelle der Zufälligkeit bekannt ist. So geben Sie das Problem erneut an:
[0, MAX)
mit gleichmäßiger Verteilung ausgibt .[rmin, rmax]
dem0 <= rmin < rmax < MAX
.Wenn nach meiner Erfahrung die Anzahl der Behälter (oder "Kisten") erheblich kleiner als der Bereich der ursprünglichen Zahlen ist und die ursprüngliche Quelle kryptografisch stark ist, besteht keine Notwendigkeit, all diese Rigamarole durchzugehen, und eine einfache Modulo-Teilung würde dies tun genügen (wie
output = rnd.next() % (rmax+1)
wennrmin == 0
) und erzeugen Zufallszahlen, die gleichmäßig "genug" und ohne Geschwindigkeitsverlust verteilt sind. Der Schlüsselfaktor ist die Zufallsquelle (dh Kinder, versuchen Sie dies nicht zu Hause mitrand()
).Hier ist ein Beispiel / ein Beweis dafür, wie es in der Praxis funktioniert. Ich wollte Zufallszahlen von 1 bis 22 generieren, mit einer kryptografisch starken Quelle, die zufällige Bytes erzeugt (basierend auf Intel RDRAND). Die Ergebnisse sind:
Dies ist so nahe an der Uniform, wie ich es für meinen Zweck benötige (fairer Würfelwurf, Generierung kryptografisch starker Codebücher für Verschlüsselungsmaschinen des Zweiten Weltkriegs wie http://users.telenet.be/d.rijmenants/en/kl-7sim.htm usw. ). Der Ausgang zeigt keine nennenswerte Vorspannung.
Hier ist die Quelle des kryptografisch starken (wahren) Zufallszahlengenerators: Intel Digital Random Number Generator und ein Beispielcode, der 64-Bit-Zufallszahlen (ohne Vorzeichen) erzeugt.
Ich habe es unter Mac OS X mit clang-6.0.1 (gerade) und mit gcc-4.8.3 mit dem Flag "-Wa, q" kompiliert (da GAS diese neuen Anweisungen nicht unterstützt).
quelle
gcc randu.c -o randu -Wa,q
(GCC 5.3.1 unter Ubuntu 16) oderclang randu.c -o randu
(Clang 3.8.0) funktioniert, aber Dumps Core zur Laufzeit mitIllegal instruction (core dumped)
. Irgendwelche Ideen?rand()
. Ich habe einige Tests ausprobiert und diese Frage gestellt , kann aber noch keine endgültige Antwort finden.Wie bereits erwähnt, reicht Modulo nicht aus, da es die Verteilung verzerrt. Hier ist mein Code, der Bits maskiert und verwendet, um sicherzustellen, dass die Verteilung nicht verzerrt ist.
Mit dem folgenden einfachen Code können Sie sich die Verteilung ansehen:
quelle
v = rand(); if (v > RAND_MAX - (RAND_MAX % range) -> reject and try again; else return v % range;
Ich verstehe, dass Modulo eine viel langsamere Operation als das Maskieren ist, aber ich denke immer noch ... es sollte getestet werden.rand()
gibt einint
im Bereich zurück[0..RAND_MAX]
. Dieser Bereich kann leicht ein Unterbereich von seinuint32_t
undrandomInRange(0, ,b)
erzeugt dann niemals Werte in dem Bereich(INT_MAX...b]
.Gibt eine Gleitkommazahl im Bereich [0,1] zurück:
quelle