changing probability of getting a random number - c++

I would like to generate a random number between 0 and 3 and I have the following in my code:
int random = rand() % 4;
This works fine but I would like it to generate 1, 2, and 3 most of the time and 0 only occasionally.
What is the best way to go about this? What are the names of common algorithms to address this problem?

Here's one way. Suppose you want 0, 1, 2, 3 to have a distribution of 5%, 20%, 30%, 45%.
You could do it like this:
double val = (double)rand() / RAND_MAX;
int random;
if (val < 0.05) // 5%
random = 0;
else if (val < 0.25) // 5% + 20%
random = 1;
else if (val < 0.55) // 5% + 20% + 30%
random = 2;
else
random = 3;
Of course it doesn't have to be done with floating-point. I just did it this way since it's more intuitive.

You can use the discrete_distribution class from the random library.
#include <iostream>
#include <random>
#include <ctime>
int main()
{
std::discrete_distribution<> dist({ 1.0, 4.0, 4.0, 4.0 });
std::mt19937 eng(std::time(0));
for (int i=0; i<100; ++i)
std::cout << dist(eng);
}
Demo: http://ideone.com/z8bq4
If you can't use C++11, these classes also exist in boost.

You didn't give exact proportions, but suppose you want 1, 2, and 3 to each occur 32% of the time, and 0 to occur the other 4%. Then you could write:
int random = rand() % 25;
if(random > 0)
random = random % 3 + 1;
(Obviously you'd need to adjust that for different proportions. And the above is just one approach; many similar approaches could work.)

how many numbers have you tested this on? if it is actually true you can instead generate a range from say 0->3999 using a = rand()%4000 and use int = a/1000 this should remove the weight of the apparently under produced zero.

I would just map more values to 1,2,3 from a larger set. For example: 9 and map 1,2,3 => 1, 3,4,5=>2, 6,7,8=>3 and 0 for zero. There are other ways, but I am working within your question

Just code exactly what you want:
int myrand(void)
{
const int percentZero = 10;
if ((rand()%100) < percentZero) return 0;
return 1 + (rand() % 3);
}
You can change the percentage of time zero is returned to whatever you want.

You need to find a probability distribution that works for your case. Since you're only talking about the numbers 0-3 this is quite easy, you could either call rand() again if the first result is a 0, or you could use weights:
int random = rand() % 16;
if(random > 10)
{
random = 3;
}
else if(random > 5)
{
random = 2;
}
else if(random > 0)
{
random = 1;
}
This isn't a particularly elegant, but hopefully it shows you how you can create a custom distribution to fit your needs.

Related

c++ random numbers, evenly distributed

I need to generate random numbers between two values in c++, which can be generated like that(in a range 1 to 6)
:
#include <iostream>
#include <random>
int main(int argc, char* argv[]){
srand(time(NULL));
for (int j = 0; j < 6 ; j++) {
randomNumber = rand() % 6 + 1;
cout << randomNumber;
return 0;
}
But I also need to ensure that it generates at least once each number in the range, for example: range [1,6], you must generate at least once the 1,2,3,4,5 and 6.
And there is another thing, the numbers generated, must be evenly distributed, like if want generate 20 iterations in range of[1,6], each number(1,2,3,4,5,6) should have approximately the same quantity generated at 20 iterations. Example: 1,3,5,2,4,6,2,4,6,1,3,5,6,5,4,3,5,2,1.... and not like this: 1,3,5,6,6,6,2,6,4,4,4,4,4,4...
If anyone knows how to solve this, I'll be very grateful to you.
Thanks!!!
This is where the C++ Standard Library comes to the rescue. Typed off the top of my head:
#include <random>
int get_rand_in_1_6()
{
static std::random_device rnd;
return std::uniform_int_distribution <int> ( rnd )( 1, 6 );
}
The result will be uniformly-distributed.
(You may not get one of each in the first six iterations, though. Being uniformly distributed does not guarantee any specific values in any specific sub-interval, only that the number is just as likely as any other to appear. Given enough iterations, you will see that.)
[edit 2]: IDR the exact mathematical formula for minimum number of iterations to guarantee that you see each number at least once — I am wildly guessing around 15 for the minimum number of pulls to guarantee that.
If I understand correctly, std::shuffle might help you:
std::random_device rd;
std::mt19937 gen(rd());
int numbers[] = {1, 2, 3, 4, 5, 6};
for (int j = 0; j < 20 ; j++) {
if (j % 6 == 0) { std::shuffle(numbers, numbers + 6, gen); }
std::cout << numbers[j % 6];
}
Demo
You have then sequence of random permutations.

How to create a random number that doesn't repeat?

I have a variable that will either become 1 or 0, and all I know is rand()% 2.
The problem is when I loop it it keeps becoming either 1 for about four times straight then 0, or 0 for straight 6 times then 1.
I want it to be like 0 for once or twice, then 1, then 0 again. Something like that.
Is it possible to do this?
You either want a random number or a predictable result. You can't choose the amount of randomness, the whole point of a random number generator is to generate something unpredictable.
But what you can do is simply use the random number in a different way. If you want, say, at most, 4 consecutive runs of 0 or 1 you could determine the count of consecutive numbers using rand and generate the numbers yourself:
int number = 0;
for (int runs = 0; runs < 100; ++runs) {
int count = rand() % 4;
for (int i = 0; i < (count ? count : 1); ++i) { // Ensure at least 1 run
printf("%d", number);
}
number = 1 - number;
}
See codepad example:
http://codepad.org/OKe5Agib
If you really want to have only runs of 1 or 2, while maintaining some randomness, you can keep track of it like this;
int nextRandomIshThing( ) {
static int n1 = 0;
static int n2 = -1;
if( n1 != n2 ) {
n1 = n2;
// use a high-order bit, which supposedly has better randomness
// 14 because the standard guarantees that rand() produces at least
// 15 bits of randomness (not sure why that exactly)
n2 = (rand( ) >> 14) & 1;
} else {
n2 = !n2;
}
return n2;
}
http://codepad.org/HTTtPezu
But beware that depending on how you're using this, it means that users can "game" your system; "I've seen 2 1's, therefore the next must be 0!". A truly random source will always produce long sequences. There is a 1 in 8 chance for a truly random source to produce 4 1's or 0's in a row, and a 1 in 16 chance of 5. When you consider that you don't care where exactly the run starts, this becomes even more likely. If you want to be fair, embrace this instead of fighting it!
Oh and don't forget to srand.

How to generate negative random integer in c++

I wrote a function that takes integers. It won't crash if the user types for example, -5, but it will convert it into positive =-(
int getRandoms(int size, int upper, int lower)
{
int randInt = 0;
randInt = 1 + rand() % (upper -lower + 1);
return randInt;
}
What should I change in the function in order to build random negative integers?
The user inputs the range.
There are two answers to this, if you are using C++11 then you should be using uniform_int_distribtion, it is preferable for many reasons for example Why do people say there is modulo bias when using a random number generator? is one example and rand() Considered Harmful presentation gives a more complete set of reasons. Here is an example which generates random integers from -5 to 5:
#include <iostream>
#include <random>
int main()
{
std::random_device rd;
std::mt19937 e2(rd());
std::uniform_int_distribution<int> dist(-5, 5);
for (int n = 0; n < 10; ++n) {
std::cout << dist(e2) << ", " ;
}
std::cout << std::endl ;
}
If C++11 is not an option then the method described in C FAQ in How can I get random integers in a certain range? is the way to go. Using that method you would do the following to generate random integers from [M, N]:
M + rand() / (RAND_MAX / (N - M + 1) + 1)
For a number in the closed range [lower,upper], you want:
return lower + rand() % (upper - lower + 1); // NOT 1 + ...
This will work for positive or negative values, as long as upper is greater than or equal to lower.
Your version returns numbers from a range of the same size, but starting from 1 rather than lower.
You could also use Boost.Random, if you don't mind the dependency. http://www.boost.org/doc/libs/1_54_0/doc/html/boost_random.html
You want to start by computing the range of the numbers, so (for example) -10 to +5 is a range of 15.
You can compute numbers in that range with code like this:
int rand_lim(int limit) {
/* return a random number in the range [0..limit)
*/
int divisor = RAND_MAX/limit;
int retval;
do {
retval = rand() / divisor;
} while (retval == limit);
return retval;
}
Having done that, getting the numbers to the correct range is pretty trivial: add the lower bound to each number you get.
Note that C++11 has added both random number generator and distribution classes that can take care of most of this for you.
If you do attempt to do this on your own, when you reduce numbers to a range, you pretty much need to use a loop as I've shown above to avoid skew. Essentially any attempt at just using division or remainder on its own almost inevitably introduces skew into the result (i.e., some results will happen more often than others).
You only need to sum to the lower-bound of the range [lbound, ubound]:
int rangesize = ubound - lbound + 1;
int myradnom = (rand() % rangesize) + lbound;

More efficent way of generating this random distribution?

Is there a more efficent, possibly more mathematical and less algorithmic way of achieving a similar random number distribution to this?
unsigned int weighted_random_UINT()
{
float r2 = 1;
while(rand() % 4 != 0) // 3/4 chance
{
r2 *= fmod(
((float)rand()/RAND_MAX)+1, // random float between 1 and 2
(float)UINT_MAX
);
}
return (unsigned int)r2 - 1;
}
Below is a less safe but more easily readable version of the inside of the while.
r2 *= ((float)rand()/RAND_MAX)+1;
The distribution visualized:
Comparison between the smoother solution in the question (1st graph) and the faster solution in the best answer (2nd graph):
comparison http://with-logic.co.uk/a/graph.png
I think you don't have to loop through it, but once is enough, like so:
unsigned int weighted_random_UINT()
{
float r2 = ((float)rand()/RAND_MAX)+1; // random float between 1 and 2
unsigned int k = 0;
while(rand() % 4 != 0) // 3/4 chance
{k = k < UINT_MAX ? k + 1: UINT_MAX;}
return (unsigned int)fpow(r2,(float)k) - 1;
}
The first part is a geometric distribution, and the last one is an uniform distribution.
And you want (1+U(0,1))^G(3/4).
It should be possible to find some faster way to find G(3/4) though.
Edit:
I found it on wikipedia:
http://en.wikipedia.org/wiki/Geometric_distribution#Related_distributions
G(p)=floor(ln(U)/ln(1-p))
Thus you want:
U^floor(ln(U)/ln(1-3/4))
Which should be just two calls to rand.

Biased Random Number Generator

I am looking for a random number generator that can be biased. For instance, say I want a random number between 1-5, with the probability being:
1: Comes up 20% of the time
2: Comes up 10% of the time
3: Comes up 40% of the time
4: Comes up 25% of the time
5: Comes up 5% of the time
Is there anything in the standard library, or other libraries out there that would do this? Alternatively, is there an efficient way to do this myself?
For your problem, just pick a random element from this list uniformly:
[1, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5]
In general, check this answer: Weighted random numbers
In TR1 and C++0x, there is <random> header which contains the discrete_distribution class to generate such numbers, among others.
You may also want to check out GSL which contains much more random distributions (and random number generators) than the standard <random> library. (But note that GSL uses GPLv3.)
Best way's probably to just take the normal unbiased random generator then return based on the interval its value falls into.
Just an if statement that gives 1 for 0:0.2, 2 for 0.2:0.3, 3 for 0.3:0.7, 4 for 0.7:0.95 and 5 for 0.95:1. Best to make either the lower or upper limit of the interval inclusive and the other exclusive.
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;}
}
Something like that.
The Boost random number library provides the ability to specify different shaped distributions for your generator. It's a great library - see http://www.boost.org/doc/libs/1_42_0/libs/random/index.html.
Coming late to the party on this one. Here is the C++0x answer:
#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';
}
Which for me outputs:
avg[1] = 0.199779
avg[2] = 0.100002
avg[3] = 0.400111
avg[4] = 0.250257
avg[5] = 0.049851
What you are describing is the implementation of a random number generator that draws from a particular probability distribution. For example, drawing numbers from a Gaussian distribution should draw random numbers such that the probability of a particular draw, x is proportional to
(source: wikimedia.org)
.
In general, the approach is to draw from a uniform random distribution and then pick the value of the desired distribution's cumulative distribution function (CDF) at that drawn location. In the case of a Normal Gaussian, draw a random number, x from a uniform distribution (this is what standard random number generators should give) and then choose as the random, Gaussian distributed value. For your case, the CDF you describe is a piece-wise continuous stair-step function which could be implemented using any of the many (correct) answers you have already received.
Of course, this is all trivia. What you should be doing is using a library that already handles this for you. Statistics and random number generation are not trivial and there's no need to re-invent the wheel. See Neil's answer (and check out the Boost random number library).
Why don't you just use a regular random number generator that return number between 0.0 and 1.0, and wrap it with another function that returns a number according to your requirements?
like
double biased (double seed) {
if (seed >= 0.0 && seed <0.2) return 1;
else if ...
}
Throw a random real number x in [0,1], if 0< x<0.2 return 1, if 0.2<x <0.3 return 2, etc.
See here for the general problem.
Kenny gave an appropriate answer tailored to your particular frequency distribution.
The more general answer works with a CDF - Cumulative Distribution Function - for the data, and uses a uniform random number to pick a value within the distribution.
I am doing to do the same thing and I found this:
http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/
Seems good enough for the purpose you stated.
#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;
}
And here is a plot of the result:
I was looking for something like this for TypeScript, but only found this question for C.
So here is a biased random number generator in TypeScript I came up with, in case anybody needs something like this in TypeScript. I am sure you can translate it to C somehow.
export async function weightedRandomItem<T>(list: { weight: number; item: T }[]): Promise<T> {
const weightSum = sumBy(list, (item) => item.weight)
const randomIndex = await randomIntegerBetween(1, weightSum)
let currentIndex = 1
for (const listItem of list) {
if (randomIndex >= currentIndex && randomIndex < currentIndex + listItem.weight) {
return listItem.item
}
currentIndex += listItem.weight
}
throw new Error("No item selected. Impossible.")
}
where randomIntegerBetween(minInclusive: number, maxInclusive: number) returns a random integer from the specified range (min and max inclusive) from the RNG of your choice.
sumBy() is the lodash function in this case, and it should be self-explanatory.
As input you could pass in something like:
[{
weight: 10,
item: 1,
},
{
weight: 50,
item: 2,
},
{
weight: 30,
item: 3,
},
{
weight: 10,
item: 4,
}]
Then, the result would most probably be 2.