Generating random integer from a range

前端 未结 13 1514
失恋的感觉
失恋的感觉 2020-11-22 03:12

I need a function which would generate a random integer in given range (including border values). I don\'t unreasonable quality/randomness requirements, I have four requirem

相关标签:
13条回答
  • 2020-11-22 03:58

    In this thread rejection sampling was already discussed, but I wanted to suggest one optimization based on the fact that rand() % 2^something does not introduce any bias as already mentioned above.

    The algorithm is really simple:

    • calculate the smallest power of 2 greater than the interval length
    • randomize one number in that "new" interval
    • return that number if it is less than the length of the original interval
      • reject otherwise

    Here's my sample code:

    int randInInterval(int min, int max) {
        int intervalLen = max - min + 1;
        //now calculate the smallest power of 2 that is >= than `intervalLen`
        int ceilingPowerOf2 = pow(2, ceil(log2(intervalLen)));
    
        int randomNumber = rand() % ceilingPowerOf2; //this is "as uniform as rand()"
    
        if (randomNumber < intervalLen)
            return min + randomNumber;      //ok!
        return randInInterval(min, max);    //reject sample and try again
    } 
    

    This works well especially for small intervals, because the power of 2 will be "nearer" to the real interval length, and so the number of misses will be smaller.

    PS
    Obviously avoiding the recursion would be more efficient (no need to calculate over and over the log ceiling..) but I thought it was more readable for this example.

    0 讨论(0)
  • 2020-11-22 03:59

    If your compiler supports C++0x and using it is an option for you, then the new standard <random> header is likely to meet your needs. It has a high quality uniform_int_distribution which will accept minimum and maximum bounds (inclusive as you need), and you can choose among various random number generators to plug into that distribution.

    Here is code that generates a million random ints uniformly distributed in [-57, 365]. I've used the new std <chrono> facilities to time it as you mentioned performance is a major concern for you.

    #include <iostream>
    #include <random>
    #include <chrono>
    
    int main()
    {
        typedef std::chrono::high_resolution_clock Clock;
        typedef std::chrono::duration<double> sec;
        Clock::time_point t0 = Clock::now();
        const int N = 10000000;
        typedef std::minstd_rand G;
        G g;
        typedef std::uniform_int_distribution<> D;
        D d(-57, 365);
        int c = 0;
        for (int i = 0; i < N; ++i) 
            c += d(g);
        Clock::time_point t1 = Clock::now();
        std::cout << N/sec(t1-t0).count() << " random numbers per second.\n";
        return c;
    }
    

    For me (2.8 GHz Intel Core i5) this prints out:

    2.10268e+07 random numbers per second.

    You can seed the generator by passing in an int to its constructor:

        G g(seed);
    

    If you later find that int doesn't cover the range you need for your distribution, this can be remedied by changing the uniform_int_distribution like so (e.g. to long long):

        typedef std::uniform_int_distribution<long long> D;
    

    If you later find that the minstd_rand isn't a high enough quality generator, that can also easily be swapped out. E.g.:

        typedef std::mt19937 G;  // Now using mersenne_twister_engine
    

    Having separate control over the random number generator, and the random distribution can be quite liberating.

    I've also computed (not shown) the first 4 "moments" of this distribution (using minstd_rand) and compared them to the theoretical values in an attempt to quantify the quality of the distribution:

    min = -57
    max = 365
    mean = 154.131
    x_mean = 154
    var = 14931.9
    x_var = 14910.7
    skew = -0.00197375
    x_skew = 0
    kurtosis = -1.20129
    x_kurtosis = -1.20001
    

    (The x_ prefix refers to "expected")

    0 讨论(0)
  • 2020-11-22 04:01

    How about the Mersenne Twister? The boost implementation is rather easy to use and is well tested in many real-world applications. I've used it myself in several academic projects such as artificial intelligence and evolutionary algorithms.

    Here's their example where they make a simple function to roll a six-sided die:

    #include <boost/random/mersenne_twister.hpp>
    #include <boost/random/uniform_int.hpp>
    #include <boost/random/variate_generator.hpp>
    
    boost::mt19937 gen;
    
    int roll_die() {
        boost::uniform_int<> dist(1, 6);
        boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist);
        return die();
    }
    

    Oh, and here's some more pimping of this generator just in case you aren't convinced you should use it over the vastly inferior rand():

    The Mersenne Twister is a "random number" generator invented by Makoto Matsumoto and Takuji Nishimura; their website includes numerous implementations of the algorithm.

    Essentially, the Mersenne Twister is a very large linear-feedback shift register. The algorithm operates on a 19,937 bit seed, stored in an 624-element array of 32-bit unsigned integers. The value 2^19937-1 is a Mersenne prime; the technique for manipulating the seed is based on an older "twisting" algorithm -- hence the name "Mersenne Twister".

    An appealing aspect of the Mersenne Twister is its use of binary operations -- as opposed to time-consuming multiplication -- for generating numbers. The algorithm also has a very long period, and good granularity. It is both fast and effective for non-cryptographic applications.

    0 讨论(0)
  • 2020-11-22 04:02

    The simplest (and hence best) C++ (using the 2011 standard) answer is

    #include <random>
    
    std::random_device rd;     // only used once to initialise (seed) engine
    std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case)
    std::uniform_int_distribution<int> uni(min,max); // guaranteed unbiased
    
    auto random_integer = uni(rng);
    

    No need to re-invent the wheel. No need to worry about bias. No need to worry about using time as random seed.

    0 讨论(0)
  • 2020-11-22 04:02

    The formula for this is very simple, so try this expression,

     int num = (int) rand() % (max - min) + min;  
     //Where rand() returns a random number between 0.0 and 1.0
    
    0 讨论(0)
  • 2020-11-22 04:02

    The following expression should be unbiased if I am not mistaken:

    std::floor( ( max - min + 1.0 ) * rand() ) + min;
    

    I am assuming here that rand() gives you a random value in the range between 0.0 and 1.0 NOT including 1.0 and that max and min are integers with the condition that min < max.

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