2010-05-05 15 views
6

Sto cercando un generatore di numeri casuali che può essere influenzato. Per esempio, dire che voglio un numero casuale tra 1-5, con l'essere probabilità:Generatore casuale di numeri casuali

1: esce il 20% del tempo
2: arriva il 10% del tempo
3: viene su 40 % del tempo
4: arriva il 25% del tempo
5: esce il 5% del tempo

c'è qualcosa nella libreria standard, o di altre librerie là fuori che farebbe questo? In alternativa, c'è un modo efficace per farlo da solo?

+10

spero non stai scrivendo software per casinò! – Alan

+2

Haha no, sono sicuro che un casinò assumerebbe qualcuno un po 'più intelligente. – cmptrer

+1

Da ieri: http://stackoverflow.com/questions/2772882/c-picking-a-random-item-based-on-probabilities e questo era un duplicato di scads di versioni precedenti della stessa domanda (che io sono troppo pigro per trovare). La parola che potresti aver perso durante la ricerca è "discreta", che è importante in quanto un numero delle risposte qui sotto si applica meglio alle distribuzioni continue. – dmckee

risposta

9

La libreria di numeri casuali Boost offre la possibilità di specificare diverse distribuzioni sagomate per il generatore. È una grande libreria - vedi http://www.boost.org/doc/libs/1_42_0/libs/random/index.html.

+1

Sì, non reinventare la ruota! –

+5

Un giorno comincerò a usare Boost. Un giorno. –

+0

