问题
The problem is that I need to generate a random integer between 0 and 999 (for investigation of a mathematical conjecture). All of the values need to have the same probability of coming up.
I have tried rand()
, but with RAND_MAX
being 32767 (on my compiler) this means that just taking rand() % 1000
leads to the first 1–767 being significantly more likely to come up (and that's assuming that all possibilities have the same probability in rand()
in the first place).
I'm using Windows so /dev/random
isn't an option.
回答1:
You can do something like this using uniform_int_distribution with C++11:
#include <iostream>
#include <random>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 999);
for (int n=0; n<1000; ++n)
std::cout << dis(gen) << ' ';
std::cout << '\n';
}
回答2:
Your modulus observation is correct, and one of several reasons that rand()
doesn't hold up to mathematical scrutiny. From the notes here:
There are no guarantees as to the quality of the random sequence produced. In the past, some implementations of rand() have had serious shortcomings in the randomness, distribution and period of the sequence produced (in one well-known example, the low-order bit simply alternated between 1 and 0 between calls). rand() is not recommended for serious random-number generation needs, like cryptography.
C++11 introduces several new random number generators that hold to stricter standards that would likely be suitable for your purposes..
If you can sacrifice more than a few bytes of overhead (it's safe to assume that you can), I recommend std::mersenne_twister_engine
回答3:
I think the easiest way to do this is to just throw away the numbers in the interval [32000, 32767]
, and only apply the % 1000
to the remaining numbers. This should get you a much more even distribution.
Alternately you could use boost's random/uniform distribution components (or from C++11 if that's available) as these will provide a more sound PRNG than rand
.
回答4:
There is no such thing as "truly random" in computers. I also don't beleive there is any significantly higher chance of 1-767 (or technically, 0-767, in that case) than any other number. However, if you need "better" random numbers, and then C++11 has support for using Mersenne Twister, which is a higher grade random number generator.
Some more information here: http://www.cplusplus.com/reference/random/mt19937/
回答5:
the problem is that rand() gives you uniformly distributed variable on domain [0,RAND_MAX]
with RAND_MAX
being most likely 32767. You cannot map this domain into larger domain by a simple multiplication
u=(double)rand();
d=(double)RAND_MAX;
double div= u/d;
double res=div*interval_range;
because this would be correct only if RAND_MAX was an even multiple of interval_range
. however you will not have all values in your larger domain then. But if your new, desired domain is smaller that RAND_MAX
as in your case, you can truncate uniform distribution generated by rand()
to your desired domain (what essentialy means reject rand()
values greater than your desired domain). The truncated uniform distribution is still uniform, so you will have new uniformly distributed variable on your new domain (this will be conditional distribution more precisely). Statistical example:
so truncated uniform distribution will have another "moments", parameters that describe it (mean, std_dev, variance, etc) but will be uniform again.
Example code:
int main{
int o=RAND_MAX;
std::map<int,int> m1;
int min=0,max=999;
for (int i=0; i<1000*9994240; ++i){//9994240=305*32768 32768=RAND_MAX+1
int r=rand();
if(r<=max){
m1[r]++;
}
}
for (auto & i : m1)
std::cout << i.first << " : " << i.second << '\n';
}
result: 0 : 42637 1 : 42716 2 : 42590 3 : 42993 4 : 42936 5 : 42965 6 : 42941 7 : 42705 8 : 42944 9 : 42707 10 : 42860 11 : 43012 12 : 42793 //... 995 : 42861 996 : 42911 997 : 42865 998 : 42877 999 : 43159
you can achieve desired result on any domain this way:
#include <iostream>
#include <random>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 1000);
for (int n=0; n<1000; ++n)
std::cout << dis(gen) << ' ';
std::cout << '\n';
}
however you should really use boost in this case:
#include <iostream>
#include "boost/random.hpp"
#include "boost/generator_iterator.hpp"
using namespace std;
int main() {
typedef boost::mt19937 RNGType;
RNGType rng;
boost::uniform_int<> zero_to_n( 0, 999 );
boost::variate_generator< RNGType, boost::uniform_int<> >
dice(rng, zero_to_n);
int n = dice();
}
回答6:
Get a random number using
rand()
.Divide it with
RAND_MAX
. You will get a floating point number between 0 and 1.Multiply this number with 1000.
来源:https://stackoverflow.com/questions/16153589/generating-a-uniform-random-integer-in-c