Generieren Sie Zufallszahlen mit der C ++ 11-Zufallsbibliothek

135

Wie der Titel schon sagt, versuche ich, mithilfe der neuen C ++ 11- <random>Bibliothek einen Weg zu finden, Zufallszahlen zu generieren . Ich habe es mit diesem Code versucht:

std::default_random_engine generator;
std::uniform_real_distribution<double> uniform_distance(1, 10.001);

Das Problem mit dem Code, den ich habe, ist, dass jedes Mal, wenn ich ihn kompiliere und ausführe, immer die gleichen Zahlen generiert werden. Meine Frage ist also, welche anderen Funktionen in der Zufallsbibliothek dies erreichen können, während sie wirklich zufällig sind.

Für meinen speziellen Anwendungsfall habe ich versucht, einen Wert innerhalb des Bereichs zu erhalten [1, 10]

smac89
quelle
3
Diese Frage grenzt gefährlich an "hauptsächlich meinungsbasierte". Wenn Sie die Aufforderung zur Stellungnahme loswerden können, kann ich diese Frage als sehr nützlich ansehen (falls sie noch nicht gestellt wurde).
John Dibling
4
Ich schlage vor, a std::mt19937als Motor zu verwenden, es sei denn, Sie haben einen guten Grund, dies nicht zu tun. Und die Verteilung ist an beiden Enden ein geschlossenes Intervall.
Chris
2
@chris die Verteilung ist nicht an beiden Enden geschlossen, überprüfen Sie diesen Link oder diesen Link
memo1288
1
@ memo1288, Danke, ich dachte, das OP verwendet ein std::uniform_int_distribution, das an beiden Enden geschlossen ist .
Chris

Antworten:

191

Stephan T. Lavavej (stl) von Microsoft hat bei Going Native einen Vortrag darüber gehalten, wie die neuen C ++ 11-Zufallsfunktionen verwendet werden und warum nicht rand(). Darin enthielt er eine Folie, die Ihre Frage im Grunde löst. Ich habe den Code von dieser Folie unten kopiert.

Sie können seinen vollständigen Vortrag hier sehen: http://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

#include <random>
#include <iostream>

int main() {
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_real_distribution<double> dist(1.0, 10.0);

    for (int i=0; i<16; ++i)
        std::cout << dist(mt) << "\n";
}

Wir verwenden random_deviceeinmal, um den genannten Zufallszahlengenerator zu setzen mt. random_device()ist langsamer als mt19937, muss aber nicht gesetzt werden, da zufällige Daten von Ihrem Betriebssystem angefordert werden (die von verschiedenen Orten stammen, wie z. B. RdRand ).


Wenn Sie sich diese Frage / Antwort ansehen , wird anscheinend uniform_real_distributioneine Zahl in dem Bereich zurückgegeben [a, b), in dem Sie möchten [a, b]. Um das zu tun, sollten unsere uniform_real_distibutioneigentlich so aussehen:

std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));
Bill Lynch
quelle
3
Da die Frage nach der allgemeinsten Methode zum Generieren von Zufallszahlen fragt, die Sie möglicherweise nur verwenden default_random_engine
möchten
2
@aaronman: Ich gehe nach STLs Vortrag, wo er das ausdrücklich nicht mag default_random_engine.
Bill Lynch
5
@chris wir alle kennen den Unterschied zwischen einem Vektor und einer Karte, nicht jeder kennt den Unterschied zwischen mt19937 und ranlux24, wenn es jemandem gelungen ist, Programmierer zu werden, ohne zu wissen, was ein Vektor und ein Wörterbuch sind, sollte er vielleicht einen haben std::default_container, hoffentlich gibt es keinen Menschen, die sich als Programmierer betrachten, die die Unterschiede nicht kennen, haben viele Skriptsprachen eine Standardstruktur für den Kartentyp, die auf eine Vielzahl von Arten implementiert werden kann, die der Benutzer möglicherweise nicht kennt
Aaronon
21
Der nextafterAnruf ist für die meisten Anwendungen übertrieben. Die Chancen einer zufälligen doubleLandung genau auf dem Endpunkt sind so gering, dass es keinen praktischen Unterschied zwischen Einschließen und Ausschließen gibt.
Mark Ransom
3
@ Chris Unrelated (aber Sie die Tür geöffnet), Ihre std::vectornicht Analogie hier nicht arbeiten , weil std::vector ist wegen CPU - Caching eigentlich ein guter Standard. Es ist sogar besser std::listals das Einsetzen in die Mitte. Dies gilt auch dann, wenn Sie alle Container verstehen und aufgrund der algorithmischen Komplexität eine fundierte Entscheidung treffen können.
void.pointer
24

