Ich habe 3 Frage zu srand(), rand() und %.



  • Hallo,
    ich lerne derzeitig C++ und ich bin auf srand(), rand() und % gestoßen. Ich kann sagen als was srand() und rand() fungieren, aber nicht wie die Initialisierung einzutragen ist. srand() stellt uns einen Zufallsgenerator zur Verfügung, rand() stellt uns Zufallszahlen zur Verfügung und % ist die Moduladivision die ich nie so richtig verstanden habe.

    Ich lerne anhand von Beispielen relativ schnell. Deshalb würde ich mich über Beispiele freuen :).

    Mit freundlichen Grüßen



  • srand() stellt uns einen Zufallsgenerator zur Verfügung

    Nein. Das 's' in srand steht für seed - srand seedet den Zufallsgenerator.
    (Den Begriff seeden kannst du mal googlen).

    rand() stellt uns Zufallszahlen zur Verfügung

    *Pseudozufallszahlen 😉

    und % ist die Moduladivision die ich nie so richtig verstanden habe.

    Das ist gar nicht gut. Der Modulo ist in der Programmierung und in der Informatik allgemein sehr wichtig, den solltest du dir wirklich mal ansehen.

    Gut, du bist also der induktive Typ, kein Problem.

    Ich bin jetzt zu faul. 😃 Warte kurz, hier kommt bestimmt gleich jemand.



  • std::srand initialisiert den Pseudo-Zufallszahlengenerator std::rand(). Das macht man einmal zu Programmanfang, danach gibt rand() eine vom Seed abhängige Reihe von "Zufalls"-Zahlen zurück. Wenn man immer eine andere Reihe haben will, wirft man da üblicherweise die Systemzeit rein, das ist std::time(0). Will man immer die gleiche Reihe, muss man den gleichen Seed nehmen -- das ist aber eher selten gewollt.

    Der %-Operator gibt auf einfachen Integern den Rest der Division zurück. 10 / 3 = 3 Rest 1, also 10 % 3 = 1. 256 / 10 = 25 Rest 6, also 256 % 10 = 6.

    Der Rest einer Division durch n ist zwischen 0 und n - 1, also ist std::rand() % n ein einfacher Weg, Zufallszahlen zwischen 0 und n - 1 zu kriegen. Für Würfel beispielsweise std::rand() % 6 + 1.

    Codebeispiel:

    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    
    int main() {
      std::cout << "Reihe 1\n\n";
    
      std::srand(1234); // Fester Seed
    
      for(int i = 0; i < 20; ++i) {
        std::cout << std::rand() % 6 << '\n';
      }
    
      ////////////////////////////
    
      std::cout << "\n\nReihe 2\n\n";
    
      std::srand(1234); // Fester Seed
    
      for(int i = 0; i < 20; ++i) {
        std::cout << std::rand() % 6 << '\n';
      }
    
      ////////////////////////////
    
      std::cout << "\n\nReihe 3 (Seed aus Systemzeit)\n\n";
    
      std::srand(std::time(0));
    
      for(int i = 0; i < 20; ++i) {
        std::cout << std::rand() % 6 << '\n';
      }
    }
    

    Allerdings gibt es im neuen C++-Standard sehr viel bessere Mechanismen als den alten, von C geerbten linearen Kongruenzgenerator mit globalem Zustand. Dort wird der Generator von der Verteilung getrennt gehalten, und der Generatorzustand als Objekt. Das sieht beispielsweise so aus:

    #include <ctime>
    #include <iostream>
    #include <random>
    
    int main() {
      std::mt19937 rng(std::time(0));                   // Mersenne-Twister-Generator (Periode 2^19937)
      std::uniform_int_distribution<int> wuerfel(1, 6); // gleichverteilte Integer zwischen 1 und 6
      std::normal_distribution<double> normalverteilt;  // Normalverteilte Fließkommazahlen
    
      for(int i = 0; i < 20; ++i) {
        std::cout << wuerfel(rng) << '\n';
        std::cout << normalverteilt(rng) << '\n';
      }
    }
    

    Da gibt's eine Menge Verteilungen und Generatortypen. Mehr hier: http://en.cppreference.com/w/cpp/numeric/random



  • Gut, du bist also der induktive Typ, kein Problem.

    Ich bin nicht absichtlich der induktive Typ. Ich habe es aus einem Buch gelernt ich weiß nicht woher ich die gesamten Begrifflichkeiten hehr holen sollte und ich ging davon aus das es richtig sei was ich im Buch lerne -.



  • Der %-Operator gibt auf einfachen Integern den Rest der Division zurück. 10 / 3 = 3 Rest 1, also 10 % 3 = 1. 256 / 10 = 25 Rest 6, also 256 % 10 = 6.

    Das habe ich verstanden.

    Der Rest einer Division durch n ist zwischen 0 und n - 1, also ist std::rand() % n ein einfacher Weg, Zufallszahlen zwischen 0 und n - 1 zu kriegen. Für Würfel beispielsweise std::rand() % 6 + 1.
    

    Das habe ich verstanden.

    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    
    int main() {
      std::cout << "Reihe 1\n\n";
    
      std::srand(1234); // Fester Seed
    
      for(int i = 0; i < 20; ++i) {
        std::cout << std::rand() % 6 << '\n';
      }
    
      ////////////////////////////
    
      std::cout << "\n\nReihe 2\n\n";
    
      std::srand(1234); // Fester Seed
    
      for(int i = 0; i < 20; ++i) {
        std::cout << std::rand() % 6 << '\n';
      }
    
      ////////////////////////////
    
      std::cout << "\n\nReihe 3 (Seed aus Systemzeit)\n\n";
    
      std::srand(std::time(0));
    
      for(int i = 0; i < 20; ++i) {
        std::cout << std::rand() % 6 << '\n';
      }
    }
    

    Verstehe ich soweit, aber was ist ein seed? Kam in meinem Buch bisher noch nicht vor.



  • Anfänger53 schrieb:

    Ich verstehe nicht wieso du bei std::rand() % 6 + 1. + 1 mit berechnest.

    Weil man Zahlen zwischen 1 und 6 haben will und nicht zwischen 0 und 5. Mit % kann man aber nur Zahlen zwischen 0 und x berechnen, deshalb verschiebt man das Interval hinterher.

    Anfänger53 schrieb:

    Verstehe ich soweit, aber was ist ein seed? Kam in meinem Buch bisher noch nicht vor.

    Ein Pseudozufallszahlengenerator hat irgendwo einen "State", der beim Ziehen einer Zahl aktualisiert wird. Dieser State wird beim Seeden initialisiert. Würde man den immer gleich initialisieren, würde man auch immer die gleichen Zufallszahlen bekommen, wie seldon schon geschrieben hat.



  • Ein Pseudozufallszahlengenerator wendet immer Operationen auf eine Zahl an um sie so zu verändern, dass sie möglichst wenig mit der ursprünglichen Zahl gemeinsam hat. Seed ist der Startwert, mit dem es losgeht.

    srand() setzt den Startwert, rand() verändert die Zahl (den Status) und gibt sie zurück.



  • dass sie möglichst wenig mit der ursprünglichen Zahl gemeinsam hat.

    Das ist eine komische Definition, denn dafür muss erstmal klar sein was man so gemeinsam haben kann.

    Besser wäre: Der PRNG verändert die Zahl und die darauffolgenden so dass es dem menschlichen Betrachter als völlig zufällige und muster-lose Zahlenfolge erscheint.



  • Ethon schrieb:

    Ein Pseudozufallszahlengenerator wendet immer Operationen auf eine Zahl an um sie so zu verändern, dass sie möglichst wenig mit der ursprünglichen Zahl gemeinsam hat. Seed ist der Startwert, mit dem es losgeht.

    srand() setzt den Startwert, rand() verändert die Zahl (den Status) und gibt sie zurück.

    Es ist irrelevant was ich als seed eingebe, weil es sich später wieder verändert durch rand(). Die Modul Division in diesem Sachverhalt hat den Zweck einen Endwert zu setzen. Demzufolge sollte das richtig sein --> Startwert <= rand() <= Endwert.
    Ist es richtig?



  • Anfänger53 schrieb:

    Es ist irrelevant was ich als seed eingebe, weil es sich später wieder verändert durch rand().

    Falsch! Nimm mal srand(0) und lasse dein Programm ein paar Mal laufen. Du wirst dann feststellen, dass rand() immer die gleichen Zahlen generiert.

    Stelle es dir so vor:
    Ein Zufallsgenerator generiert basierend auf einem Startwert (Seed Wert) voellig deterministisch Zahlen - der Algortihmsu beinhaltet nichts zufaelliges.
    Das einzige 'zufaellige' ist der Startwert. Und eine einfache Moeglichkeit einen (mehr oder weniger zufaelligen) Startwert zu bekommen ist die aktuelle Zeit zu nehmen.



  • Falsch! Nimm mal srand(0) und lasse dein Programm ein paar Mal laufen. Du wirst dann feststellen, dass rand() immer die gleichen Zahlen generiert.

    Stelle es dir so vor:
    Ein Zufallsgenerator generiert basierend auf einem Startwert (Seed Wert) voellig deterministisch Zahlen - der Algortihmsu beinhaltet nichts zufaelliges.
    Das einzige 'zufaellige' ist der Startwert. Und eine einfache Moeglichkeit einen (mehr oder weniger zufaelligen) Startwert zu bekommen ist die aktuelle Zeit zu nehmen.

    Ich habe es verstanden.



  • Danke an all diejenigen dir mir geholfen haben.



  • Sone schrieb:

    dass sie möglichst wenig mit der ursprünglichen Zahl gemeinsam hat.

    Das ist eine komische Definition, denn dafür muss erstmal klar sein was man so gemeinsam haben kann.

    Besser wäre: Der PRNG verändert die Zahl und die darauffolgenden so dass es dem menschlichen Betrachter als völlig zufällige und muster-lose Zahlenfolge erscheint.

    Das ist eine spannende Frage, was mathematisch als zufällig gelten kann. Donald Knuth bespricht das Thema in "The Art of Computer Programming", Band 2, über 35 dicht mit Mathematik bepackte Seiten, was einen Eindruck davon vermittelt, dass es nicht ganz trivial ist.

    Wenn es euch interessiert, kann ich die Buchreihe nur empfehlen; sie enthält viele Dinge, die man als Programmierer eigentlich verstanden haben sollte. Allerdings leitet Knuth vieles from-scratch her (also von der nackten Mathematik), und das muss man wirklich durcharbeiten, nicht nur durchlesen.


Log in to reply