开发者

How do I scale down numbers from rand()?

The following code outputs a random number each second:

int main ()
{
    srand(time(NULL)); // Seeds number generator with execution time.

    while (true)
    {
        int rawRand = rand();

        std::cout << rawRand << std::endl;

        sleep(1);
 开发者_C百科   }
}

How might I size these numbers down so they're always in the range of 0-100?


If you are using C++ and are concerned about good distribution you can use TR1 C++11 <random>.

#include <random>

std::random_device rseed;
std::mt19937 rgen(rseed()); // mersenne_twister
std::uniform_int_distribution<int> idist(0,100); // [0,100]

std::cout << idist(rgen) << std::endl;


All the examples posted so far actually give badly distributed results. Execute the code often and create a statistic to see how the values become skewed.

A better way to generate a real uniform random number distribution in any range [0, N] is the following (assuming that rand actually follows a uniform distribution, which is far from obvious):

unsigned result;
do {
    result = rand();
} while (result > N);

Of course, that method is slow but it does produce a good distribution. A slightly smarter way of doing this is to find the largest multiple of N that is smaller than RAND_MAX and using that as the upper bound. After that, one can safely take the result % (N + 1).

For an explanation why the naive modulus method is bad and why the above is better, refer to Julienne’s excellent article on using rand.


int rawRand = rand() % 101;

See (for more details):

rand - C++ Reference

Others have also pointed out that this is not going to give you the best distribution of random numbers possible. If that kind of thing is important in your code, you would have to do:

int rawRand = (rand() * 1.0 / RAND_MAX) * 100;

EDIT

Three years on, I'm making an edit. As others mentioned, rand() has a great deal of issues. Obviously, I can't recommend its use when there are better alternatives going forward. You can read all about the details and recommendations here:

rand() Considered Harmful | GoingNative 2013


You can do

cout << rawRand % 100 << endl; // Outputs between 0 and 99

cout << rawRand % 101 << endl; // outputs between 0 and 100

For the people downvoting; note one minute after this was originally posted I left the comment:

From http://www.cplusplus.com/reference/clibrary/cstdlib/rand "Notice though that this modulo operation does not generate a truly uniformly distributed random number in the span (since in most cases lower numbers are slightly more likely), but it is generally a good approximation for short spans."

With 64-bit ints and using 100 numbers as output, the numbers 0-16 are represented with 1.00000000000000000455 % of the numbers (an relative accuracy to identically distributed of 1% by about 10-18), while the numbers 17-99 are represented with 0.99999999999999999913 % of the numbers. Yes, not perfectly distributed, but a very good approximation for small spans.

Also note, where does the OP ask for identically distributed numbers? For all we know these are being used for purposes where a small deviations doesn't matter (e.g., anything other than cryptography -- and if they are using the numbers for cryptography this question is much too naive for them to be writing their own cryptography).

EDIT - For people who are truly concerned with having a uniform distribution of random numbers the following code works. Note this isn't necessarily optimal as with 64-bit random ints, it will require two calls of rand() once every 10^18 calls.

unsigned N = 100; // want numbers 0-99
unsigned long randTruncation = (RAND_MAX / N) * N; 
// include every number the N times by ensuring rawRand is between 0 and randTruncation - 1 or regenerate.
unsigned long rawRand = rand();

while (rawRand >= randTruncation) {
    rawRand = rand();  
// with 64-bit int and range of 0-99 will need to generate two random numbers
// about 1 in every (2^63)/16 ~ 10^18 times (1 million million times)

// with 32-bit int and range of 0-99 will need to generate two random numbers 
// once every 46 million times.

}
cout << rawRand % N << stdl::endl;


See man 3 rand -- you need to scale by dividing by RAND_MAX to obtain the range [0, 1] after which you can multiply by 100 for your target range.


For the range min to max (inclusive), use: int result = rand() % (max - min + 1) + min;


How long an answer would you like.

the simplest is to convert using the remainder when divided by 101:

int value = rawRand % 101;

A semipurist would rescale using doubles:

double dbl = 100 * ((double)rawRand / RAND_MAX);
int ivalue = (int)(dbl + 0.5);   // round up for above 0.5

And a purist would say that rand does not produce random numbers.

For your info, the quality of random numbers is measured by taking a sequence of numbers and then calculating the mathematical probability that the source of that sequence was random. The simple hack using the remainder is a very poor choice if you are after randomness.


rawRand % 101 would give [0-100], inclusive.


Some people have posted the following code as an example:

int rawRand = (rand() / RAND_MAX) * 100;

This is an invalid way of solving the problem, as both rand() and RAND_MAX are integers. In C++, this results in integral division, which will truncate the results decimal points. As RAND_MAX >= rand(), the result of that operation is either 1 or 0, meaning rawRand can be only 0 or 100. A correct way of doing this would be the following:

int rawRand = (rand() / static_cast<double>(RAND_MAX)) * 100;

Since one the operands is now a double, floating point division is used, which would return a proper value between 0 and 1.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