Generazione di numeri casuali in C ++ 11: come generare, come funziona? [chiuso]


102

Recentemente mi sono imbattuto in un nuovo modo per generare numeri casuali in C ++ 11, ma non sono riuscito a digerire i documenti che ho letto su di esso (cos'è quel motore , termine matematico come distribuzione , "dove tutti gli interi prodotti sono ugualmente probabili ").

Quindi qualcuno può spiegare

  • quali sono?
  • cosa significano?
  • come generare?
  • Come funzionano?
  • eccetera

Puoi chiamare tutto in una FAQ sulla generazione di numeri casuali.


6
Chiedere informazioni sugli RNG senza sapere cos'è una distribuzione è come chiedere informazioni sui parser di espressioni senza sapere cosa sia un'espressione ... La libreria RNG in C ++ 11 è rivolta a persone che conoscono alcune statistiche e hanno maggiori esigenze rispetto alla distribuzione piatta generata da rand, dovresti dare una rapida occhiata a wikipedia per alcune statistiche di base e concetti RNG, altrimenti sarà davvero difficile spiegarti la logica <random>e l'utilizzo dei suoi vari pezzi.
Matteo Italia

26
@Matteo: Difficilmente. Un bambino può afferrare il concetto che un dado produce numeri casuali, senza capire cosa sia una distribuzione.
Benjamin Lindley

3
@Benjamin: ed è qui che si ferma la sua comprensione, che è solo il primissimo passo (i motori), e senza nemmeno capire perché è importante che generino una distribuzione piatta. Tutto il resto della libreria rimane un mistero senza comprendere le distribuzioni e altri concetti statistici.
Matteo Italia

Risposte:


142

La domanda è troppo ampia per una risposta completa, ma permettimi di scegliere un paio di punti interessanti:

Perché "altrettanto probabile"

Supponi di avere un semplice generatore di numeri casuali che genera i numeri 0, 1, ..., 10 ciascuno con la stessa probabilità (pensa a questo come al classico rand()). Ora vuoi un numero casuale nell'intervallo 0, 1, 2, ciascuno con la stessa probabilità. La tua reazione istintiva sarebbe quella di prendererand() % 3 . Ma aspetta, i resti 0 e 1 si verificano più spesso del resto 2, quindi questo non è corretto!

Questo è il motivo per cui abbiamo bisogno di distribuzioni appropriate , che prendono una fonte di interi casuali uniformi e li trasformano nella nostra distribuzione desiderata, comeUniform[0,2] nell'esempio. Meglio lasciare questo a una buona libreria!

motori

Quindi al centro di tutta la casualità c'è un buon generatore di numeri pseudocasuali che genera una sequenza di numeri distribuiti uniformemente su un certo intervallo e che idealmente hanno un periodo molto lungo. L'implementazione standard di rand()non è spesso la migliore, quindi è bene avere una scelta. Linear-congruential e il Mersenne twister sono due buone scelte (LG è in realtà spesso usato darand() ); ancora una volta, è bene lasciare che sia la libreria a gestirlo.

Come funziona

Facile: per prima cosa, imposta un motore e semina. Il seme determina completamente l'intera sequenza di numeri "casuali", quindi a) usane uno diverso (ad esempio preso da /dev/urandom) ogni volta, eb) memorizza il seme se desideri ricreare una sequenza di scelte casuali.

#include <random>

typedef std::mt19937 MyRNG;  // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val;           // populate somehow

MyRNG rng;                   // e.g. keep one global instance (per thread)

void initialize()
{
  rng.seed(seed_val);
}

Ora possiamo creare distribuzioni:

std::uniform_int_distribution<uint32_t> uint_dist;         // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation);  // N(mean, stddeviation)

... E usa il motore per creare numeri casuali!

while (true)
{
  std::cout << uint_dist(rng) << " "
            << uint_dist10(rng) << " "
            << normal_dist(rng) << std::endl;

}

Concorrenza

Un motivo più importante per preferire <random>il tradizionale rand()è che ora è molto chiaro e ovvio come rendere sicura la generazione di numeri casuali: o fornire a ogni thread il proprio motore locale del thread, sezionato su un seme locale del thread o sincronizzare l'accesso all'oggetto motore.

Varie

  • Un interessante articolo su TR1 random su codeguru.
  • Wikipedia ha un buon riassunto (grazie, @Justin).
  • In linea di principio, ogni motore dovrebbe result_typedigitare a, che è il tipo integrale corretto da utilizzare per il seme. Penso di aver avuto un'implementazione buggy una volta che mi ha costretto a forzare il seme per std::mt19937a uint32_tsu x64, alla fine questo dovrebbe essere risolto e si può dire MyRNG::result_type seed_val, e quindi rendere il motore molto facilmente sostituibile.

Ancora una volta, Kerrek mi ha battuto sul tempo con una risposta molto migliore di quella su cui stavo lavorando. +1
Justin ᚅᚔᚈᚄᚒᚔ

@ Justin: sono sicuro di essermi perso un sacco di cose, sentiti libero di aggiungere ulteriori aspetti a questo argomento! :-)
Kerrek SB

