Come generare un numero casuale in C ++?


150

Sto cercando di fare un gioco con i dadi e ho bisogno di numeri casuali (per simulare i lati del dado. So come farlo tra 1 e 6). utilizzando

#include <cstdlib> 
#include <ctime> 
#include <iostream>

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "\n"; 
}

non funziona molto bene, perché quando eseguo il programma alcune volte, ecco l'output che ottengo:

6
1
1
1
1
1
2
2
2
2
5
2

Quindi voglio un comando che genererà ogni volta un numero casuale diverso , non lo stesso 5 volte di seguito. C'è un comando che lo farà?


55
A parte i problemi di distribuzione, tieni presente che con numeri casuali arriva la possibilità di ottenere lo stesso risultato più volte di seguito. Se ti fosse garantito di non ottenere lo stesso numero due volte di seguito, i risultati non sarebbero davvero casuali, vero?
cdhowie,

5
Cosa ti fa pensare che quei numeri non siano casuali? Lancia un dado per davvero e molto bene potresti ottenere quel risultato. Se fossero garantiti per essere diversi tra ogni lancio, non sarebbe davvero casuale.
Mattjgalloway,

2
Leggi anche eternallyconfuzzled.com/arts/jsw_art_rand.aspx perché l'utilizzo dell'operatore modulo non è spesso una buona idea.
Benjamin Bannier,

4
Stai fraintendendo molto più di quanto si possa inserire in un commento o anche in una risposta. Devi imparare, indipendentemente, sui generatori di numeri pseudo-casuali, sui semi, sull'importanza di raccogliere un seme veramente casuale e sulle distribuzioni uniformi.
Kerrek SB,

20
Quando semini con il tempo. Questo significa anche che se esegui il tuo programma più di una volta al secondo otterrai lo stesso numero.
Martin York,

Risposte:


79

Il problema fondamentale dell'applicazione di prova è che si chiama sranduna volta e poi si chiama randuna volta e si esce.

L'intero punto della srandfunzione è inizializzare la sequenza di numeri pseudo-casuali con un seme casuale.

Ciò significa che se si passa lo stesso valore per sranddue diverse applicazioni (con lo stesso srand/ randattuazione) allora si ottiene esattamente la stessa sequenza di rand()valori letti dopo che in entrambe le applicazioni.

Tuttavia, nella tua applicazione di esempio, la sequenza pseudo-casuale è costituita da un solo elemento: il primo elemento di una sequenza pseudo-casuale generata da seme uguale al tempo corrente di secondprecisione. Cosa ti aspetti di vedere in uscita allora?

Ovviamente quando ti capita di eseguire l'applicazione nello stesso secondo - usi lo stesso valore seed - quindi il tuo risultato è lo stesso ovviamente (come già menzionato Martin York in un commento alla domanda).

In realtà dovresti chiamare srand(seed)una volta e poi chiamare rand() più volte e analizzare quella sequenza: dovrebbe apparire casuale.

MODIFICARE:

Ah, ho capito. Una descrizione apparentemente verbale non è sufficiente (forse una barriera linguistica o qualcosa del genere ... :)).

OK. Esempio di codice C vecchio stile basato sulle stesse srand()/rand()/time()funzioni utilizzate nella domanda:

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main(void)
{
    unsigned long j;
    srand( (unsigned)time(NULL) );

    for( j = 0; j < 100500; ++j )
    {
        int n;

        /* skip rand() readings that would make n%6 non-uniformly distributed
          (assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
        while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
        { /* bad value retrieved so get next one */ }

        printf( "%d,\t%d\n", n, n % 6 + 1 );
    }

    return 0;
}

^^^ QUESTA sequenza da una singola esecuzione del programma dovrebbe apparire casuale.

EDIT2:

Quando si utilizza la libreria standard C o C ++ è importante comprendere che al momento non esiste una singola funzione standard o classe che produce dati effettivamente casuali definitivamente (garantiti dallo standard). L'unico strumento standard che affronta questo problema è std :: random_device che purtroppo non fornisce ancora garanzie di casualità effettiva.

