简体   繁体   中英

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() . Eg

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. Eg 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() .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM