C++11 discrete distribution random number generator
The C++11 discrete distribution( or discrete_distribution) produces random integers ‘i’,using the discrete probability function-the function is shown at the end of this post.
Link :C++ random number generator
The discrete_distribution template class declaration is shown below.
template<class IntType = double > class discrete_distribution;
The default type is int type ,so the default constructed object always produces int type random numbers.
All the distribution’s types and member function is given below.
Types
typedef IntType result_type; typedef unspecified param_type;
The param_type is as structure whose definition is compiler dependent.
Constructors and reset function
discrete_distribution( ); template<class InputIterator> discrete_distribution( InputIterator firstW , InputIterator lastW ); discrete_distribution( initializer_list<double> wl ); template<class UnaryOperation> discrete_distribution( size_t nw , double xmin , double xmax , UnaryOperation fw ); explicit discrete_distribution(const param_type& parm); void reset( );
You can see that the discrete_distribution have five constructors:
a)The first constructor is the default constructor.
b)The second constructor accept two arguments: firstW and lastW are iterators pointing to the first and one past the last element of the same container.The container can be vector or array or any equivalent type.
c)The third constructor accept initializer_list.
d)The fourth constructor accept 4 arguments.Refer to the first three arguments type from the above declaration but the fourth argument is a function which return a double type or a type convertible to double type.
e)The fifth constructor accept param_type object.
The program below shows how to declare the distribution’s object by calling the different constructors.
double func(double arg) { return arg; } int main( ) { discrete_distribution< > dd; //calls the default constructor vector<int> vec={ 20 , 30 , 4 , 67 , 90 , 41 , 51}; discrete_distribution< > dd1(vec.begin() , vec.end() ); //calls the second constructor initializer_list< double > il={ 2.3 , 6.7 , 9.034 , 13.89 }; discrete_distribution< > dd2(il) ; //calls the third constructor discrete_distribution<unsigned int > dd3( 9 , 0 , 100 , func ); //calls the 4th constructor discrete_distribution<unsigned int >::param_type pt; discrete_distribution<unsigned int > dd4(pt) ; //calls the 5th constructor discrete_distribution< > dd5(pt) ; //error! pt is ‘unsigned int’ type but dd5 is ‘int’ type return 0; }
reset()
The reset() function reset the state of the distribution.It does nothing in discrete_distribution so you can neglect it.
Generating functions
template<class URNG> result_type operator( )(URNG& g); template<class URNG> result_type operator( )(URNG& g, const param_type& parm);
the first operator() function
The random sequence is obtained using the operator() function.The first overloaded operator() accept URNG(Uniform Random Number Generator) or engine.
discrete_distribution< > dd; default_random_engine dre ; cout<< dd(dre ) << ” ” << dd(dre ) ; //generate two random numbers discrete_distribution< > dd1dd5(9 , 0 , 100 , func ); //func is the function from before cout<< dd1(dre ) << ” ” << dd1(dre ) ; //generate two random numbers
Output in Code::blocks,
6 3
If the distribution object is constructed by calling the default constructor the output will be always 0,so ‘dd’ will always give 0 as it’s random output.
the second operator( ) function
The second overloaded operator( ) function accept URNG and param_type object.
discrete_distribution<unsigned int >::param_type pt; discrete_distribution<unsigned int > dd(pt) ; default_random_engine dre ; cout<< dd(dre , pt ) << ” ” << dd(dre , pt ) ;
Output,
Here ‘dd’ is also constructed by calling the default constructor so the output is 0 and 0.
Property functions
vector<double> probabilities( ) const; param_type param() const; void param(const param_type& parm); result_type min() const; result_type max() const;
probabilities() const; function
This function returns a vector of double type.The returned vector stores the probabilites of the random number generated by distribution.The nth element of the vector will correspond to the probabilities of the nth generated random number.
vector<double> vecRet={ 0 , 0 , 0 } ; vector<int>vec={ 11 , 02 , 13 }; discrete_distribution< > dd( vec.begin() , vec.end() ); vecRet=dd.probabilities( ); for( auto elem:vecRet ) { cout<< elem << ” ” ; }
Output in code::blocks,
Suppose if ‘dd’ generates a random numbers say ‘x , y , z’ using any engine,then x’s probability will be 0.423077 , y’s probability will be 0.0769231 and z’s probability will b 0.5.This also shows that the probability of any random number generated is not dependent on the engine’s used and also it is not dependent on the random number which the engine pass to the distribution.
param( )
This function returns the param_type object.
discrete_distribution< > dd( 15 , 0 , 100 , func) ; //func is from before discrete_distribution< >::param_type pt; pt=dd.<span class="funcName">param</span>() ;
param(param_type)
Using this function we can change the parameter of the distribution to the parameter of the param_type object by passing the param_type object.
discrete_distribution< > dd( 15 , 0 , 100 , func) , dd1( 15 , 0 , 100 , func) ; //dd and dd1 will give the same random sequence default_random_engine dre1 , dre2 ; cout<< dd( dre1 ) << ” ” << dd(dre2) << endl << dd(dre1) << ” ” << dd(dre2) << endl ; discrete_distribution< >::param_type( 25 , 0 , 8 , func ); dd1.param(pt); cout<< dd( dre1 ) << ” ” << dd(dre2) ;
Output in code::blocks,
3 3
4 11
The first and second output of both the dd and dd1 objects are same because they have the same parameters but after calling param(pt) the parameters of dd1 is changed to the parameter of pt.Since different parameter will give different output the third output of dd and dd1 is different.
min() function
The min() returns the smallest value the distribution can generate,which is the value 0.
discrete_distribution< > dd( 15 , 0 , 100 , func) ; cout<< dd.min() ;
Output ,
The minimum value is always 0.
max() function
The max() returns the largest value the distribution can generate.To determine the largest value the distribution can generate we will examine the constructors called by the object’s during it’s declaration.
a)If the object calls the default constructor,the maximum value is always 0.
b)If the object accept vector as it’s argument then the maximum value the distribution can generate is the number of elements of the vector minus 1:vector object size() – 1 .If the vector contains no element then 0 is the maximum value.
c)If the object accept initializer_list object as it’s argument then the maximum value the distribution can generate is the size of the initializer_list object minus 1 .If the object contains no element then 0 is the maximum value.
c)If the object calls the constructor that accept four arguments than the maximum value is the first argument minus 1.
discrete_distribution< > dd ; cout<< dd.max() << endl ; discrete_distribution< > dd1( 15 , 0 , 100 , func ) ; cout<< dd1.max() ;
Output ,
14
For dd1 the first argument is 15 so the maximum value is 14(15-1).You can check yourself the maximum value of object accepting vector and initializer_list object during the constructor call.
operator== and operator!=
The two functions operator== and operator!= check if the parameters value are equal.If the parameters are equal operator== returns true and operator!= returns 0.The operator== returns true (and operator!= false) as long as the parameters value are equal no matter what state the distribution object is in.
discrete_distribution< > dd , dd1 ; cout<< (dd==dd1) << ” ” << (dd!=dd1) << endl ; discrete_distribution< > dd2(12 , 0 , 50 , func) , dd3( 20 , 0 , 60 , func ); cout<< (dd2==dd3) << ” ” << (dd2!=dd3) << endl ;
Output,
0 1
operator<< and operator>>
The function operator<< allows you to save the state of the distribution.This function also allows you to save the state of the engine.The state saved can later be regained by using the operator>> function.Using this regained state we can reproduce the same random sequence which was generated earlier when the distribution was at that state.Note for the distribution to reproduce the same sequence not only the distribution’s state but also the engine’s state must be the same.
In the code below we will try to save the distribution state and also save the engine’s state,in this way we will be able to reproduce the same random sequence.
#include <iostream> #include <random> #include <sstream> using namespace std; double func(double arg) { return arg; } int main( ) { stringstream engState , //object to save the engine state disState ; //object to save the distribution state discrete_distribution<>ddIO1(25 , 0 , 80 , func) , ddIO2(25 , 0 , 80 , func) ; default_random_engine dre1 , dre2 ; cout<< ddIO1(dre1) << endl ; //first random number disState<< ddIO1 ; //Save the 2nd distribution state of ddIO to disState engState<< dre1 ; //Save the 2nd engine state of dre1 to engState cout<< “Second and third state output of ddIO1 and dre1 \n”; cout<< ddIO1( dre1 ) << ” ” ; cout<< ddIO1(dre1) << endl ; disState>> ddIO2 ; //reassign the distribution state saved in disState to ddIO2 engState>> dre2 ; //reassign the engine’s state saved in engState to dre2 cout<< “\n\nOutputting the random sequence using ddIO2 and dre2 current state\n”; cout<< ddIO1( dre2 ) << ” ” ; cout<< ddIO1( dre2 ) << endl ; cin.get( ); return 0; }
Output in Code::blocks,
Second and third state output of ddIO1 and dre1
16 11
Outputting the random sequence using ddIO2 and dre2 current state
16 11
The second sate of ddIO1 and dre1 has been assigned to ddIO2 and dre2,so the numbers generated by the second sate and it’s consecutive state of ddIO1 and dre1 will be same as the sequence generated by the ddIO2 and dre2 current state and it’s consecutive state.Note reproducing the same sequence can be useful for debugging purpose.
Side note
discrete_distribution produces random integers i,0≤ i < n,distributed according to the discrete probability function,
P(i/p0, … , pn-1) = pi
The ‘n’ value is ‘max()+1’ (look at the max( ) function given above).
And,
pk=wk / S ;
k=0, 1 , … , n-1
0<S=w0 + … + wn-1
To know the sequence ‘w’ consider the cases given below,
Each overloaded constructor can be referred from the Constructor section above.
i)If the default constructor is called , n= 1 and p0= 1, and such default constructed object always give the value 0.
ii)If vector is passed to the constructor :
a)If firstW == lastW(vector contains no element) , let n=1 and w0=1
b)Else [firstW , lastW) form the sequence ‘w‘ and n=vector size-1 .
iii)If initializer_list is passed as the constructor argument it is same as when vector is pass and in this case firstW=il.begin() and lastW=il.end().
iv)If four arguments is passed to the constructor,
a)If the first argument nw=0, then n=0 and w0=1.
b)Else n=nw , 0 < δ= ( xmax − xmin ) /n ,and wk=fw(xmin + k·δ + δ/2),
k=0, 1 , … , n-1
v)if param_type object is passed then any of the rules above will apply depending on the argument type the param_type object accepted when it was constructed.