A seconda della natura dell'applicazione, dovresti prima decidere se hai davvero bisogno di dati veramente casuali (imprevedibili). Un caso degno di nota quando si ha sicuramente bisogno della vera casualità è la sicurezza delle informazioni, ad esempio la generazione di chiavi simmetriche, chiavi private asimmetriche, valori salt, token di sicurezza, ecc.

Tuttavia, i numeri casuali di livello di sicurezza sono un settore separato che vale un articolo separato.

Nella maggior parte dei casi è sufficiente un generatore di numeri pseudo-casuali , ad esempio per simulazioni o giochi scientifici. In alcuni casi è persino necessaria una sequenza pseudo-casuale definita in modo coerente, ad esempio nei giochi è possibile scegliere di generare esattamente le stesse mappe in fase di esecuzione per evitare di archiviare molti dati.

La domanda originale e la moltitudine ricorrente di domande identiche / simili (e anche molte "risposte" sbagliate a queste) indicano che è innanzitutto importante distinguere i numeri casuali dai numeri pseudo-casuali E capire cos'è la sequenza numerica pseudo-casuale in il primo posto E rendersi conto che i generatori di numeri pseudo-casuali NON sono usati allo stesso modo in cui si potrebbero usare generatori di numeri casuali veri.

Intuitivamente quando richiedi un numero casuale - il risultato restituito non dovrebbe dipendere dai valori precedentemente restituiti e non dovrebbe dipendere se qualcuno ha richiesto qualcosa prima e non dovrebbe dipendere in quale momento e da quale processo e su quale computer e da quale generatore e in quale galassia è stata richiesta. Questo è ciò che significa "casuale" dopo tutto - essere imprevedibile e indipendente da qualsiasi cosa - altrimenti non è più casuale, giusto? Con questa intuizione è naturale cercare sul web alcuni incantesimi da lanciare per ottenere un numero così casuale in qualsiasi contesto possibile.

^^^ Quel tipo di aspettative intuitive È MOLTO SBAGLIATO e dannoso in tutti i casi che coinvolgono generatori di numeri pseudo-casuali - nonostante sia ragionevole per numeri casuali reali.

Mentre esiste la nozione significativa di "numero casuale", non esiste un "numero pseudo-casuale". Un generatore di numeri pseudo-casuali produce effettivamente una sequenza numerica pseudo-casuale .

Quando gli esperti parlano della qualità del PRNG, in realtà parlano delle proprietà statistiche della sequenza generata (e delle sue notevoli sub-sequenze). Ad esempio, se si combinano due PRNG di alta qualità utilizzandoli entrambi a turno - è possibile che si producano cattive sequenze risultanti - nonostante generino buone sequenze ciascuna separatamente (quelle due buone sequenze possono semplicemente correlarsi tra loro e quindi combinarsi male).

La sequenza pseudo-casuale è infatti sempre deterministica (predeterminata dal suo algoritmo e dai suoi parametri iniziali), cioè in realtà non c'è nulla di casuale al riguardo.

In particolare rand()/ srand(s)coppia di funzioni fornisce una sequenza numerica pseudo-casuale (!) Singolare per processo non thread-safe (!) Generata con algoritmo definito dall'implementazione. La funzione rand()produce valori nell'intervallo [0, RAND_MAX].

Citazione dalla norma C11:

La srandfunzione utilizza l'argomento come seed per una nuova sequenza di numeri pseudo-casuali a cui restituire le chiamate successive rand. Se srandviene quindi chiamato con lo stesso valore di seed, la sequenza di numeri pseudo-casuali deve essere ripetuta. Se randviene chiamato prima che srandsia stata effettuata qualsiasi chiamata , deve essere generata la stessa sequenza di quando srandviene chiamata per la prima volta con un valore seed di 1.