13
Per la parte "popola in qualche modo", penso che std::random_devicevalga la pena menzionare piuttosto che/dev/urandom
Cubbi

2
Un esempio di std::random_devicepuò essere trovato qui .
WKS

1
Il codice nell'articolo di Wikipedia è difettoso. random e random2 sono identici. Dai commenti nello snippet di codice è chiaro che l'autore non capisce come usare le funzionalità in <random>.
user515430

3

Un generatore di numeri casuali è un'equazione che, dato un numero, ti darà un nuovo numero. In genere si fornisce il primo numero o viene estratto da qualcosa come l'ora di sistema.

Ogni volta che chiedi un nuovo numero, utilizza il numero precedente per eseguire l'equazione.

Un generatore di numeri casuali non è considerato molto buono se ha la tendenza a produrre lo stesso numero più spesso di altri numeri. cioè se volevi un numero casuale compreso tra uno e 5 e avevi questa distribuzione di numeri:

  • 1: 1%
  • 2: 80%
  • 3: 5%
  • 4: 5%
  • 5: 9%

2 viene generato MOLTO più spesso di qualsiasi altro numero, quindi è più probabile che venga prodotto rispetto ad altri numeri. Se tutti i numeri fossero uguali, avresti il ​​20% di possibilità di ottenere ogni numero ogni volta. Per dirla in un altro modo, la distribuzione di cui sopra è molto irregolare perché 2 è favorito. Una distribuzione con tutti i 20% sarebbe pari.

In genere, se si desidera un numero casuale reale, si estraggono dati da qualcosa come il tempo o un'altra fonte naturale piuttosto che un generatore di numeri casuali.


8
La maggior parte dei generatori di numeri casuali genera buone distribuzioni uniformi. Non sono solo casuali; il problema è che sono calcolati e quindi puoi indovinare il numero successivo dato un numero sufficiente nella sequenza (questo fatto li rende dannosi per la sicurezza dove sono richiesti numeri veramente casuali). Per giochi e cose dovresti stare bene.
Martin York

5
Sono abbastanza sicuro che l'OP stia chiedendo informazioni specifiche sulle funzionalità fornite nell'intestazione <random> C ++. Questa risposta non riguarda nemmeno la programmazione, figuriamoci C ++.
Benjamin Lindley

1
@ Martin: la sicurezza non richiede necessariamente una fonte di numeri veramente casuali. AES in modalità contatore (per un esempio) può fare abbastanza bene anche se è deterministico. Richiede una quantità ragionevole di entropia nella chiave, ma non una vera casualità.
Jerry Coffin

@Benjamin Lindley: Nevermind. Ho appena riletto e ho capito che mi sbagliavo.
N_A
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.