> What do you mean by standard library compatible?
The standard library specifies the minimal requirements for a
uniform random number generator. A generator which meets these requirements will interoperate in a well defined manner with the random number facilities provided by the library.
For instance, if
my_random_number_generator is standard library compatible, the following would be possible:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
#include <iostream>
#include <random>
#include <ctime>
#include <iomanip>
#include <algorithm>
// a standard library compatible uniform random number generator
struct my_random_number_generator // crude lcg: generates random numbers in the range [ 37, 547 ]
{
my_random_number_generator( unsigned int seed ) : seed(seed) {}
using result_type = unsigned int ;
static constexpr result_type min() { return 37 ; }
static constexpr result_type max() { return modulus + min() - 1 ; }
result_type operator() () { return seed = ( multiplier * seed ) % modulus + min() ; }
private:
// note: only for exposition
static constexpr unsigned int modulus = 511 ;
static constexpr unsigned int multiplier = 48271 ;
unsigned int seed = 0 ;
};
int main()
{
my_random_number_generator rng( std::time(nullptr) ) ;
std::cout << std::fixed << std::setprecision(1) ;
// generate 10 integers uniformly distributed in [ -5000, +9000 ]
std::uniform_int_distribution<int> distr( -5000, 9000 ) ;
for( int i = 0 ; i < 10 ; ++i ) std::cout << distr(rng) << ' ' ;
std::cout << '\n' ;
// generate 10 floating point numbers uniformly distributed in [ 0, 1 )
for( int i = 0 ; i < 10 ; ++i ) std::cout << std::generate_canonical<double,64>(rng) << ' ' ;
std::cout << '\n' ;
// generate 10 floating point numbers normal distribution with mean 13 and standard deviation 2.5
std::normal_distribution<double> normal( 13.0, 2.5 ) ;
for( int i = 0 ; i < 10 ; ++i ) std::cout << normal(rng) << ' ' ;
std::cout << '\n' ;
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } ;
// shuffle the contents of the array randomly
std::shuffle( std::begin(array), std::end(array), rng ) ;
for( int v : array ) std::cout << v << ' ' ;
std::cout << '\n' ;
}
|
http://coliru.stacked-crooked.com/a/f78c5ad3123d94f7
> what is the point behind "using return_type = uint64_t;" or the last line about "operator"?
These are the requirements for a standard library compatible
uniform random number generator:
http://en.cppreference.com/w/cpp/concept/UniformRandomNumberGenerator
> Isn't a high quality generator going to return a uniform real distribution?
A uniform random number generator g of type G is a function object returning unsigned integer values such that each value in the range of possible results has (ideally) equal probability of being returned. [Note: The degree to which g’s results approximate the ideal is often determined statistically. —end note ] - IS |
> What is with the extra stuff, like "generate_canonical" if the function can just return that anyway?
> (just have "return 1/ double(rnRaw());")
Let us say, the random number generator generates
n-bit unsigned integers.
And the mantissa of
double has
m bits.
The rng can generate
2n distinct values.
To get a uniformly distributed double value in [0.0,1.0), we would need to pick one out of
2m-1 possible distinct values. (
m-1, assuming that we are not interested in denormal values.)
If
n is less than
m-1, to generate enough entropy, we would need to generate more than one
n-bit random integer.
std::generate_canonical does that in an optimal manner.
For instance, with our earlier toy
my_random_number_generator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
#include <iostream>
#include <random>
#include <ctime>
#include <iomanip>
#include <limits>
// a standard library compatible uniform random number generator
struct my_random_number_generator // crude lcg: generates random numbers in the range [ 37, 547 ]
{
my_random_number_generator( unsigned int seed ) : seed(seed) {}
using result_type = unsigned int ;
static constexpr result_type min() { return 37 ; }
static constexpr result_type max() { return modulus + min() - 1 ; }
result_type operator() ()
{
static int n = 0 ;
std::cout << ++n << ' ' ;
return seed = ( multiplier * seed ) % modulus + min() ;
}
private:
// note: only for exposition
static constexpr unsigned int modulus = 511 ;
static constexpr unsigned int multiplier = 48271 ;
unsigned int seed = 0 ;
};
int main()
{
my_random_number_generator rng( std::time(nullptr) ) ;
std::generate_canonical< float, std::numeric_limits<float>::digits >(rng) ; // calls rng() three times
std::cout << '\n' ;
std::generate_canonical< double, std::numeric_limits<double>::digits >(rng) ; // calls rng() seven times
std::cout << '\n' ;
std::generate_canonical< long double, std::numeric_limits<long double >::digits>(rng) ; // // calls rng() eight times
std::cout << '\n' ;
}
|
http://coliru.stacked-crooked.com/a/cbd1553a5df4ee62