Meine 'zufällige' Bibliothek bietet einen äußerst praktischen Wrapper für zufällige C ++ 11-Klassen. Sie können fast alle Dinge mit einer einfachen 'get'-Methode erledigen.

Beispiele:

  1. Zufallszahl in einem Bereich

    auto val = Random::get(-10, 10); // Integer
    auto val = Random::get(10.f, -10.f); // Float point
  2. Zufälliger Boolescher Wert

    auto val = Random::get<bool>( ) // 50% to generate true
    auto val = Random::get<bool>( 0.7 ) // 70% to generate true
  3. Zufälliger Wert aus einer std :: initilizer_list

    auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
  4. Zufälliger Iterator aus dem Iteratorbereich oder dem gesamten Container

    auto it = Random::get( vec.begin(), vec.end() ); // it = random iterator
    auto it = Random::get( vec ); // return random iterator

Und noch mehr Dinge! Schauen Sie sich die Github-Seite an:

https://github.com/effolkronium/random

Ilya Polishchuk
quelle
4

I rot all das Zeug oben, über 40 andere Seiten mit c ++ darin wie dieser und das beobachtete Video von Stephan T. Lavavej „STL“ und war immer noch nicht sicher , wie Zufallszahlen funktionieren in der Praxis , damit ich einen vollen Sonntag nahm , um herauszufinden , Worum geht es und wie es funktioniert und verwendet werden kann.

Meiner Meinung nach hat STL Recht damit, "srand nicht mehr zu verwenden", und er hat es im Video 2 gut erklärt . Er empfiehlt außerdem:

a) void random_device_uniform()- für verschlüsselte Erzeugung, aber langsamer (aus meinem Beispiel)

b) die Beispiele mit mt19937- schneller, Fähigkeit, Samen zu erstellen, nicht verschlüsselt


Ich habe alle beanspruchten C ++ 11-Bücher herausgezogen, auf die ich Zugriff habe, und festgestellt, dass deutsche Autoren wie Breymann (2015) immer noch einen Klon von verwenden

srand( time( 0 ) );
srand( static_cast<unsigned int>(time(nullptr))); or
srand( static_cast<unsigned int>(time(NULL))); or

nur mit <random>statt <time> and <cstdlib>#includings - also sei vorsichtig, nur aus einem Buch zu lernen :).

Bedeutung - das sollte seit c ++ 11 nicht mehr verwendet werden, weil:

Programme benötigen häufig eine Zufallszahlenquelle. Vor dem neuen Standard stützten sich sowohl C als auch C ++ auf eine einfache C-Bibliotheksfunktion namens rand. Diese Funktion erzeugt pseudozufällige Ganzzahlen, die gleichmäßig im Bereich von 0 bis zu einem systemabhängigen Maximalwert von mindestens 32767 verteilt sind. Die Rand-Funktion weist mehrere Probleme auf: Viele, wenn nicht die meisten Programme benötigen Zufallszahlen in einem anderen Bereich als dem eine von rand produziert. Einige Anwendungen erfordern zufällige Gleitkommazahlen. Einige Programme benötigen Zahlen, die eine ungleichmäßige Verteilung widerspiegeln. Programmierer führen häufig Nicht-Zufälligkeit ein, wenn sie versuchen, den Bereich, den Typ oder die Verteilung der von Rand generierten Zahlen zu transformieren. (Zitat aus Lippmans C ++ Primer fünfte Ausgabe 2012)


