Ich benötige eine Funktion, die eine zufällige Ganzzahl in einem bestimmten Bereich (einschließlich Randwerten) generiert. Ich habe keine unangemessenen Qualitäts- / Zufallsanforderungen, ich habe vier Anforderungen:
- Ich brauche es, um schnell zu sein. Mein Projekt muss Millionen (oder manchmal sogar Dutzende Millionen) Zufallszahlen generieren, und meine aktuelle Generatorfunktion hat sich als Engpass erwiesen.
- Ich brauche es, um einigermaßen einheitlich zu sein (die Verwendung von rand () ist vollkommen in Ordnung).
- Die Min-Max-Bereiche können zwischen <0, 1> und <-32727, 32727> liegen.
- es muss siedbar sein.
Ich habe derzeit folgenden C ++ - Code:
output = min + (rand() * (int)(max - min) / RAND_MAX)
Das Problem ist, dass es nicht wirklich einheitlich ist - max wird nur zurückgegeben, wenn rand () = RAND_MAX ist (für Visual C ++ ist es 1/32727). Dies ist ein großes Problem für kleine Bereiche wie <-1, 1>, in denen der letzte Wert fast nie zurückgegeben wird.
Also griff ich nach Stift und Papier und fand die folgende Formel (die auf dem ganzzahligen Rundungstrick (int) (n + 0,5) aufbaut):
Aber es gibt mir immer noch keine gleichmäßige Verteilung. Wiederholte Läufe mit 10000 Proben ergeben ein Verhältnis von 37:50:13 für die Werte -1, 0,1.
Könnten Sie bitte eine bessere Formel vorschlagen? (oder sogar ganze Pseudozufallszahlengeneratorfunktion)
Antworten:
Eine schnelle, etwas bessere als Ihre, aber immer noch nicht richtig gleichmäßig verteilte Lösung ist
Außer wenn die Größe des Bereichs eine Potenz von 2 ist, erzeugt dieses Verfahren unabhängig von der Qualität von voreingenommene ungleichmäßig verteilte Zahlen
rand()
. Lesen Sie dies bitte, um einen umfassenden Test der Qualität dieser Methode zu erhalten .quelle
rand()
sollte in C ++ als schädlich angesehen werden, gibt es viel bessere Möglichkeiten, etwas zu erhalten, das gleichmäßig verteilt und tatsächlich zufällig ist.Die einfachste (und damit beste) C ++ - Antwort (unter Verwendung des 2011er Standards) lautet
Das Rad muss nicht neu erfunden werden. Sie müssen sich keine Sorgen um Voreingenommenheit machen. Sie müssen sich keine Sorgen machen, Zeit als Zufallssamen zu verwenden.
quelle
random_device
, was in einigen Fällen vollständig gebrochen werden kann . Darüber hinaus ist esmt19937
zwar eine sehr gute Allzweckwahl, aber nicht der schnellste Generator guter Qualität (siehe diesen Vergleich ) und daher möglicherweise nicht der ideale Kandidat für das OP.minstd
eine solche Methode geben wird), aber das ist ein Fortschritt. Was die schlechte Implementierung vonrandom_device
- betrifft, ist das schrecklich und sollte als Fehler angesehen werden (möglicherweise auch des C ++ - Standards, wenn es dies zulässt).rand()
keine Option ist und es für die unkritische Verwendung wie das Generieren eines zufälligen Pivot-Index von Bedeutung ist? Außerdem muss ich Sorge haben über den Baurandom_device
/mt19937
/uniform_int_distribution
in einer engen Schleife / inlined Funktion? Sollte ich es vorziehen, sie weiterzugeben?Wenn Ihr Compiler C ++ 0x unterstützt und die Verwendung für Sie eine Option für Sie ist, entspricht der neue Standardheader
<random>
wahrscheinlich Ihren Anforderungen. Es hat eine hohe Qualität,uniform_int_distribution
die minimale und maximale Grenzen akzeptiert (einschließlich nach Bedarf), und Sie können zwischen verschiedenen Zufallszahlengeneratoren wählen, um diese Verteilung anzuschließen.Hier ist Code, der eine Million Zufallszahlen erzeugt, die
int
gleichmäßig in [-57, 365] verteilt sind. Ich habe die neuen Standardfunktionen verwendet<chrono>
, um die Zeit zu bestimmen , da die von Ihnen erwähnte Leistung ein wichtiges Anliegen für Sie ist.Für mich (2,8 GHz Intel Core i5) druckt dies aus:
2.10268e + 07 Zufallszahlen pro Sekunde.
Sie können den Generator festlegen, indem Sie ein int an seinen Konstruktor übergeben:
Wenn Sie später feststellen, dass
int
dies nicht den Bereich abdeckt, den Sie für Ihre Distribution benötigen, können Sie dies beheben, indem Sie Folgendes ändernuniform_int_distribution
(z. B. inlong long
):Wenn Sie später feststellen, dass der
minstd_rand
Generator nicht hoch genug ist, kann er auch problemlos ausgetauscht werden. Z.B:Eine getrennte Kontrolle über den Zufallszahlengenerator und die Zufallsverteilung kann ziemlich befreiend sein.
Ich habe auch die ersten 4 "Momente" dieser Verteilung berechnet (nicht gezeigt) (unter Verwendung
minstd_rand
) und sie mit den theoretischen Werten verglichen , um die Qualität der Verteilung zu quantifizieren:(Das
x_
Präfix bezieht sich auf "erwartet")quelle
d
bei jeder Iteration unterschiedliche Grenzen erstellen müssten? Wie viel würde es die Schleife verlangsamen?Teilen wir das Problem in zwei Teile:
n
im Bereich von 0 bis (max-min).Der erste Teil ist offensichtlich der schwierigste. Nehmen wir an, dass der Rückgabewert von rand () vollkommen einheitlich ist. Wenn Sie Modulo verwenden, werden die ersten
(RAND_MAX + 1) % (max-min+1)
Zahlen verzerrt. Wenn wir also auf magische Weise verändern könntenRAND_MAX
zuRAND_MAX - (RAND_MAX + 1) % (max-min+1)
, es würde nicht mehr Bias.Es stellt sich heraus, dass wir diese Intuition verwenden können, wenn wir bereit sind, Pseudo-Nichtdeterminismus in die Laufzeit unseres Algorithmus einzubeziehen. Immer wenn rand () eine zu große Zahl zurückgibt, fragen wir einfach nach einer anderen Zufallszahl, bis wir eine erhalten, die klein genug ist.
Die Laufzeit wird nun geometrisch verteilt mit Erwartungswert ,
1/p
wop
ist die Wahrscheinlichkeit, eine klein genug Zahl auf dem ersten Versuch zu bekommen. DaRAND_MAX - (RAND_MAX + 1) % (max-min+1)
immer weniger als ist(RAND_MAX + 1) / 2
, wissen wir dasp > 1/2
, so dass die erwartete Anzahl von Iterationen für jeden Bereich immer weniger als zwei beträgt. Mit dieser Technik sollte es möglich sein, auf einer Standard-CPU in weniger als einer Sekunde zig Millionen Zufallszahlen zu generieren.BEARBEITEN:
Obwohl das oben Gesagte technisch korrekt ist, ist die Antwort von DSimon in der Praxis wahrscheinlich nützlicher. Sie sollten dieses Zeug nicht selbst implementieren. Ich habe viele Implementierungen von Ablehnungsstichproben gesehen und es ist oft sehr schwierig zu sehen, ob es korrekt ist oder nicht.
quelle
Wie wäre es mit dem Mersenne Twister ? Die Boost-Implementierung ist recht einfach zu bedienen und in vielen realen Anwendungen gut getestet. Ich habe es selbst in mehreren akademischen Projekten wie künstlicher Intelligenz und evolutionären Algorithmen verwendet.
Hier ist ihr Beispiel, in dem sie eine einfache Funktion zum Werfen eines sechsseitigen Würfels ausführen:
Oh, und hier ist noch ein paar Zuhälter dieses Generators, falls Sie nicht überzeugt sind, dass Sie ihn über den weitaus minderwertigen verwenden sollten
rand()
:quelle
boost::uniform_int
Verteilung), Sie können die Min-Max-Bereiche in alles umwandeln, was Sie möchten, und es ist setzbar.Dies ist eine Zuordnung von 32768 Ganzzahlen zu (nMax-nMin + 1) Ganzzahlen. Das Mapping ist ziemlich gut, wenn (nMax-nMin + 1) klein ist (wie in Ihrer Anforderung). Beachten Sie jedoch, dass die Zuordnung nicht funktioniert, wenn (nMax-nMin + 1) groß ist (z. B. können Sie 32768-Werte nicht mit gleicher Wahrscheinlichkeit 30000-Werten zuordnen). Wenn solche Bereiche benötigt werden, sollten Sie anstelle des 15-Bit-Rand () eine 32-Bit- oder 64-Bit-Zufallsquelle verwenden oder Rand () -Ergebnisse ignorieren, die außerhalb des Bereichs liegen.
quelle
RAND_MAX
diese Option ,(double) RAND_MAX
um eine Ganzzahlüberlaufwarnung zu vermeiden.Hier ist eine unvoreingenommene Version, die Zahlen generiert in
[low, high]
:Wenn Ihr Bereich relativ klein ist, gibt es keinen Grund, die rechte Seite des Vergleichs in der
do
Schleife zwischenzuspeichern.quelle
[0, h)
Einfachheit. Das Aufrufenrand()
hatRAND_MAX + 1
mögliche Rückgabewerte. wobeirand() % h
kollabiert(RAND_MAX + 1) / h
von ihnen zu jedem derh
Ausgangswerte, mit der Ausnahme , dass(RAND_MAX + 1) / h + 1
sie auf die Werte zugeordnet werden , die weniger als(RAND_MAX + 1) % h
(wegen des letzten Teilzyklus durch dieh
Ausgänge). Wir entfernen daher(RAND_MAX + 1) % h
mögliche Ausgaben, um eine unvoreingenommene Verteilung zu erhalten.Ich empfehle die Boost.Random-Bibliothek , sie ist sehr detailliert und gut dokumentiert, lässt Sie explizit angeben, welche Verteilung Sie möchten, und kann in nicht kryptografischen Szenarien eine typische Rand-Implementierung der C-Bibliothek tatsächlich übertreffen .
quelle
Angenommen, min und max sind int-Werte. [und] bedeutet, diesen Wert einzuschließen. (und) bedeutet, diesen Wert nicht einzuschließen. Verwenden Sie oben, um mit c ++ rand () den richtigen Wert zu erhalten.
Referenz: für () [] definieren, besuchen:
https://en.wikipedia.org/wiki/Interval_(mathematics)
Informationen zur Rand- und Randfunktion oder zur Definition von RAND_MAX finden Sie unter:
http://en.cppreference.com/w/cpp/numeric/random/rand
[Minimal Maximal]
(Minimal Maximal]
[Minimal Maximal)
(Minimal Maximal)
quelle
In diesem Thread wurde die Stichprobenauswahl bereits besprochen, aber ich wollte eine Optimierung vorschlagen, die auf der Tatsache basiert, dass
rand() % 2^something
keine Verzerrung eingeführt wird, wie oben bereits erwähnt.Der Algorithmus ist wirklich einfach:
Hier ist mein Beispielcode:
Dies funktioniert besonders gut für kleine Intervalle, da die Potenz von 2 "näher" an der tatsächlichen Intervalllänge liegt und daher die Anzahl der Fehlschläge geringer ist.
PS
Natürlich wäre es effizienter, die Rekursion zu vermeiden (es ist nicht erforderlich, immer wieder über die Protokollobergrenze zu rechnen.), Aber ich dachte, dass dies für dieses Beispiel besser lesbar ist.
quelle
Beachten Sie, dass in den meisten Vorschlägen der anfängliche Zufallswert, den Sie von der Funktion rand () erhalten haben, der normalerweise von 0 bis RAND_MAX reicht, einfach verschwendet wird. Sie erstellen nur eine Zufallszahl daraus, während es eine solide Prozedur gibt, die Ihnen mehr geben kann.
Angenommen, Sie möchten einen [min, max] Bereich mit ganzzahligen Zufallszahlen. Wir beginnen bei [0, max-min]
Nehmen Sie die Basis b = max-min + 1
Beginnen Sie mit der Darstellung einer Zahl, die Sie von rand () in Basis b erhalten haben.
Auf diese Weise haben Sie Floor (log (b, RAND_MAX)), da jede Ziffer in Basis b, außer möglicherweise der letzten, eine Zufallszahl im Bereich [0, max-min] darstellt.
Natürlich ist die endgültige Verschiebung zu [min, max] für jede Zufallszahl r + min einfach.
Wenn NUM_DIGIT die Anzahl der Ziffern in Basis b ist, die Sie extrahieren können, und das ist
dann ist das Obige eine einfache Implementierung des Extrahierens von NUM_DIGIT-Zufallszahlen von 0 bis b-1 aus einer RAND_MAX-Zufallszahl, die b <RAND_MAX liefert.
quelle
Die Formel dafür ist sehr einfach. Probieren Sie diesen Ausdruck aus.
quelle
int num = (int) rand() % (max - min) + min;
Der folgende Ausdruck sollte unvoreingenommen sein, wenn ich mich nicht irre:
Ich gehe hier davon aus, dass rand () einen zufälligen Wert im Bereich zwischen 0,0 und 1,0 ohne 1,0 liefert und dass max und min ganze Zahlen mit der Bedingung sind, dass min <max.
quelle
std::floor
gibt zurückdouble
, und wir brauchen hier einen ganzzahligen Wert. Ich würde nur besetzenint
anstatt zu verwendenstd::floor
.