Array Wörter Zufällig aufrufen



  • Hallo,

    wie der Name schon sagt will ich aus meinem Array mit 75 Tiere, 25 davon zufällig ausgeben lassen. Vom Prinzip her funktioniert das schon. Allerdings kommt wenn ich das Programm starte immer dieselbe Liste mit Tieren.

    Ich habe überlegt das Problem mit einer for-schleife zu lösen, allerdings hab ich da nicht so ganz den Faden gefunden.

    Es wäre schön wenn ihr mir einen Denkansatz geben könntet. Dankeschön.

    #include <iostream>
    
    using namespace std;
    
    int main() {
    
    	char Tiere[75][20] = {"Hase", "Fuchs", "L\224we", "Hamster", "Ente", "Lamm", "Hecht", "Zander", "Katze", "Hund", "Schwein", "Huhn", "Maus", "Zebra", "Nilpferd", "Robbe", "Ziege", "Kuh", 
    						"Dachs", "Wildschwein", "Reh", "Wolf", "K\201ken", "Taube", "Amsel", "Drossel", "Fink", "Star", "Eule", "Hahn", "Goldfisch", "Koi", "Affe", "Gorilla", "Panda", "Braunb\204r",
    						"Eisb\204r", "Schaf", "Koala", "Giraffe", "Elefant", "Schmetterling", "Marienk\204fer", "Biene", "Wespe", "Hummel", "Hornisse", "Kaulquappe", "Frosch", "Hai", "Rochen", "Faultier",
    						"Jaguar", "Gazelle", "Falke", "Delfin", "Tiger", "Puma", "Leopard", "Schnecke", "Schlange", "Eichh\224rnchen", "Eidechse", "Gecko", "Krabbe", "Tintenfisch", "Wal", "Blattlaus",
    						"Floh", "Zecke", "Wanze", "Spinne", "Nashorn", "Schildkr\224te", "Pinguin"};
    
    	for (int i = 0; i < 25; ++i) {
    			int zahl = (rand() % 75);
    			cout << Tiere[zahl]<< endl;
    	}
    
    	getchar();
    	return 0;
    }
    


  • en.cppreference.com schrieb:

    std::srand() seeds the pseudo-random number generator used by rand(). If rand() is used before any calls to srand(), rand() behaves as if it was seeded with srand(1).

    http://en.cppreference.com/w/cpp/numeric/random/rand



  • Wenn Du unterschiedliche Ergebnisse bei unterschiedlichen Programmaufrufen haben willst, musst Du jedes Mal den Zufallsgenerator unterschiedlich initialisieren.
    Dazu nimmst du am besten die aktuelle Uhrzeit:

    srand (time(NULL));
    

    Diese Funktion rufst Du einmal zu Programmbeginn auf, und fertig.



  • Wenn du den Zufallszahlengenerator mit dem gleichen Wert initialisiert, bekommst du auch immer die gleiche Zahlenfolge. In deinem Fall hast du ihn gar nicht initialisiert.
    Siehe http://www.cplusplus.com/reference/cstdlib/srand/



  • Belli schrieb:

    Wenn Du unterschiedliche Ergebnisse bei unterschiedlichen Programmaufrufen haben willst, musst Du jedes Mal den Zufallsgenerator unterschiedlich initialisieren.
    Dazu nimmst du am besten die aktuelle Uhrzeit:

    Nur dass du damit nicht dein Ziel ("bei unterschiedlichen Programmaufrufen") erreichst, sondern eher "zu unterschiedlichen Aufrufszeitpunkten, gebinnt in Sekunden". Wenn du zur selben Sekunde das Programm mehrfach aufrufst, kommt das gleiche heraus.

    Warum nicht modernen Code nutzen und std::random_device nehmen? Also, random_device liefert dir den Zufall und uniform_int_distribution wandelt den irgendwie gearteten Zufall in gleichmäßig verteilte Zahlen um.

    #include <random>
    
    ...
    
    std::random_device rd;
    std::uniform_int_distribution<int> dist(0, sizeof(Tiere)/sizeof(Tiere[0])-1);
    for (int i = 0; i < 25; ++i) {
        int zahl = dist(rd);
        cout << Tiere[zahl]<< endl;
    }
    


  • wob schrieb:

    Belli schrieb:

    Wenn Du unterschiedliche Ergebnisse bei unterschiedlichen Programmaufrufen haben willst, musst Du jedes Mal den Zufallsgenerator unterschiedlich initialisieren.
    Dazu nimmst du am besten die aktuelle Uhrzeit:

    Nur dass du damit nicht dein Ziel ("bei unterschiedlichen Programmaufrufen") erreichst

    Ich bin ziemlich sicher, dass der TE sein Ziel damit erreicht.

    wob schrieb:

    Warum nicht modernen Code nutzen und std::random_device nehmen?

    Ich versuche (immer), aus dem Gezeigten Rückschlüsse auf den Kenntinsstand des TE zu ziehen, und eine daran orientierte Lösung zu finden.



  • @Belli
    Kann ich verstehen, aber andererseits ist das langfristig nicht immer hilfreich. IMHO ist es besser, sofort die State-of-the-art Lösung zu zeigen, auch wenn man sich da manchmal reinfuchsen muss. Ansonsten läuft TE iwann wieder gegen die Wand und fragt sich, was er eigentlich falsch gemacht hat, obwohl er nach Hilfe gefragt hat und die Antwort erst ein Mal funktioniert. Dann muss er noch mal fragen und kriegt dann erst die richtige Antwort. Diesen Zwischenschritt kann er sich eigentlich sparen.



  • Naja, ist doch nicht so, dass srand/rand nicht funktioniert?!
    Vor C++11 haben wir jahrelang so Zufallszahlen generiert.



  • Belli schrieb:

    Naja, ist doch nicht so, dass srand/rand nicht funktioniert?!
    Vor C++11 haben wir jahrelang so Zufallszahlen generiert.

    Ist doch nicht so dass es vor C++11 unmöglich gewesen wäre was anderes zu verwenden.
    Wo's wichtig war hab ich vor 10 Jahren Mersenne Twister verwendet.
    (Würde ich heute nicht mehr, denn es gibt besseres als MT, z.B. Xorshift/Xoroshiro, aber die kannte ich damals noch nicht.)



  • Stimmt.
    In diesem konkreten Fall scheint es mir nicht so wichtig zu sein.
    Wenn es für den TE irgendwann wichtig werden sollte, wird er wahrscheinlich einen anderen Kenntnisstand haben und gezielt(er) danach fragen.

    Ansonsten hat ja wob bereits eine Variante vorgeschlagen.



  • Belli schrieb:

    Ansonsten hat ja wob bereits eine Variante vorgeschlagen.

    Die aber nebenbei auch kein guter Rat ist - sehe ich jetzt erst.
    std::random_device als Default "nimm mal einfach den" Generator zu empfehlen ist IMO ganz schlechter Rat. std::random_device kann nämlich schön teuer werden. Beliebig teuer. Und garantiert gleichzeitig nix was die Qualität der Zahlen angeht.



  • hustbaer schrieb:

    Belli schrieb:

    Ansonsten hat ja wob bereits eine Variante vorgeschlagen.

    Die aber nebenbei auch kein guter Rat ist - sehe ich jetzt erst.
    std::random_device als Default "nimm mal einfach den" Generator zu empfehlen ist IMO ganz schlechter Rat. std::random_device kann nämlich schön teuer werden. Beliebig teuer. Und garantiert gleichzeitig nix was die Qualität der Zahlen angeht.

    Gut, ich dachte, dass es für 25 Zahlen ggf. nicht lohnt, da einen std::mt19937 zu nutzen. Den ich dann einfach auch aus random_device seede - und zwar meistens mit einer einzigen Zufallszahl, obwohl der Zustand des mt19937 größer ist. Und ne seed_seq... naja, mir ist unklar, wie viel Werte ich auch dem random_device reintun müsste, damit der mt vollständig zufällig initialisiert wird. Eigentlich schade, dass man nicht das random_device direkt an den mt-Constructor übergeben kann.

    Und für die Garantien: die macht der C++-Standard zwar nicht, aber z.B. nutzt libc++ meines Wissens einfach /dev/urandom.

    Aber generell hast du schon recht: sobald man ein paar mehr Zahlen braucht, sollte man schon wissen, welchen Generator man gerade nutzt und wie lange er für die Berechnung einer Zufallszahl braucht.

    In der überwiegenden Anzahl Fälle wird folgendes das beste/einfachste sein:

    // einmalig den rng initialisieren, meistens recht eine Zufallszahl aus.
    std::mt19937 rng(std::random_device{}());
    
    // ab jetzt nur noch rng benutzen, z.B.
    std::cout << std::uniform_int_distribution<int>(0, 100)(rng) << '\n';
    std::cout << std::normal_distribution<double>(10.0,1.0)(rng) << '\n';
    


  • Danke erstmal für die Hilfe!

    Mittlerweile funktioniert es so wie ich es mir vorgestellt habe. Allerdings ist mir nun ein weiteres Problem in den Sinn gekommen was ich vorher nicht bedacht habe.

    Die Wörter können doppelt vorkommen, was sehr ungünstig ist. Ich habe gedacht das mit einer if-Bedingung zu lösen allerdings komm ich nicht darauf wie ich das denn vergleichen sollte.

    Hab schon einiges versucht was ich über doppelte Zufallszahlen gefunden habe, aber irgendwie hat es mich einfach nicht weiter gebracht.

    Hat hier nochmal jemand einen Tipp?



  • Jo...
    Wenn du keine Duplikate willst musst du dir iwie merken, welche Zahl(en) du bereits gezogen hast und musst die dann ignorieren. Da deine Tiernamen etwas ungünstig organisiert sind schlage ich folgende Lösung vor:

    1. Zu Beginn hast du 75 Tiernamen mit den Indizes 0-74
    2. Du füllst eine Liste (std::vector) mit allen gültigen Indizes
    3. Du wählst zufällig einen Eintrag aus dieser Liste aus und gibst den Tiernamen an diesem Index aus.
    4. Du löschst den Eintrag aus der Liste
    5. falls <25 Tiernamen ausgegeben weiter bei 3
    6. fertig


  • Hallo hustbaer,

    hustbaer schrieb:

    ... denn es gibt besseres als MT (Mersenne Twister), z.B. Xorshift/Xoroshiro

    Interessant! wieder ein neuer RNG - den kannte ich noch nicht. Ich benutze seit einiger Zeit pcg32, und bei meinen Anwendungen werde ich wahrscheinlich zwischen MT, pcg32 oder Xoroshiro128+ keinen Unterschied feststellen.
    pcg32, und auch Xoroshiro128+ wie ich sehe, hat gegenüber MT den Vorteil wesentlich weniger Resourcen zu verbraten.

    Gruß Werner



  • DocShoe schrieb:

    1. Du füllst eine Liste (std::vector) mit allen gültigen Indizes

    Ich hab allerdings noch nicht damit gearbeitet, kann also gerade noch recht wenig damit anfangen. Und bin mir auch nicht wirklich darüber bewusst wie ich das anwenden muss.



  • moglie23 schrieb:

    Ich hab allerdings noch nicht damit gearbeitet, kann also gerade noch recht wenig damit anfangen.

    Dann ist jetzt ein guter Zeitpunkt.

    moglie23 schrieb:

    Und bin mir auch nicht wirklich darüber bewusst wie ich das anwenden muss.

    Du möchtest 25 aus 75 haben.

    Beispiel für 3 aus 10:

    Liste = {0, 1, 2, 3 ,4 ,5 ,6 ,7 ,8, 9}
    Zufallszahl = 3, Liste[3] ist 3, gib Tier[3] aus
    lösche Element 3 aus Liste
    Liste = {0, 1, 2, 4 ,5 ,6 ,7 ,8, 9} // jetzt nur noch 9 Elemente
    Zufallszahl = 5, Liste[5] ist 6, gib Tier[6] aus
    lösche Element 5 aus Liste
    Liste = {0, 1, 2, 4 ,5 ,7 ,8, 9} // jetzt nur noch 8 Elemente
    Zufallszahl = 5, Liste[5] ist 7, gib Tier[7] aus
    lösche Element 5 aus Liste
    Liste = {0, 1, 2, 4 ,5 ,8, 9} // jetzt nur noch 7 Elemente
    


  • moglie23 schrieb:

    DocShoe schrieb:

    1. Du füllst eine Liste (std::vector) mit allen gültigen Indizes

    Ich hab allerdings noch nicht damit gearbeitet, kann also gerade noch recht wenig damit anfangen. Und bin mir auch nicht wirklich darüber bewusst wie ich das anwenden muss.

    Schau dir bitte mal dringend die STL an.

    Insbesondere solltest du kennen:
    http://www.cplusplus.com/reference/vector/vector/
    http://www.cplusplus.com/reference/string/

    Außerdem schau dir an, was es schon an fertigen Algorithmen gibt:
    http://www.cplusplus.com/reference/algorithm/

    Um einen std::vector mit Indizes (=aufeinanderfolgenden Zahlen) zu füllen, schau dir std::iota an (das suche ich immer in <algorithm> , es findet sich aber im Header <numeric> ).

    Da du ja zufällige Indizes haben willst, brauchst du nicht kompliziert aus der Mitte des Arrays herauszulöschen, sondern kannst stattdessen das letzte Element an diese Stelle kopieren und danach das letzte Element löschen. Das geht schneller.

    Wenn du nur "wenige Werte aus vielen" haben willst (und die die Reihenfolge egal ist), kannst du alternativ folgendes tun (Pseudcode für 6 aus 49):

    std::set<int> ergebnis;
    while (ergenis.size() < 6) {
      ergebnis.insert(get_random_value(1, 49));
    }
    

    Sowas ist gut, solange du nur wenige Werte ermitteln wirst, d.h. wenn du einen Wert doppelt ziehst, ziehst du einfach noch einmal. Das ist aber nicht sinnvoll, wenn du viele Dinge ziehen willst.



  • In der STL gibt es auch random_shuffle/shuffle.



  • Th69 schrieb:

    In der STL gibt es auch random_shuffle/shuffle.

    Oh ja, gute Idee 👍


Anmelden zum Antworten