Manca solo una documentazione adeguata ... sarebbe bello sapere quali sono i concetti richiesti per il motore e la distribuzione in modo da essere in grado di crearne uno senza la progettazione inversa della libreria :( –

15

per il vostro problema, basta scegliere un elemento casuale da questa lista in modo uniforme:

[1, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5] 

In generale, controllare questa risposta: Weighted random numbers


In TR1 e C++ 0x, non v'è <random> header che contiene lo discrete_distribution class per generare tali numeri, tra gli altri.

Si potrebbe anche voler controllare GSL che contiene molto più random distributions (e generatori di numeri casuali) rispetto alla libreria standard <random>. (Ma nota che GSL usa GPLv3.)

+2

Forse avrei dovuto spiegare la mia situazione attuale un po 'meglio. Quello di cui ho veramente bisogno sarà un numero casuale compreso tra 1 e 50.000. Creare una lista che a lungo sembra non necessaria e non necessaria. Dispiace per la confusione. – cmptrer

+6

Ecco perché una buona progettazione richiede una serie adeguata di requisiti. Hai chiesto il caso semplice e hai ottenuto la risposta (corretta) semplice. –

12

Il modo migliore è probabilmente prendere il normale generatore casuale non distorto e poi tornare in base all'intervallo in cui il suo valore cade.

Solo un'istruzione if che fornisce 1 per 0: 0.2, 2 per 0.2: 0.3, 3 per 0.3: 0.7, 4 per 0.7: 0.95 e 5 per 0.95: 1. Meglio rendere il limite inferiore o superiore dell'intervallo compreso e l'altro esclusivo.

int biasedRandom(){ 
double i = randomNumber(); 
if(i<= 0.2){return 1;} 
else if(i <= 0.3){return 2;} 
else if(i <= 0.7){return 3;} 
else if(i <= 0.95){return 4;} 
else{return 5;} 
} 

Qualcosa del genere.

+2

Se hai un sacco di intervalli da controllare, quello che dovresti fare è trovare un array di distribuzione cumulativo (non sapevo come chiamarlo) e fare una ricerca binaria su di esso ogni volta per trovare il numero che era generato. –

+2

Sembra che questo generatore restituisca sempre 5. Double/floats necessari e qualche altra matematica a meno che randomNumber() restituisca un valore 0-1. – Xorlev

+0

Sì, avevi ragione, avrebbe dato 5 ogni volta, non volevo renderlo un int, era un errore. Grazie per segnalarlo. – AaronM

0

Perché non usi semplicemente un normale generatore di numeri casuali che restituisce un numero compreso tra 0.0 e 1.0 e lo avvolge con un'altra funzione che restituisce un numero in base alle tue esigenze?

come

double biased (double seed) { 
if (seed >= 0.0 && seed <0.2) return 1; 
else if ... 
} 
+2

Non userei 'seed' come identificatore per un numero generato a caso, è confuso ... –

+0

Perché no? La funzione C rand prende anche un seme, nella maggior parte dei casi il tempo di sistema. Quindi, il pregiudizio è una funzione che genera un numero casuale basato su un seme. – DaClown

0

gettare un casuale numero reale x in [0,1], if 0< x<0.2 return 1, if 0.2<x <0.3 return 2, ecc

Vedere here per il problema generale.

0

Kenny ha fornito una risposta appropriata in base alla particolare distribuzione di frequenza.

La risposta più generale funziona con un CDF - Funzione di distribuzione cumulativa - per i dati e utilizza un numero casuale uniforme per selezionare un valore all'interno della distribuzione.

5

Quello che stai descrivendo è l'implementazione di un generatore di numeri casuali che attinge da una particolare distribuzione di probabilità. Ad esempio, disegnando i numeri da una distribuzione gaussiana si devono disegnare numeri casuali tali che la probabilità di una particolare estrazione, x è proporzionale a alt text http://upload.wikimedia.org/math/1/8/4/184fa5540b76903b1653d9f83912265d.png.

In generale, l'approccio consiste nel disegnare da una distribuzione casuale uniforme e quindi selezionare il valore della funzione di distribuzione cumulativa della distribuzione desiderata (CDF) in quella posizione disegnata. Nel caso di un gaussiano normale, traccia un numero casuale, x da una distribuzione uniforme (questo è ciò che i generatori di numeri casuali standard dovrebbero fornire) e quindi scegli alt text come valore distribuito casuale gaussiano. Per il tuo caso, il CDF che descrivi è una funzione di gradino continuo continuo pezzo-saggio che può essere implementata utilizzando una delle tante (corrette) risposte che hai già ricevuto.

Naturalmente, questo è tutto trivia. Quello che stai facendo dovrebbe fare sta usando una libreria che già gestisce questo per voi. Le statistiche e la generazione di numeri casuali non sono banali e non è necessario reinventare la ruota. Vedi la risposta di Neil (e controlla la libreria Boost random number).

4

In ritardo per la festa su questo. Ecco la risposta C++ 0x:

#include <iostream> 
#include <random> 
#include <iterator> 

int main() 
{ 
    // Set up distribution 
    double interval[] = {1, 2, 3, 4, 5, 6}; 
    double weights[] = { .2, .1, .4, .25, .05}; 
    std::piecewise_constant_distribution<> dist(std::begin(interval), 
               std::end(interval), 
               std::begin(weights)); 
    // Choose generator 
    std::mt19937 gen; // seed as wanted 
    // Demonstrate by pouring into avg[rand-1] 
    const unsigned N = 1000000; 
    double avg[sizeof(weights)/sizeof(weights[0])] = {0}; 
    for (unsigned i = 0; i < N; ++i) 
     avg[static_cast<unsigned>(dist(gen)) - 1]++; 
    // Comute averages 
    for (double* i = std::begin(avg); i < std::end(avg); ++i) 
     *i /= N; 
    // Display 
    for (unsigned i = 1; i <= sizeof(avg)/sizeof(avg[0]); ++i) 
     std::cout << "avg[" << i << "] = " << avg[i-1] << '\n'; 
} 

che per me le uscite:

avg[1] = 0.199779 
avg[2] = 0.100002 
avg[3] = 0.400111 
avg[4] = 0.250257 
avg[5] = 0.049851 
+0

+1 Risposta eccellente! –

0
#include <boost/random/discrete_distribution.hpp> 
#include <boost/random/mersenne_twister.hpp> 
#include <boost/random/variate_generator.hpp> 

#include <iostream> 

int main() 
{ 

    unsigned int seed = 42; 
    boost::mt19937 generator(seed); 

    // return 0 with probability 10% 
    //  1     40% 
    //  2     50% 
    boost::random::discrete_distribution<int> custom_dist{1,4,5}; 

    boost::variate_generator<boost::mt19937&, 
    boost::random::discrete_distribution<int> > rndn(generator, custom_dist); 

    for (unsigned int i = 0; i<10000; i++) { 
    std::cout << rndn() << std::endl; 
    } 

    return 0; 

} 

E qui è un grafico del risultato:

Output

Problemi correlati