Molte persone ragionevolmente si aspettano che rand()produrrebbe una sequenza di numeri uniformemente distribuiti semi-indipendenti nella gamma 0a RAND_MAX. Beh, sicuramente dovrebbe (altrimenti è inutile) ma sfortunatamente non solo lo standard non lo richiede - c'è persino un esplicito disclaimer che afferma che "non vi sono garanzie sulla qualità della sequenza casuale prodotta" . In alcuni casi storici rand/ srandimplementazione era di pessima qualità davvero. Anche se nelle implementazioni moderne è molto probabilmente abbastanza buono, ma la fiducia è rotta e non è facile da recuperare. Oltre alla sua natura non thread-safe, il suo utilizzo sicuro in applicazioni multi-thread è complicato e limitato (ancora possibile - è possibile utilizzarli solo da un thread dedicato).

Il nuovo modello di classe std :: mersenne_twister_engine <> (e la sua convenienza typedefs - std::mt19937/ std::mt19937_64con una buona combinazione di parametri del modello) fornisce un generatore di numeri pseudo-casuali per oggetto definito nello standard C ++ 11. Con gli stessi parametri del modello e gli stessi parametri di inizializzazione, oggetti diversi genereranno esattamente la stessa sequenza di output per oggetto su qualsiasi computer in qualsiasi applicazione costruita con libreria standard conforme a C ++ 11. Il vantaggio di questa classe è la sua prevedibile sequenza di output di alta qualità e la piena coerenza tra le implementazioni.

Inoltre ci sono più motori PRNG definiti nello standard C ++ 11 - std :: linear_congruential_engine <> (storicamente usato come srand/randalgoritmo di qualità equa in alcune implementazioni di librerie standard C) e std :: subtract_with_carry_engine <> . Inoltre, generano sequenze di output per oggetto completamente definite e dipendenti da parametri.

Sostituzione di esempio C ++ 11 dei giorni nostri per il codice C obsoleto sopra:

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    // seed value is designed specifically to make initialization
    // parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
    // different across executions of application
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);

    for( unsigned long j = 0; j < 100500; ++j )
    /* ^^^Yes. Generating single pseudo-random number makes no sense
       even if you use std::mersenne_twister_engine instead of rand()
       and even when your seed quality is much better than time(NULL) */    
    {
        std::mt19937::result_type n;
        // reject readings that would make n%6 non-uniformly distributed
        while( ( n = gen() ) > std::mt19937::max() -
                                    ( std::mt19937::max() - 5 )%6 )
        { /* bad value retrieved so get next one */ }

        std::cout << n << '\t' << n % 6 + 1 << '\n';
    }

    return 0;
}

La versione del codice precedente che utilizza std :: uniform_int_distribution <>

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);
    std::uniform_int_distribution<unsigned> distrib(1, 6);

    for( unsigned long j = 0; j < 100500; ++j )
    {
        std::cout << distrib(gen) << ' ';
    }

    std::cout << '\n';
    return 0;
}

Ho fatto una domanda simile nel link qui ma non sono ancora riuscito a trovare una risposta chiara. Puoi per favore dimostrare "In realtà dovresti chiamare srand (seed) una volta e poi chiamare rand ()" con i codici perché ho già fatto quello che dici ma non funziona correttamente.
bashburak,

2
@bashburak Sembra che tu abbia perso totalmente il punto di questa risposta. Perché hai tagliato esattamente la mia citazione? Nella mia risposta ho detto letteralmente "In realtà dovresti chiamare srand (seed) una volta e poi chiamare rand () molte volte e analizzare quella sequenza - dovrebbe apparire casuale." Hai notato che dovresti chiamare rand () MOLTE VOLTE dopo la chiamata a singolo srand (...)? La tua domanda nel tuo link è esattamente il duplicato di questa domanda con lo stesso equivoco.
Serge Dundich,

Questa è una vecchia risposta, ma viene visualizzata quando si google "C ++ generazione di numeri casuali". È un consiglio scadente per i programmatori C ++, perché ti consiglia di usare rand()e srand(). Puoi aggiornarlo?
Yakk - Adam Nevraumont,

