2011-08-27 17 views
23

Il nuovo standard C++ 11 ha un intero capitolo dedicato ai generatori di numeri casuali. Ma come faccio a eseguire il compito più comune più semplice che ha usato essere codificato come questo, ma senza ricorrere alla libreria standard C:Come posso generare un numero casuale usando la libreria standard C++ 11

srand((unsigned int)time(0)); 
int i = rand();

Esistono valori di default ragionevoli per i motori di numeri casuali, distribuzioni, e semi che si potrebbe usare fuori dalla scatola?

+0

Wikipedia? http://en.wikipedia.org/wiki/C%2B%2B0x#Extensible_random_number_facility – quasiverse

+2

Cosa c'è di sbagliato nel codice che hai? AFAIK, i nuovi generatori di numeri casuali sono stati aggiunti per più applicazioni "serie" in cui gli aspetti della generazione di numeri casuali sono davvero importanti. – GManNickG

+3

@GMan: Per essere onesti, molti dei motori di numeri casuali nel nuovo standard potrebbero essere descritti come semplici e veloci e non li vedrei come particolarmente "seri". –

risposta

30

Si dovrebbe essere in grado di fare qualcosa di simile:

std::default_random_engine e((unsigned int)time(0)); 
int i = e(); 

La qualità del default_random_engine dipende dall'implementazione. È inoltre possibile utilizzare std::min_rand0 o std::min_rand.

Probabilmente un modo migliore per eseguire il seeding di un motore casuale è un numero casuale reale disponibile dall'implementazione piuttosto che utilizzare time.

E.g.

std::random_device rd; 
std::default_random_engine e(rd()); 
+1

Per chiarire, intendi "dovrebbe" come in "questo non esiste ma dovrebbe" o sd "questo esiste e il tuo compilatore dovrebbe supportarlo"? – GManNickG

+8

Informativo: la più grande differenza tra questa risposta e l'esempio dell'OP è che il codificatore controlla dove si trova lo stato del motore: è in 'e'. Nel codice dell'OP lo stato è nascosto come dati statici all'interno di 'rand'. Questa è una differenza importante quando si ha a che fare con programmi multithread. 'rand' deve avere un qualche tipo di protezione per renderlo thread-safe. "default_random_engine' no. Se si desidera chiamarlo da più thread, si fornisce il meccanismo di sincronizzazione da soli, esternamente. Ciò significa che "default_random_engine" può essere più veloce se non è necessario sincronizzarlo. –

+0

@GMan: Significa che l'unica implementazione a cui ho accesso al momento non lo supporta, quindi non sono stato in grado di testare il mio codice;). –

2

Se il codice esistente era appropriato prima del nuovo standard, allora continuerà ad esserlo. I nuovi generatori di numeri casuali sono stati aggiunti per applicazioni che richiedono una qualità superiore di pseudo-casualità, ad es. simulazione stocastica.

0

Generazione di numeri casuali è un problema difficile. Non c'è un modo veramente casuale per farlo. Se stai solo generando casualità per seminare un ambiente di gioco, allora il tuo approccio dovrebbe andare bene. rand() ha diversi difetti.

Se hai bisogno di casualità per generare chiavi di crittografia, allora sei S.O.L. In tal caso, il modo migliore è di andare al sistema operativo, che di solito ha un meccanismo. Su POSIX è random() (o letto da/dev/random se sei così disposto). Su Windows è possibile utilizzare il CryptoAPI:

https://www.securecoding.cert.org/confluence/display/seccode/MSC30-C.+Do+not+use+the+rand%28%29+function+for+generating+pseudorandom+numbers

0

Si potrebbe utilizzare RC4 per generare byte casuali. Questo probabilmente ha le proprietà che vuoi. È veloce e abbastanza semplice da implementare. La sequenza è ripetibile su tutte le implementazioni quando il seme è noto e completamente imprevedibile quando il seme non è noto. http://en.wikipedia.org/wiki/RC4

2

Io uso il seguente codice nel mio progetto. 'motore' e 'distribuzione' possono essere uno dei forniti dalla libreria.

