Maxpm
Maxpm

Reputation: 25551

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);
    }
}

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

Upvotes: 39

Views: 22682

Answers (9)

Konrad Rudolph
Konrad Rudolph

Reputation: 545518

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.

Upvotes: 32

dr jimbob
dr jimbob

Reputation: 17721

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;

Upvotes: 6

Blastfurnace
Blastfurnace

Reputation: 18652

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;

Upvotes: 85

Justin Niessner
Justin Niessner

Reputation: 245399

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

Upvotes: 27

tyree731
tyree731

Reputation: 483

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.

Upvotes: -6

Michael Shaw
Michael Shaw

Reputation: 644

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.

Upvotes: -5

Jake Petroules
Jake Petroules

Reputation: 24140

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

Upvotes: -2

Costique
Costique

Reputation: 23722

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

Upvotes: -6

Dirk is no longer here
Dirk is no longer here

Reputation: 368191

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.

Upvotes: 3

Related Questions