@ Yakk-AdamNevraumont In realtà non consiglia di utilizzare rand()e srand(). In realtà risponde solo alla domanda con la descrizione fornita. È evidente dalla descrizione (che usa rand/ srand) che dovrebbero essere spiegati i concetti di base della generazione di numeri pseudo-casuali, come il significato stesso della sequenza pseudo-casuale e il suo seme. Sto cercando di fare esattamente questo e utilizzare il più semplice e familiare rand/ srandcombinazione. La cosa divertente è che alcune altre risposte - anche con una valutazione molto grande - soffrono degli stessi malintesi dell'autore della domanda.
Serge Dundich,

@ Yakk-AdamNevraumont Ho preso il tuo consiglio e modificato la mia risposta con alcune informazioni sulle novità C ++ più recenti. Anche se lo considero un po 'fuori tema - ma il tuo suggerimento e alcune altre risposte indicano che entrambe le buone std::rand/std::srandfunzionalità della libreria C ++ vecchia e nuova come std::random_device<>, std :: mersenne_twister_engine <> e una moltitudine di distribuzioni casuali richiedono alcune spiegazioni.
Serge Dundich,

216

L'uso di modulo può introdurre distorsioni nei numeri casuali, a seconda del generatore di numeri casuali. Vedi questa domanda per maggiori informazioni. Naturalmente, è perfettamente possibile ottenere numeri ripetuti in una sequenza casuale.

Prova alcune funzionalità di C ++ 11 per una migliore distribuzione:

#include <random>
#include <iostream>

int main()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]

    std::cout << dist6(rng) << std::endl;
}

Vedi questa domanda / risposta per maggiori informazioni sui numeri casuali C ++ 11. Quanto sopra non è l'unico modo per farlo, ma è un modo.


7
La quantità di distorsione introdotta usando %6è incredibilmente piccola. Forse significativo se stai scrivendo un gioco di craps da utilizzare a Las Vegas, ma senza conseguenze in quasi tutti gli altri contesti.
Hot Licks

9
HotLicks: d'accordo, ma se stai usando una versione di C ++ che supporta random_devicee mt19937già, non c'è letteralmente alcun motivo per non fare tutto e usare anche lo standard uniform_int_distribution.
Quuxplusone,

4
Tutti i programmatori dovrebbero consigliare alle persone di evitare il modulo come la peste perché utilizza la divisione e che costa centinaia di cicli di clock e può rovinare i tempi dell'applicazione e / o bruciare molta carica della batteria.

3
Rng è per "range"?
Christoffer,

4
@ ChristofferHjärtström: è per r andom n umber g enerator.
Cornstalks,

11

Se stai usando boost libs puoi ottenere un generatore casuale in questo modo:

#include <iostream>
#include <string>

// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>

using namespace std;
using namespace boost;

int current_time_nanoseconds(){
    struct timespec tm;
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_nsec;
}

int main (int argc, char* argv[]) {
    unsigned int dice_rolls = 12;
    random::mt19937 rng(current_time_nanoseconds());
    random::uniform_int_distribution<> six(1,6);

    for(unsigned int i=0; i<dice_rolls; i++){
        cout << six(rng) << endl;
    }
}

Dove la funzione current_time_nanoseconds()fornisce l'ora corrente in nanosecondi che viene utilizzata come seme.


Ecco una classe più generale per ottenere numeri interi e date casuali in un intervallo:

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"


using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;


class Randomizer {
private:
    static const bool debug_mode = false;
    random::mt19937 rng_;

    // The private constructor so that the user can not directly instantiate
    Randomizer() {
        if(debug_mode==true){
            this->rng_ = random::mt19937();
        }else{
            this->rng_ = random::mt19937(current_time_nanoseconds());
        }
    };

    int current_time_nanoseconds(){
        struct timespec tm;
        clock_gettime(CLOCK_REALTIME, &tm);
        return tm.tv_nsec;
    }

