Ich versuche ein Spiel mit Würfeln zu machen, und ich muss Zufallszahlen enthalten (um die Seiten des Würfels zu simulieren. Ich weiß, wie man es zwischen 1 und 6 macht). Verwenden von
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
srand((unsigned)time(0));
int i;
i = (rand()%6)+1;
cout << i << "\n";
}
funktioniert nicht sehr gut, denn wenn ich das Programm ein paar Mal starte, bekomme ich folgende Ausgabe:
6
1
1
1
1
1
2
2
2
2
5
2
Ich möchte also einen Befehl, der jedes Mal eine andere Zufallszahl generiert , nicht dieselbe fünfmal hintereinander. Gibt es einen Befehl, der dies erledigt?
Antworten:
Das grundlegendste Problem Ihrer Testanwendung besteht darin, dass Sie
srand
einmal anrufen und dann einmal anrufenrand
und beenden.Der ganze Sinn der
srand
Funktion besteht darin, die Folge von Pseudozufallszahlen mit einem zufälligen Startwert zu initialisieren .Es bedeutet , dass , wenn Sie übergeben den gleichen Wert zu
srand
in zwei verschiedenen Anwendungen (mit der gleichensrand
/rand
Umsetzung) , dann erhalten Sie genau die gleiche Sequenz vonrand()
Werten nach , dass in beiden Anwendungen lesen.In Ihrer Beispielanwendung besteht die Pseudozufallssequenz jedoch nur aus einem Element - dem ersten Element einer Pseudozufallssequenz, die aus einem Startwert erzeugt wird, der der aktuellen
second
Genauigkeitszeit entspricht. Was erwarten Sie dann bei der Ausgabe?Wenn Sie die Anwendung in derselben Sekunde ausführen - Sie verwenden denselben Startwert -, ist Ihr Ergebnis natürlich das gleiche (wie Martin York bereits in einem Kommentar zur Frage erwähnt hat).
Eigentlich sollten Sie einmal anrufen
srand(seed)
und dannrand()
viele Male anrufen und diese Sequenz analysieren - sie sollte zufällig aussehen.BEARBEITEN:
Oh ich verstehe. Anscheinend reicht eine verbale Beschreibung nicht aus (vielleicht Sprachbarriere oder so ... :)).
OK. Altmodisches C-Codebeispiel basierend auf denselben
srand()/rand()/time()
Funktionen, die in der Frage verwendet wurden:^^^ DASS Sequenz aus einem einzigen Durchlauf des Programms soll zufällig aussehen.
EDIT2:
Bei Verwendung der C- oder C ++ - Standardbibliothek ist es wichtig zu verstehen, dass es derzeit keine einzige Standardfunktion oder -klasse gibt, die definitiv zufällige Daten erzeugt (garantiert durch den Standard). Das einzige Standardwerkzeug, das sich diesem Problem nähert, ist std :: random_device , das leider immer noch keine Garantie für die tatsächliche Zufälligkeit bietet.
Abhängig von der Art der Anwendung sollten Sie zunächst entscheiden, ob Sie wirklich zufällige (unvorhersehbare) Daten benötigen. Ein bemerkenswerter Fall, in dem Sie mit Sicherheit echte Zufälligkeit benötigen, ist die Informationssicherheit - z. B. das Generieren symmetrischer Schlüssel, asymmetrischer privater Schlüssel, Salt-Werte, Sicherheitstoken usw.
Zufallszahlen mit Sicherheitsstufe sind jedoch eine separate Branche, die einen separaten Artikel wert ist.
In den meisten Fällen ist der Pseudo-Zufallszahlengenerator ausreichend - z. B. für wissenschaftliche Simulationen oder Spiele. In einigen Fällen ist sogar eine konsistent definierte Pseudozufallssequenz erforderlich. In Spielen können Sie beispielsweise zur Laufzeit genau dieselben Karten erstellen, um zu vermeiden, dass viele Daten gespeichert werden.
Die ursprüngliche Frage und die wiederkehrende Vielzahl identischer / ähnlicher Fragen (und sogar viele fehlgeleitete "Antworten" darauf) zeigen, dass es in erster Linie wichtig ist, Zufallszahlen von Pseudozufallszahlen zu unterscheiden UND zu verstehen, in welcher Pseudozufallszahlenfolge sie sich befinden der erste Ort UND um zu erkennen, dass Pseudozufallszahlengeneratoren NICHT so verwendet werden, wie Sie echte Zufallszahlengeneratoren verwenden könnten.
^^^ DIESE Art von intuitiven Erwartungen ist SEHR FALSCH und schädlich in allen Fällen, in denen Pseudozufallszahlengeneratoren verwendet werden - obwohl sie für echte Zufallszahlen angemessen sind.
Während der sinnvolle Begriff "Zufallszahl" existiert, gibt es keine "Pseudozufallszahl". Ein Pseudo-Zufallszahlengenerator erzeugt tatsächlich Pseudozufallszahlenfolge .
Wenn Experten über die Qualität von PRNG sprechen, sprechen sie tatsächlich über statistische Eigenschaften der generierten Sequenz (und ihrer bemerkenswerten Teilsequenzen). Wenn Sie beispielsweise zwei PRNGs von hoher Qualität kombinieren, indem Sie beide nacheinander verwenden - Sie können eine schlechte resultierende Sequenz erzeugen - obwohl sie jeweils gute Sequenzen erzeugen (diese beiden guten Sequenzen können einfach miteinander korrelieren und somit schlecht kombinieren).
Die pseudozufällige Sequenz ist in der Tat immer deterministisch (vorgegeben durch ihren Algorithmus und ihre Anfangsparameter), dh es ist tatsächlich nichts Zufälliges daran.
Speziell
rand()
/srand(s)
Funktionspaar stellen eine singuläre, nicht threadsichere (!) Pseudozufallszahlenfolge pro Prozess bereit, die mit einem implementierungsdefinierten Algorithmus generiert wird. Die Funktionrand()
erzeugt Werte im Bereich[0, RAND_MAX]
.Zitat aus dem C11-Standard:
Viele Menschen erwarten vernünftigerweise, dass
rand()
dies eine Folge von halbunabhängigen, gleichmäßig verteilten Zahlen im Bereich0
von erzeugen würdeRAND_MAX
. Nun, es sollte auf jeden Fall (ansonsten ist es nutzlos), aber leider erfordert dies nicht nur der Standard - es gibt sogar einen expliziten Haftungsausschluss, der besagt, dass "es keine Garantien für die Qualität der erzeugten Zufallssequenz gibt" . In einigen historischen Fällenrand
/srand
Implementierung war von sehr schlechter Qualität in der Tat. Obwohl es in modernen Implementierungen höchstwahrscheinlich gut genug ist - aber das Vertrauen ist gebrochen und nicht einfach wiederherzustellen. Abgesehen davon, dass es nicht threadsicher ist, ist seine sichere Verwendung in Multithread-Anwendungen schwierig und begrenzt (immer noch möglich - Sie können sie nur von einem dedizierten Thread aus verwenden).Die neue Klassenvorlage std :: mersenne_twister_engine <> (und ihre praktischen typedefs -
std::mt19937
/std::mt19937_64
mit einer guten Kombination von Vorlagenparametern) bietet einen im C ++ 11-Standard definierten Pseudozufallszahlengenerator pro Objekt . Mit denselben Vorlagenparametern und denselben Initialisierungsparametern generieren verschiedene Objekte auf jedem Computer in jeder Anwendung, die mit einer C ++ 11-kompatiblen Standardbibliothek erstellt wurde, genau dieselbe Ausgabesequenz pro Objekt. Der Vorteil dieser Klasse ist die vorhersagbar hohe Ausgabesequenz und die vollständige Konsistenz über alle Implementierungen hinweg.Es gibt auch mehr PRNG-Engines, die im C ++ 11-Standard definiert sind - std :: linear_congruential_engine <> (
srand/rand
in einigen Implementierungen der C-Standardbibliothek historisch als Algorithmus für faire Qualität verwendet) und std :: subtract_with_carry_engine <> . Sie erzeugen auch vollständig definierte parameterabhängige Ausgabesequenzen pro Objekt.Beispiel für modernen C ++ 11-Ersatz für den veralteten C-Code oben:
Die Version des vorherigen Codes, die std :: uniform_int_distribution <> verwendet
quelle
rand()
und zu verwendensrand()
. Kannst du es aktualisieren?rand()
undsrand()
. Tatsächlich beantwortet es die Frage nur mit der bereitgestellten Beschreibung. Aus der Beschreibung (dierand
/ verwendetsrand
) geht hervor, dass die Grundkonzepte der Erzeugung von Pseudozufallszahlen erklärt werden sollten - genau wie die Bedeutung der Pseudozufallssequenz und ihres Keims. Ich versuche , genau das zu tun, und verwenden Sie die einfache und vertrauterand
/srand
Kombination. Das Lustige ist, dass einige andere Antworten - selbst bei sehr hohen Bewertungen - unter denselben Missverständnissen leiden wie der Autor der Frage.std::rand/std::srand
und neue C ++ Bibliothek Funktionen wiestd::random_device<>
, std :: mersenne_twister_engine <> und eine Vielzahl von Zufallsverteilungen erfordern eine Erklärung.Die Verwendung von Modulo kann abhängig vom Zufallszahlengenerator zu einer Verzerrung der Zufallszahlen führen. Siehe diese Frage für weitere Informationen. Natürlich ist es durchaus möglich, sich wiederholende Zahlen in zufälliger Reihenfolge zu erhalten.
Probieren Sie einige C ++ 11-Funktionen für eine bessere Verteilung aus:
Weitere Informationen zu C ++ 11-Zufallszahlen finden Sie in dieser Frage / Antwort. Das Obige ist nicht der einzige Weg, dies zu tun, sondern ein Weg.
quelle
%6
ist verschwindend gering. Vielleicht von Bedeutung, wenn Sie ein Craps-Spiel schreiben, das in Las Vegas verwendet werden soll, aber in fast jedem anderen Kontext keine Konsequenz hat.random_device
undmt19937
bereits unterstützt , gibt es buchstäblich keinen Grund, nicht alles zu tun und auch den Standarduniform_int_distribution
zu verwenden.Wenn Sie Boost- Bibliotheken verwenden, können Sie auf folgende Weise einen Zufallsgenerator erhalten:
Wobei die Funktion
current_time_nanoseconds()
die aktuelle Zeit in Nanosekunden angibt, die als Keim verwendet wird.Hier ist eine allgemeinere Klasse, um zufällige Ganzzahlen und Daten in einem Bereich zu erhalten:
quelle
http://en.cppreference.com/w/cpp/numeric/random/rand
quelle
%6
.) Und wenn Sie sich für die Verwendung derstd::rand
C ++ - API derrand
C-Bibliotheksfunktion entschieden haben, warum dann nichtstd::time
undstd::srand
aus Gründen der Konsistenz des C ++ - Stils?Hier erhalten Sie den vollständigen
Randomer
Klassencode zum Generieren von Zufallszahlen!Wenn Sie Zufallszahlen in verschiedenen Teilen des Projekts benötigen, können Sie eine separate Klasse
Randomer
erstellen, um alle darin enthaltenen Inhalte zusammenzufassenrandom
.Sowas in der Art:
Eine solche Klasse wäre später praktisch:
Sie können diesen Link als Beispiel überprüfen, wie ich eine solche
Randomer
Klasse verwende, um zufällige Zeichenfolgen zu generieren. Sie können auch verwenden,Randomer
wenn Sie möchten.quelle
Anwendungsfall
Ich verglich das Problem der Vorhersagbarkeit mit einer Tüte mit sechs Stück Papier, auf die jeweils ein Wert von 0 bis 5 geschrieben war. Jedes Mal, wenn ein neuer Wert benötigt wird, wird ein Stück Papier aus dem Beutel gezogen. Wenn der Beutel leer ist, werden die Nummern wieder in den Beutel gelegt.
... daraus kann ich eine Art Algorithmus erstellen.
Algorithmus
Eine Tasche ist normalerweise eine
Collection
. Ich habe einbool[]
(auch als boolesches Array, Bitebene oder Bitmap bekannt) ausgewählt, um die Rolle des Beutels zu übernehmen.Der Grund, warum ich mich für a entschieden habe,
bool[]
ist, dass der Index jedes Elements bereits der Wert jedes Blattes Papier ist. Wenn die Papiere etwas anderes erfordern würden, hätte ichDictionary<string, bool>
an seiner Stelle ein verwendet. Der boolesche Wert wird verwendet, um zu verfolgen, ob die Zahl noch gezeichnet wurde oder nicht.Ein aufgerufener Zähler
RemainingNumberCount
wird so initialisiert,5
dass er herunterzählt, wenn eine Zufallszahl ausgewählt wird. Dies erspart uns das Zählen, wie viele Papierstücke jedes Mal übrig bleiben, wenn wir eine neue Zahl zeichnen möchten.Den nächsten Zufallswert zu wählen , ich bin mit
for..loop
scannen durch den Beutel von Indizes und einem Zähler zählen ab , wenn eineindex
wirdfalse
genanntNumberOfMoves
.NumberOfMoves
wird verwendet, um die nächste verfügbare Nummer auszuwählen.NumberOfMoves
wird zuerst als zufälliger Wert zwischen0
und festgelegt5
, da 0,5 Schritte verfügbar sind, die wir durch den Beutel ausführen können. Bei der nächsten IterationNumberOfMoves
wird ein zufälliger Wert zwischen0
und festgelegt4
, da jetzt 0 bis 4 Schritte durch den Beutel ausgeführt werden können. Wenn die Zahlen verwendet werden, verringern sich die verfügbaren Zahlen, sodass wir stattdessenrand() % (RemainingNumberCount + 1)
den nächsten Wert für berechnenNumberOfMoves
.Wenn der
NumberOfMoves
Zähler Null erreicht,for..loop
sollte dies wie folgt sein:for..loop
Index übereinstimmt.false
.for..loop
.Code
Der Code für die obige Lösung lautet wie folgt:
(Fügen Sie die folgenden drei Blöcke nacheinander in die Haupt-CPP-Datei ein.)
Eine Konsolenklasse
Ich erstelle diese Konsolenklasse, weil sie das Umleiten von Ausgaben erleichtert.
Unten im Code ...
... kann ersetzt werden durch ...
... und dann kann diese
Console
Klasse auf Wunsch gelöscht werden.Hauptmethode
Anwendungsbeispiel wie folgt:
Beispielausgabe
Als ich das Programm ausführte, erhielt ich die folgende Ausgabe:
Schlusserklärung
Dieses Programm wurde mit Visual Studio 2017 geschrieben und ich habe mich dafür entschieden, es zu einem
Visual C++ Windows Console Application
Projekt zu machen.Net 4.6.1
.Ich mache hier nichts Besonderes, daher sollte der Code auch in früheren Versionen von Visual Studio funktionieren.
quelle
Hier ist eine Lösung. Erstellen Sie eine Funktion, die die Zufallszahl zurückgibt, und platzieren Sie sie außerhalb der Hauptfunktion, um sie global zu machen. Hoffe das hilft
quelle
Dieser Code erzeugt Zufallszahlen von
n
bism
.Beispiel:
quelle
srand(time(0))
zur Hauptfunktion hinzugefügtrandom(n, m)
?srand(time(0))
zur Hauptfunktion hinzufügen , nicht zur for-Schleife oder innerhalb der Funktionsimplementierung.Wann immer Sie eine einfache Websuche
random number generation
in der Programmiersprache C ++ durchführen, taucht diese Frage normalerweise zuerst auf! Ich möchte meinen Hut in den Ring werfen, um das Konzept der Pseudozufallszahlengenerierung in C ++ für zukünftige Codierer, die unweigerlich dieselbe Frage im Web suchen , hoffentlich besser zu klären !Die Grundlagen
Die Erzeugung von Pseudozufallszahlen umfasst den Prozess der Verwendung eines deterministischen Algorithmus , der eine Folge von Zahlen erzeugt, deren Eigenschaften ungefähr Zufallszahlen ähneln . Ich sage ungefähr ähnlich , weil wahre Zufälligkeit in Mathematik und Informatik ein ziemlich schwer fassbares Rätsel ist . Daher wird der Begriff Pseudozufall verwendet, um pedantisch korrekter zu sein!
Bevor Sie tatsächlich eine PRNG verwenden können, das heißt
pseudo-random number generator
, müssen Sie den Algorithmus mit einem Anfangswert bieten oft auch als bezeichnet Samen . Der Startwert darf jedoch nur einmal gesetzt werden, bevor der Algorithmus selbst verwendet wird!Wenn Sie also eine gute Folge von Zahlen wollen, müssen Sie dem PRNG einen ausreichenden Startwert geben!
Der alte C-Weg
Die abwärtskompatible Standardbibliothek von C, die C ++ hat, verwendet einen sogenannten linearen Kongruenzgenerator, der in der
cstdlib
Header-Datei enthalten ist! Dieses PRNG funktioniert durch eine diskontinuierliche stückweise Funktion, die modulare Arithmetik verwendet, dh einen schnellen Algorithmus, der gerne die verwendetmodulo operator '%'
. Das Folgende ist die übliche Verwendung dieses PRNG in Bezug auf die ursprüngliche Frage, die von @Predictability gestellt wurde:Die allgemeine Verwendung von Cs PRNG birgt eine ganze Reihe von Themen wie:
std::rand()
ist nicht sehr intuitiv für die richtige Erzeugung von Pseudozufallszahlen zwischen einem bestimmten Bereich, z. B. für die Erzeugung von Zahlen zwischen [1, 6], wie es @Predictability wollte.std::rand()
eliminiert aufgrund des Pigeonhole-Prinzips die Möglichkeit einer gleichmäßigen Verteilung von Pseudozufallszahlen .std::rand()
, durch denstd::srand( ( unsigned int )std::time( nullptr ) )
technisch gesät wird, ist nicht korrekt, da ertime_t
als eingeschränkter Typ angesehen wird . Daher ist die Umstellung vontime_t
aufunsigned int
nicht garantiert!Ausführlichere Informationen zu den allgemeinen Problemen bei der Verwendung von PRNG von C und deren mögliche Umgehung finden Sie unter Verwenden von rand () (C / C ++): Hinweise zur rand () -Funktion der C-Standardbibliothek !
Der Standard C ++ Weg
Seit der Veröffentlichung der Norm ISO / IEC 14882: 2011, dh C ++ 11, ist die
random
Bibliothek seit einiger Zeit von der Programmiersprache C ++ getrennt. Diese Bibliothek ist mit mehreren PRNGs und verschiedenen Verteilungstypen ausgestattet, z. B. Gleichverteilung , Normalverteilung , Binomialverteilung usw. Das folgende Beispiel für den Quellcode zeigt eine sehr grundlegende Verwendung derrandom
Bibliothek im Hinblick auf die ursprüngliche Frage von @ Predictability:Die 32-Bit- Mersenne-Twister- Engine mit einer gleichmäßigen Verteilung von Ganzzahlwerten wurde im obigen Beispiel verwendet. (Der Name der Engine im Quellcode klingt seltsam, weil der Name aus der Zeit von 2 ^ 19937-1 stammt.) In diesem Beispiel wird auch
std::random_device
die Engine gesetzt, die ihren Wert vom Betriebssystem erhält (Wenn Sie ein Linux-System verwenden, wirdstd::random_device
ein Wert von zurückgegeben/dev/urandom
).Beachten Sie , dass Sie verwenden müssen nicht
std::random_device
Samen jeden Motor . Sie können Konstanten oder sogar diechrono
Bibliothek verwenden! Sie müssen auch nicht die 32-Bit-Version derstd::mt19937
Engine verwenden, es gibt andere Optionen ! Weitere Informationen zu den Funktionen derrandom
Bibliothek finden Sie unter cplusplus.comAlles in allem sollten C ++ - Programmierer nicht
std::rand()
mehr verwenden, nicht weil es schlecht ist , sondern weil der aktuelle Standard bessere Alternativen bietet, die einfacher und zuverlässiger sind . Hoffentlich finden viele von Ihnen dies hilfreich, insbesondere diejenigen von Ihnen, die kürzlich im Internet gesucht habengenerating random numbers in c++
!quelle
für zufällig jede RUN-Datei
quelle
Hier ist ein einfacher Zufallsgenerator mit ca. gleiche Wahrscheinlichkeit, positive und negative Werte um 0 zu erzeugen:
quelle