Ich fand schließlich die beste Erklärung aus 20 Büchern in Bjarne Stroustrups neueren - und er sollte sich damit auskennen - in "Eine Tour durch C ++ 2019", "Programmierprinzipien und Übung mit C ++ 2016" und "The C ++ Programming Language 4th Edition" 2014 "und auch einige Beispiele in" Lippmans C ++ Primer fünfte Ausgabe 2012 ":

Und es ist wirklich einfach, weil ein Zufallszahlengenerator aus zwei Teilen besteht: (1) einer Engine, die eine Folge von Zufalls- oder Pseudozufallswerten erzeugt. (2) eine Verteilung, die diese Werte in eine mathematische Verteilung in einem Bereich abbildet.


Trotz der Meinung von Microsofts STL schreibt Bjarne Stroustrups:

In bietet die Standardbibliothek Zufallszahlen-Engines und -Verteilungen (§24.7). Verwenden Sie standardmäßig die default_random_engine, die für eine breite Anwendbarkeit und niedrige Kosten ausgewählt wurde.

Das void die_roll()Beispiel stammt von Bjarne Stroustrups - eine gute Idee, mit der Engine und Distribution generiert werden using (mehr dazu hier) .


Um die Zufallszahlengeneratoren, die von der Standardbibliothek hier bereitgestellt werden, in der Praxis nutzen zu können, wurde <random> hier ein ausführbarer Code mit verschiedenen Beispielen auf das Mindeste reduziert, was hoffentlich Zeit und Geld für Sie sicher macht:

    #include <random>     //random engine, random distribution
    #include <iostream>   //cout
    #include <functional> //to use bind

    using namespace std;


    void space() //for visibility reasons if you execute the stuff
    {
       cout << "\n" << endl;
       for (int i = 0; i < 20; ++i)
       cout << "###";
       cout << "\n" << endl;
    }

    void uniform_default()
    {
    // uniformly distributed from 0 to 6 inclusive
        uniform_int_distribution<size_t> u (0, 6);
        default_random_engine e;  // generates unsigned random integers

    for (size_t i = 0; i < 10; ++i)
        // u uses e as a source of numbers
        // each call returns a uniformly distributed value in the specified range
        cout << u(e) << " ";
    }

    void random_device_uniform()
    {
         space();
         cout << "random device & uniform_int_distribution" << endl;

         random_device engn;
         uniform_int_distribution<size_t> dist(1, 6);

         for (int i=0; i<10; ++i)
         cout << dist(engn) << ' ';
    }

    void die_roll()
    {
        space();
        cout << "default_random_engine and Uniform_int_distribution" << endl;

    using my_engine = default_random_engine;
    using my_distribution = uniform_int_distribution<size_t>;

        my_engine rd {};
        my_distribution one_to_six {1, 6};

        auto die = bind(one_to_six,rd); // the default engine    for (int i = 0; i<10; ++i)

        for (int i = 0; i <10; ++i)
        cout << die() << ' ';

    }


    void uniform_default_int()
    {
       space();
       cout << "uniform default int" << endl;

       default_random_engine engn;
       uniform_int_distribution<size_t> dist(1, 6);

        for (int i = 0; i<10; ++i)
        cout << dist(engn) << ' ';
    }

    void mersenne_twister_engine_seed()
    {
        space();
        cout << "mersenne twister engine with seed 1234" << endl;

        //mt19937 dist (1234);  //for 32 bit systems
        mt19937_64 dist (1234); //for 64 bit systems

        for (int i = 0; i<10; ++i)
        cout << dist() << ' ';
    }


    void random_seed_mt19937_2()
    {
        space();
        cout << "mersenne twister split up in two with seed 1234" << endl;

        mt19937 dist(1234);
        mt19937 engn(dist);

        for (int i = 0; i < 10; ++i)
        cout << dist() << ' ';

        cout << endl;

        for (int j = 0; j < 10; ++j)
        cout << engn() << ' ';
    }



    int main()
    {
            uniform_default(); 
            random_device_uniform();
            die_roll();
            random_device_uniform();
            mersenne_twister_engine_seed();
            random_seed_mt19937_2();
        return 0;
    }

