creating a non-uniform, integer distribution using tr1 <random>
Right now I use the following code to create a uniform distribution of integers with a range. (I took out the seeding code)
int random(int min, int max)
{
static std::mt19937 gen;
std::uniform_int<int> dist(min, max);
return dist(gen);
}
I am trying to modify it to give a distribution that favors twords the min value, and almost never produces nears the max value. I can see all of the pre-made distributions, but none of them are integer. And also I can't tell which one fits my needs based on any of the documentation. The closest I have come is the chi squared distribution as shown on wikipedia, where k=2
But I can't figure out, based on the documentation how to use it with integers, let alone set the k value.
How can I set up my function to use an appropriate non-uniform, integer distribution?
still working on choosing the correct distro: here are the results of std::poisson_distribution<int> dist((m开发者_如何学Pythonax - min) * .1);
from 0 to 20:
not quite there yet, as 0 should be more frequent than 1, but it should help the next person out, will post more results as they come.
well my final solution became a combination of methods:
int randomDist(int min, int max)
{
static std::mt19937 gen;
std::chi_squared_distribution<double> dist(2);
int x;
do
{
x = (int)(max*dist(gen)/10) + min;
}
while (x > max);
return x;
}
giving the result of:
There are other integer distributions there, they just don't have int
in their names. They do have typedef IntType result_type
in their class definitions.
The ones which behave as you describe are:
binomial_distribution
(t, p)
This generates numbers in the range 0 ≤ x ≤ t, so you need to translate the range by
min
. The mean is at t·p, so choose a p near 0.std::binomial_distribution<int> dist(max - min, .1);
return dist(gen) + min;
poisson_distribution
(λ)
This generates numbers 0 ≤ x < ∞, but large numbers are progressively less likely. You can censor anything above
max
to limit it to a range. The parameter λ is the mean. Choosing it to match the previous example:std::poisson_distribution<int> dist((max - min) * .1);
int x;
do
x = dist(gen) + min;
while (x > max);
return x;
geometric_distribution
(p)
Also generates numbers 0 ≤ x < ∞, but 0 is the most likely outcome and every subsequent number is less likely than the previous. Again choosing the parameter to match the mean of the previous example:
std::geometric_distribution<int> dist(1 / ((max - min) * .1 + 1));
int x;
do
x = dist(gen) + min;
while (x > max);
return x;
You can also use any of the continuous distributions to generate a double
and then round it to an int
.
In addition to the distributions said in @aaz's great answer, keep in mind that you can also transform your uniform distribution to any probability distribution function you may think of, using inverse transform sampling (that however is actually feasible only for some "nice" functions) or rejection sampling (can be applied in any case, but can be computationally expensive).
It seems to me that a distribution that would fit your needs would be the (negative) exponential distribution:
Luckily, it is one of the distributions to which you can apply inverse transform sampling, which means that, having a sample from a uniform [0, 1] distribution, you can get an exponential distribution by applying the formula:
x = - ln(1-p)/lambda
with p
is a random value from the uniform distribution and lambda
is the parameter of the exponential distribution; see here for more info.
Once you get x
(which will be a double
), just cast it to int
(or round it with a function like:
int round(double val)
{
// warning: can give counterintuitive results with negative numbers
return int(val+0.5);
}
) to obtain your result.
Edit
By the way, I didn't notice that even the exponential distribution is already included in <random>
(link)... well, even better, you don't need to write the code, but a little bit of theory is never wasted :)
.
精彩评论