    // C++ 03
    // ========
    // Dont forget to declare these two. You want to make sure they
    // are unacceptable otherwise you may accidentally get copies of
    // your singleton appearing.
    Randomizer(Randomizer const&);     // Don't Implement
    void operator=(Randomizer const&); // Don't implement

public:
    static Randomizer& get_instance(){
        // The only instance of the class is created at the first call get_instance ()
        // and will be destroyed only when the program exits
        static Randomizer instance;
        return instance;
    }
    bool method() { return true; };

    int rand(unsigned int floor, unsigned int ceil){
        random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
        return (rand_(rng_));
    }

    // Is not considering the millisecons
    time_duration rand_time_duration(){
        boost::posix_time::time_duration floor(0, 0, 0, 0);
        boost::posix_time::time_duration ceil(23, 59, 59, 0);
        unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
        return seconds(rand_seconds);
    }


    date rand_date_from_epoch_to_now(){
        date now = second_clock::local_time().date();
        return rand_date_from_epoch_to_ceil(now);
    }

    date rand_date_from_epoch_to_ceil(date ceil_date){
        date epoch = ptime(date(1970,1,1)).date();
        return rand_date_in_interval(epoch, ceil_date);
    }

    date rand_date_in_interval(date floor_date, date ceil_date){
        return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
    }

    ptime rand_ptime_from_epoch_to_now(){
        ptime now = second_clock::local_time();
        return rand_ptime_from_epoch_to_ceil(now);
    }

    ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date){
        ptime epoch = ptime(date(1970,1,1));
        return rand_ptime_in_interval(epoch, ceil_date);
    }

    ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
        time_duration const diff = ceil_date - floor_date;
        long long gap_seconds = diff.total_seconds();
        long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
        return floor_date + seconds(step_seconds);
    }
};

1
Ora che abbiamo random come parte dello standard, scoraggerei l'uso della versione boost a meno che non si stia utilizzando un compilatore veramente vecchio.
Martin York,

9
#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    srand(time(NULL));
    int random_number = std::rand(); // rand() return a number between ​0​ and RAND_MAX
    std::cout << random_number;
    return 0;
}

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


Qual è la differenza con il codice dell'autore della domanda? (Tranne che non si utilizza %6.) E se si decide di utilizzare la funzione di libreria std::randC ++ API of randC, perché non utilizzare std::timee std::srandper motivi di coerenza in stile C ++?
Serge Dundich,

4

Puoi ottenere il Randomercodice di classe completo per generare numeri casuali da qui!

Se hai bisogno di numeri casuali in diverse parti del progetto, puoi creare una classe separata Randomerper incapsulare tutto ciò che contiene random.

Qualcosa del genere:

class Randomer {
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /*  ... some convenient ctors ... */ 

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
        : gen_{seed}, dist_{min, max} {
    }

    // if you want predictable numbers
    void SetSeed(unsigned int seed) {
        gen_.seed(seed);
    }

    size_t operator()() {
        return dist_(gen_);
    }
};

Una lezione del genere sarebbe utile in seguito:

int main() {
    Randomer randomer{0, 10};
    std::cout << randomer() << "\n";
}

Puoi controllare questo link come esempio di come uso tale Randomerclasse per generare stringhe casuali. Puoi anche usarlo Randomerse lo desideri.


Non vorresti riutilizzare il generatore per tutti i tuoi oggetti Randomer? Soprattutto perché è relativamente costoso creare inizializzare e mantenere il suo stato.
Martin York,

3

Genera un numero casuale diverso ogni volta, non lo stesso sei volte di seguito.

Scenario di caso d'uso

Ho paragonato il problema della prevedibilità a un sacchetto di sei pezzi di carta, ciascuno con un valore da 0 a 5 scritto su di esso. Un pezzo di carta viene estratto dal sacchetto ogni volta che è richiesto un nuovo valore. Se la borsa è vuota, i numeri vengono rimessi nella borsa.

... da questo, posso creare un algoritmo di sorta.

Algoritmo

Una borsa è di solito a Collection. Ho scelto un bool[](altrimenti noto come array booleano, piano bit o mappa bit) per assumere il ruolo di borsa.