Ich denke, das summiert sich auf alles und wie gesagt, ich habe eine Menge gelesen und Zeit gebraucht, um diese Beispiele zu destillieren. Wenn Sie weitere Informationen zur Zahlengenerierung haben, freue ich mich, dies per PM oder im Kommentarbereich zu erfahren und wird es bei Bedarf hinzufügen oder diesen Beitrag bearbeiten. Bool

Ivanovic
quelle
0

Hier ist etwas, das ich gerade in diese Richtung geschrieben habe:

#include <random>
#include <chrono>
#include <thread>

using namespace std;

//==============================================================
// RANDOM BACKOFF TIME
//==============================================================
class backoff_time_t {
  public:
    random_device                      rd;
    mt19937                            mt;
    uniform_real_distribution<double>  dist;

    backoff_time_t() : rd{}, mt{rd()}, dist{0.5, 1.5} {}

    double rand() {
      return dist(mt);
    }
};

thread_local backoff_time_t backoff_time;


int main(int argc, char** argv) {
   double x1 = backoff_time.rand();
   double x2 = backoff_time.rand();
   double x3 = backoff_time.rand();
   double x4 = backoff_time.rand();
   return 0;
}

~

Bill Moore
quelle
0

Hier ist eine Ressource, die Sie über den Pseudozufallszahlengenerator lesen können.

https://en.wikipedia.org/wiki/Pseudorandom_number_generator

Grundsätzlich benötigen Zufallszahlen im Computer einen Startwert (diese Zahl kann die aktuelle Systemzeit sein).

Ersetzen

std::default_random_engine generator;

Durch

std::default_random_engine generator(<some seed number>);
Ngọc Khánh Nguyễn
quelle
-3

Sie haben zwei häufige Situationen. Das erste ist, dass Sie Zufallszahlen wollen und sich nicht zu sehr um die Qualität oder Ausführungsgeschwindigkeit kümmern. Verwenden Sie in diesem Fall das folgende Makro

#define uniform() (rand()/(RAND_MAX + 1.0))

das gibt Ihnen p im Bereich von 0 bis 1 - epsilon (es sei denn, RAND_MAX ist größer als die Genauigkeit eines Doppels, aber machen Sie sich darüber Sorgen, wenn Sie dazu kommen).

int x = (int) (uniform () * N);

Gibt nun eine zufällige ganze Zahl von 0 bis N -1 an.

Wenn Sie andere Distributionen benötigen, müssen Sie p transformieren. Oder manchmal ist es einfacher, uniform () mehrmals aufzurufen.

Wenn Sie wiederholbares Verhalten wünschen, setzen Sie mit einer Konstanten, andernfalls mit einem Aufruf von time ().

Wenn Sie sich jetzt Gedanken über Qualität oder Laufzeitleistung machen, schreiben Sie uniform () neu. Aber ansonsten berühren Sie den Code nicht. Halten Sie immer uniform () auf 0 bis 1 minus Epsilon. Jetzt können Sie die C ++ - Zufallszahlenbibliothek umbrechen, um eine bessere Uniform () zu erstellen. Dies ist jedoch eine Art Option auf mittlerer Ebene. Wenn Sie sich Gedanken über die Eigenschaften des RNG machen, lohnt es sich auch, ein wenig Zeit zu investieren, um zu verstehen, wie die zugrunde liegenden Methoden funktionieren, und dann eine bereitzustellen. Sie haben also die vollständige Kontrolle über den Code und können garantieren, dass die Reihenfolge mit demselben Startwert immer exakt gleich ist, unabhängig von der Plattform oder der Version von C ++, mit der Sie verknüpfen.

Malcolm McLean
quelle
3
Nur dass das nicht einheitlich ist (0 bis N-1). Der Grund ist einfach, nehmen wir an, N = 100 und RAND_MAX = 32758. Es gibt keine Möglichkeit, 32758 Elemente (RAND_MAX) einheitlich auf 100 Eingänge abzubilden. Der einzigartige Weg ist eine Grenze für 32000 gesetzt und rand () erneut auszuführen, wenn es außerhalb der Grenzen liegt
amchacon
1
Wenn N 100 ist, muss Ihr RNG extrem gut sein, um die Abweichung von einer flachen Verteilung erkennen zu können.
Malcolm McLean