How do I scale down numbers from rand()?

后端 未结 9 1892
小蘑菇
小蘑菇 2020-11-27 14:38

The following code outputs a random number each second:

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

    while (true)         


        
相关标签:
9条回答
  • 2020-11-27 15:00

    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;
    
    0 讨论(0)
  • 2020-11-27 15:03

    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;
    
    0 讨论(0)
  • 2020-11-27 15:06

    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.

    0 讨论(0)
  • 2020-11-27 15:09

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

    0 讨论(0)
  • 2020-11-27 15:12

    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 讨论(0)
  • 2020-11-27 15:18

    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.

    0 讨论(0)
提交回复
热议问题