Il motivo per cui ho scelto a bool[]è perché l'indice di ogni articolo è già il valore di ciascun pezzo di carta. Se i documenti richiedessero qualcos'altro scritto su di loro, avrei usato un Dictionary<string, bool>al suo posto. Il valore booleano viene utilizzato per tenere traccia del fatto che il numero sia stato ancora disegnato o meno.

Un contatore chiamato RemainingNumberCountviene inizializzato a 5quel conto alla rovescia quando viene scelto un numero casuale. Questo ci evita di dover contare quanti pezzi di carta rimangono ogni volta che desideriamo disegnare un nuovo numero.

Per selezionare il prossimo valore casuale sto usando a for..loopper scansionare il sacco di indici e un contatore per contare quando indexviene falsechiamato NumberOfMoves.

NumberOfMovesè usato per scegliere il prossimo numero disponibile. NumberOfMovesviene prima impostato come valore casuale tra 0e 5, poiché ci sono 0..5 passaggi disponibili che possiamo fare attraverso il sacchetto. Alla successiva iterazione NumberOfMovesè impostato un valore casuale tra 0e 4, poiché ora ci sono 0..4 passi che possiamo fare attraverso il sacchetto. Man mano che vengono utilizzati i numeri, i numeri disponibili si riducono, quindi utilizziamo invece rand() % (RemainingNumberCount + 1)per calcolare il valore successivo per NumberOfMoves.

Quando il NumberOfMovescontatore raggiunge lo zero, il for..loopseguente dovrebbe:

  1. Imposta il valore corrente in modo che sia uguale for..loopall'indice.
  2. Impostare tutti i numeri nella borsa su false.
  3. Rompere dal for..loop.

Codice

Il codice per la soluzione sopra è il seguente:

