Random number generator, C++
I know there is a bit of limitations for a random number generation in C+开发者_开发知识库+ (can be non-uniform). How can I generate a number from 1 to 14620?
Thank you.
If you've got a c++0x environment, a close derivative of the boost lib is now standard:
#include <random>
#include <iostream>
int main()
{
std::uniform_int_distribution<> d(1, 14620);
std::mt19937 gen;
std::cout << d(gen) << '\n';
}
This will be fast, easy and high quality.
You didn't specify, but if you wanted floating point instead just sub in:
std::uniform_real_distribution<> d(1, 14620);
And if you needed a non-uniform distribution, you can build your own piece-wise constant or piece-wise linear distribution very easily.
A common approach is to use std::rand()
with a modulo:
#include<cstdlib>
#include<ctime>
// ...
std::srand(std::time(0)); // needed once per program run
int r = std::rand() % 14620 + 1;
However, as @tenfour mentions in his answer, the modulo operator can disrupt the uniformity of values std::rand()
returns. This is because the modulo translates the values it discards into valid values, and this translation might not be uniform. For instance, for n
in [0, 10)
the value n % 9
translates 9
to 0
, so you can get zero by either a true zero or a 9 translated to zero. The other values have each only one chance to yield.
An alternative approach is to translate the random number from std::rand()
to a floating-point value in the range [0, 1)
and then translate and shift the value to within the range you desire.
int r = static_cast<double>(std::rand()) / RAND_MAX * 14620) + 1;
srand() / rand()
are the functions you need, as others have answered.
The problem with %
is that the result is decidedly non-uniform. To illustrate, imagine that rand()
returns a range of 0-3. Here are hypothetical results of calling it 4000 times:
0 - 1000 times
1 - 1000 times
2 - 1000 times
3 - 1000 times
Now if you do the same sampling for (rand() % 3)
, you notice that the results would be like:
0 - 2000 times
1 - 1000 times
2 - 1000 times
Ouch! The more uniform solution is this:
int n = (int)(((((double)std::rand()) / RAND_MAX) * 14620) + 1);
Sorry for the sloppy code, but the idea is to scale it down properly to the range you want using floating point math, and convert to integer.
Use rand.
( rand() % 100 ) is in the range 0 to 99
( rand() % 100 + 1 ) is in the range 1 to 100
( rand() % 30 + 1985 ) is in the range 1985 to 2014
( rand() % 14620 + 1 ) is in the range 1 to 14620
EDIT:
As mentioned in the link, the randomizer should be seeded using srand
before use. A common distinctive value to use is the result of a call to time
.
As already said, you can use rand(). E.g.
int n = rand() % 14620 + 1;
does the job, but it is non-uniform.
That means some values (low values) will occur slightly more frequently. This is because rand()
yields values in the range of 0 to RAND_MAX
and RAND_MAX
is generally not divisible by 14620. E.g. if RAND_MAX == 15000
, then the number 1 would be twice as likely as the number 1000 because rand() == 0
and rand() == 14620
both yield n==1
but only rand()==999
makes n==1000
true.
However, if 14620 is much smaller than RAND_MAX
, this effect is negligible. On my computer RAND_MAX
is equal to 2147483647. If rand()
yields uniform samples between 0 and RAND_MAX then, because 2147483647 % 14620 = 10327 and 2147483647 / 14620 = 146886, n
would be between 1 and 10328 on average 146887 times while the numbers between 10329 and 14620 would occur on average 146886 times if you draw 2147483647 samples.
Not much of a difference if you ask me.
However, if RAND_MAX == 15000
it would make a difference as explained above.
In this case some earlier posts suggested to use
int n = (int)(((((double)std::rand()) / RAND_MAX) * 14620) + 1);
to make it 'more uniform'.
Note that this only changes the numbers that occur more frequently since rand()
still returns 'only' RAND_MAX
distinct values.
To make it really uniform, you would have to reject any integer form rand()
if it is in the range between 14620*int(RAND_MAX/14620) and RAND_MAX and call rand()
again.
In the example with RAND_MAX == 15000
you would reject any values of rand()
between 14620 and 15000 and draw again.
For most application this is not necessary. I would worry more about the randomness of rand()
.
Here's a tutorial using the boost library http://www.boost.org/doc/libs/1_45_0/doc/html/boost_random/tutorial.html#boost_random.tutorial.generating_integers_in_a_range
The rand() function is not really the best Random generator, a better way would be by using CryptGenRandom().
This example should do do the trick:
#include <Windows.h>
// Random-Generator
HCRYPTPROV hProv;
INT Random() {
if (hProv == NULL) {
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_SILENT | CRYPT_VERIFYCONTEXT))
ExitProcess(EXIT_FAILURE);
}
int out;
CryptGenRandom(hProv, sizeof(out), (BYTE *)(&out));
return out & 0x7fffffff;
}
int main() {
int ri = Random() % 14620 + 1;
}
the modulus operator is the most important, you can apply a limit with this modulus, check this out:
// random numbers generation in C++ using builtin functions
#include <iostream>
using namespace std;
#include <iomanip>
using std::setw;
#include <cstdlib> // contains function prototype for rand
int main()
{
// loop 20 times
for ( int counter = 1; counter <= 20; counter++ ) {
// pick random number from 1 to 6 and output it
cout << setw( 10 ) << ( 1 + rand() % 6 );
// if counter divisible by 5, begin new line of output
if ( counter % 5 == 0 )
cout << endl;
}
return 0; // indicates successful termination
} // end main
精彩评论