So i have a Random object:
typedef unsigned int uint32;
class Random {
public:
Random() = default;
Random(std::mt19937::result_type seed) : eng(seed) {}
private:
uint32 DrawNumber();
std::mt19937 eng{std::random_device{}()};
std::uniform_int_distribution<uint32> uniform_dist{0, UINT32_MAX};
};
uint32 Random::DrawNumber()
{
return uniform_dist(eng);
}
What's the best way I can vary (through another function or otherwise) the upper bound of of the distribution?
(also willing to take advice on other style issues)
Distribution objects are lightweight. Simply construct a new distribution when you need a random number. I use this approach in a game engine, and, after benchmarking, it's comparable to using good old rand()
.
Also, I've asked how to vary the range of distribution on GoingNative 2013 live stream, and Stephen T. Lavavej, a member of the standard committee, suggested to simply create new distributions, as it shouldn't be a performance issue.
Here's how I would write your code:
using uint32 = unsigned int;
class Random {
public:
Random() = default;
Random(std::mt19937::result_type seed) : eng(seed) {}
uint32 DrawNumber(uint32 min, uint32 max);
private:
std::mt19937 eng{std::random_device{}()};
};
uint32 Random::DrawNumber(uint32 min, uint32 max)
{
return std::uniform_int_distribution<uint32>{min, max}(eng);
}
You can simply create a std::uniform_int_distribution<uint32>::param_type
and modify the range using the param()
method. You can cut down the template noise with decltype
:
decltype(uniform_dist.param()) new_range (0, upper);
uniform_dist.param(new_range);
I'm making the DrawNumber
function public
for my example. You can provide an overload that takes an upper bound, and then pass a new uniform_int_distribution::param_type
to uniform_int_distribution::operator()
The param_type
can be constructed using the same arguments as the corresponding distribution.
From N3337, §26.5.1.6/9 [rand.req.dist]
For each of the constructors of D
taking arguments corresponding to parameters of the distribution, P
shall have a corresponding constructor subject to the same requirements and taking arguments identical in number, type, and default values. Moreover, for each of the member functions of D
that return values corresponding to parameters of the distribution, P
shall have a corresponding member function with the identical name, type, and semantics.
where D
is the type of a random number distribution function object and P
is the type named by D
's associated param_type
#include <iostream>
#include <random>
typedef unsigned int uint32;
class Random {
public:
Random() = default;
Random(std::mt19937::result_type seed) : eng(seed) {}
uint32 DrawNumber();
uint32 DrawNumber(uint32 ub);
private:
std::mt19937 eng{std::random_device{}()};
std::uniform_int_distribution<uint32> uniform_dist{0, UINT32_MAX};
};
uint32 Random::DrawNumber()
{
return uniform_dist(eng);
}
uint32 Random::DrawNumber(uint32 ub)
{
return uniform_dist(eng, decltype(uniform_dist)::param_type(0, ub));
}
int main()
{
Random r;
std::cout << r.DrawNumber() << std::endl;
std::cout << r.DrawNumber(42) << std::endl;
}