(inserisci i seguenti tre blocchi nel file .cpp principale uno dopo l'altro)

#include "stdafx.h"
#include <ctime> 
#include <iostream>
#include <string>

class RandomBag {
public:
    int Value = -1;

    RandomBag() {
        ResetBag();

    }

    void NextValue() {
        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        int NumberOfMoves = rand() % (RemainingNumberCount + 1);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            if (BagOfNumbers[i] == 0) {
                NumberOfMoves--;

                if (NumberOfMoves == -1)
                {
                    Value = i;

                    BagOfNumbers[i] = 1;

                    break;

                }

            }



        if (RemainingNumberCount == 0) {
            RemainingNumberCount = 5;

            ResetBag();

        }
        else            
            RemainingNumberCount--; 

    }

    std::string ToString() {
        return std::to_string(Value);

    }

private:
    bool BagOfNumbers[6]; 

    int RemainingNumberCount;

    int NumberOfMoves;

    void ResetBag() {
        RemainingNumberCount = 5;

        NumberOfMoves = rand() % 6;

        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            BagOfNumbers[i] = 0;

    }

};

Una classe di console

Creo questa classe Console perché semplifica il reindirizzamento dell'output.

Di seguito nel codice ...

Console::WriteLine("The next value is " + randomBag.ToString());

... può essere sostituito da ...

std::cout << "The next value is " + randomBag.ToString() << std::endl; 

... e quindi questa Consoleclasse può essere cancellata se lo si desidera.

class Console {
public:
    static void WriteLine(std::string s) {
        std::cout << s << std::endl;

    }

};

Metodo principale

Esempio di utilizzo come segue:

int main() {
    srand((unsigned)time(0)); // Initialise random seed based on current time

    RandomBag randomBag;

    Console::WriteLine("First set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nSecond set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nThird set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nProcess complete.\n");

    system("pause");

}

Esempio di output

Quando ho eseguito il programma, ho ottenuto il seguente output:

First set of six...

The next value is 2
The next value is 3
The next value is 4
The next value is 5
The next value is 0
The next value is 1

Second set of six...

The next value is 3
The next value is 4
The next value is 2
The next value is 0
The next value is 1
The next value is 5

Third set of six...

The next value is 4
The next value is 5
The next value is 2
The next value is 0
The next value is 3
The next value is 1

Process complete.

Press any key to continue . . .

Dichiarazione di chiusura

Questo programma è stato scritto utilizzando Visual Studio 2017 e ho scelto di renderlo un Visual C++ Windows Console Applicationprogetto utilizzando .Net 4.6.1.

Non sto facendo nulla di particolarmente speciale qui, quindi il codice dovrebbe funzionare anche su versioni precedenti di Visual Studio.


Se questo è VS 2017, dovresti utilizzare la versione più recente della libreria standard: en.cppreference.com/w/cpp/numeric/random . Attualmente questo esempio utilizza le funzioni di libreria casuale C e "Non ci sono garanzie sulla qualità della sequenza casuale prodotta".
Robert Andrzejuk,

3

Ogni volta che esegui una ricerca web di base random number generationnel linguaggio di programmazione C ++, questa domanda è di solito la prima a comparire! Voglio buttare il mio cappello sul ring per sperare che chiarisca meglio il concetto di generazione di numeri pseudo-casuali in C ++ per programmatori futuri che inevitabilmente cercheranno questa stessa domanda sul web!

Le basi

La generazione di numeri pseudo-casuali comporta il processo di utilizzo di un algoritmo deterministico che produce una sequenza di numeri le cui proprietà assomigliano approssimativamente a numeri casuali . Dico approssimativamente assomigliare , perché la vera casualità è un mistero piuttosto sfuggente in matematica e informatica. Quindi, perché il termine pseudo-casuale viene utilizzato per essere più pedanticamente corretto!

Prima di poter effettivamente utilizzare un PRNG, ovvero, pseudo-random number generatorè necessario fornire all'algoritmo un valore iniziale spesso indicato anche come seed . Tuttavia, il seed deve essere impostato solo una volta prima di utilizzare l'algoritmo stesso!

/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
  PRNG( seed ) /// Will work as expected

/// Wrong way!
for( x in rang( 0, 10 ) ):
  seed( 1234 ) /// Seed reset for ten iterations!
  PRNG( seed ) /// Output will be the same...

Quindi, se vuoi una buona sequenza di numeri, devi fornire un ampio seme al PRNG!

Il vecchio modo C.

La libreria standard compatibile con le versioni precedenti di C che C ++ ha, utilizza quello che viene chiamato un generatore congruenziale lineare trovato nel cstdlibfile di intestazione! Questo PRNG funziona attraverso una funzione discontinua a tratti che utilizza l'aritmetica modulare, ovvero un algoritmo rapido a cui piace usare modulo operator '%'. Quanto segue è l'uso comune di questo PRNG, per quanto riguarda la domanda originale posta da @Predictability:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main( void )
{
  int low_dist  = 1;
  int high_dist = 6;
  std::srand( ( unsigned int )std::time( nullptr ) );
  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
  return 0;
}

L'uso comune del PRNG di C ospita una serie di problemi come:

  1. L'interfaccia generale di std::rand()non è molto intuitiva per la corretta generazione di numeri pseudo-casuali tra un determinato intervallo, ad esempio, producendo numeri tra [1, 6] nel modo desiderato da @Predictability.
  2. L'uso comune di std::rand()elimina la possibilità di una distribuzione uniforme di numeri pseudo-casuali, a causa del Principio di Pigeonhole .
  3. Il modo comune in cui std::rand()viene analizzato std::srand( ( unsigned int )std::time( nullptr ) )tecnicamente non è corretto, poiché time_tè considerato un tipo limitato . Pertanto, la conversione da time_ta unsigned int non è garantita!

Per informazioni più dettagliate sui problemi generali dell'utilizzo del PRNG di C e su come evitarli, consultare Utilizzo di rand () (C / C ++): Consigli per la funzione rand () della libreria standard C !

Il modo C ++ standard

Da quando è stato pubblicato lo standard ISO / IEC 14882: 2011, ovvero C ++ 11, la randomlibreria è stata separata dal linguaggio di programmazione C ++ per un po 'di tempo. Questa libreria è dotata di molteplici PRNGs, e diversi tipi di distribuzione , quali: distribuzione uniforme , distribuzione normale , distribuzione binomiale , ecc Il seguente esempio di codice source dimostra un uso molto di base della randombiblioteca, per quanto riguarda la domanda iniziale @ di Prevedibilità:

#include <iostream>
#include <cctype>
#include <random>

using u32    = uint_least32_t; 
using engine = std::mt19937;

int main( void )
{
  std::random_device os_seed;
  const u32 seed = os_seed();

  engine generator( seed );
  std::uniform_int_distribution< u32 > distribute( 1, 6 );

  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << distribute( generator ) << std::endl;
  return 0;
}

Nell'esempio sopra è stato utilizzato il motore Mersenne Twister a 32 bit , con una distribuzione uniforme di valori interi . (Il nome del motore nel codice sorgente sembra strano, perché il suo nome deriva dal suo periodo di 2 ^ 19937-1). L'esempio utilizza anche il std::random_deviceseeding del motore, che ottiene il suo valore dal sistema operativo (se si utilizza un sistema Linux, quindi std::random_devicerestituisce un valore da /dev/urandom).

Prendi nota che non devi usare std::random_deviceper seminare alcun motore . Puoi usare le costanti o anche la chronolibreria! Inoltre non è necessario utilizzare la versione a 32 bit del std::mt19937motore, ci sono altre opzioni ! Per ulteriori informazioni sulle funzionalità della randomlibreria, consultare cplusplus.com

Tutto sommato, i programmatori C ++ non dovrebbero std::rand()più usare , non perché è male , ma perché lo standard attuale offre alternative migliori che sono più semplici e affidabili . Spero che molti di voi lo trovino utile, specialmente quelli che recentemente hanno cercato sul web generating random numbers in c++!


2

Ecco una soluzione Creare una funzione che restituisce il numero casuale e posizionarlo al di fuori della funzione principale per renderlo globale. Spero che questo ti aiuti

#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
    srand((unsigned)time(0));
    int die1;
    int die2;
    for (int n=10; n>0; n--){
    die1 = rollDie();
    die2 = rollDie();
    cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
    return (rand()%6)+1;
}

2

Questo codice produce numeri casuali da na m.

int random(int from, int to){
    return rand() % (to - from + 1) + from;
}

esempio:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}