#include <random> 
#include <functional> 
#include <iostream> 
... 
std::uniform_int_distribution<unsigned int> unif; 
std::random_device rd; 
std::mt19937 engine(rd()); 
std::function<unsigned int()> rnd = std::bind(unif, engine); 

std::cout << rnd() << '\n'; 
6

unificare e semplificare alcuni dei campioni già forniti Vorrei riassumere a:

// Good random seed, good engine 
auto rnd1 = std::mt19937(std::random_device{}()); 

// Good random seed, default engine 
auto rnd2 = std::default_random_engine(std::random_device{}()); 

// like rnd1, but force distribution to int32_t range 
auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}())); 

// like rnd3, but force distribution across negative numbers as well 
auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}())); 

Poi ho corse alcuni test per vedere che cosa le impostazioni predefinite assomigliano:

#include <random> 
#include <functional> 
#include <limits> 
#include <iostream> 

template<class Func> 
void print_min_mean_max(Func f) { 
    typedef decltype(f()) ret_t; 
    ret_t min = std::numeric_limits<ret_t>::max(), max = std::numeric_limits<ret_t>::min(); 
    uint64_t total = 0, count = 10000000; 
    for (uint64_t i = 0; i < count; ++i) { 
     auto res = f(); 
     min = std::min(min,res); 
     max = std::max(max,res); 
     total += res; 
    } 
    std::cout << "min: " << min << " mean: " << (total/count) << " max: " << max << std::endl; 
} 

int main() { 
    auto rnd1 = std::mt19937(std::random_device{}()); 
    auto rnd2 = std::default_random_engine(std::random_device{}()); 

    auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}())); 
    auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}())); 

    print_min_mean_max(rnd1); 
    print_min_mean_max(rnd2); 
    print_min_mean_max(rnd3); 
    print_min_mean_max(rnd4); 
} 

produce il output:

min: 234 mean: 2147328297 max: 4294966759 
min: 349 mean: 1073305503 max: 2147483423 
min: 601 mean: 1073779123 max: 2147483022 
min: -2147481965 mean: 178496 max: 2147482978 

Quindi, come possiamo vedere, mt19937 e default_random_engine hanno un intervallo predefinito diverso, quindi si consiglia l'uso di uniform_int_distribution.

Inoltre, default_int_distribution è [0, max_int] (non negativo), anche quando si utilizza un tipo di intero con segno. Deve fornire l'intervallo in modo esplicito se si desidera un intervallo completo.

Infine, its important to remember this in momenti come questi.

+2

Di nota, c'è una versione a 64 bit di 'std :: mt19937':' std :: mt19937_64' che restituisce 64 bit di casualità per chiamata. 'auto rnd5 = std :: mt19937_64 (std :: random_device {}()); // min: 4879020137534 media: 1655417118684 max: 18446741225191893648' – Sean

+0

A proposito, è sicuro riutilizzare lo stesso motore di numeri casuali con molte distribuzioni e std :: bind() o è meglio legare ogni distribuzione a una nuova istanza del motore? Com'è: 'std :: mt19937_64 random = std :: mt19937_64 (std :: random_device {}());' 'auto randomCardinality = std :: bind (std :: uniform_int_distribution (1, 4), random);' 'auto randomValue = std :: bind (std :: uniform_real_distribution (-1.0, 1.0), random);' – Xharlie

2

Ecco qui. Doppio casuale in un intervallo:

// For ints 
// replace _real_ with _int_, 
// <double> with <int> and use integer constants 

#include <random> 
#include <iostream> 
#include <ctime> 
#include <algorithm> 
#include <iterator> 

int main() 
{ 
    std::default_random_engine rng(std::random_device{}()); 
    std::uniform_real_distribution<double> dist(-100, 100); //(min, max) 

    //get one 
    const double random_num = dist(rng); 

    //or.. 
    //print 10 of them, for fun. 
    std::generate_n( 
     std::ostream_iterator<double>(std::cout, "\n"), 
     10, 
     [&]{ return dist(rng);}); 
    return 0; 
} 
Problemi correlati