2
Questo non risponde davvero alla domanda.
HolyBlackCat

1
Non l'hai riparato. Il punto della domanda è che se si esegue il programma più volte al secondo, genera gli stessi valori casuali. Anche il tuo codice lo fa.
HolyBlackCat

1
@HolyBlackCat L'ho verificato per più esecuzioni, funziona. Hai aggiunto srand(time(0))alla funzione principale prima random(n, m)?
Amir Fo

1
È necessario aggiungere srand(time(0))alla funzione principale non per ciclo o all'interno dell'implementazione della funzione.
Amir Fo

1
Ho copiato il tuo codice alla lettera. L'hai eseguito più volte al secondo ?
HolyBlackCat

1

per casuale ogni file RUN

size_t randomGenerator(size_t min, size_t max) {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    //rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
    std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);

    return dist(rng);
}

1
Non è necessario creare il generatore più volte. Mantiene un gruppo di stati in modo da generare una sequenza di numeri casuali che ha la distribuzione appropriata (per renderlo casuale).
Martin York,

-2

Ecco un semplice generatore casuale con ca. uguale probabilità di generare valori positivi e negativi intorno a 0:

  int getNextRandom(const size_t lim) 
  {
        int nextRand = rand() % lim;
        int nextSign = rand() % lim;
        if (nextSign < lim / 2)
            return -nextRand;
        return nextRand;
  }


   int main()
   {
        srand(time(NULL));
        int r = getNextRandom(100);
        cout << r << endl;
        return 0;
   }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.