Random float number generation

后端 未结 14 1317
孤城傲影
孤城傲影 2020-11-22 05:05

How do I generate random floats in C++?

I thought I could take the integer rand and divide it by something, would that be adequate enough?

相关标签:
14条回答
  • 2020-11-22 05:34
    #include <cstdint>
    #include <cstdlib>
    #include <ctime>
    
    using namespace std;
    
    /* single precision float offers 24bit worth of linear distance from 1.0f to 0.0f */
    float getval() {
        /* rand() has min 16bit, but we need a 24bit random number. */
        uint_least32_t r = (rand() & 0xffff) + ((rand() & 0x00ff) << 16);
        /* 5.9604645E-8 is (1f - 0.99999994f), 0.99999994f is the first value less than 1f. */
        return (double)r * 5.9604645E-8;
    }
    
    int main()
    {
        srand(time(NULL));
    ...
    

    I couldn't post two answers, so here is the second solution. log2 random numbers, massive bias towards 0.0f but it's truly a random float 1.0f to 0.0f.

    #include <cstdint>
    #include <cstdlib>
    #include <ctime>
    
    using namespace std;
    
    float getval () {
        union UNION {
            uint32_t i;
            float f;
        } r;
        /* 3 because it's 0011, the first bit is the float's sign.
         * Clearing the second bit eliminates values > 1.0f.
         */
        r.i = (rand () & 0xffff) + ((rand () & 0x3fff) << 16);
        return r.f;
    }
    
    int main ()
    {
        srand (time (NULL));
    ...
    
    0 讨论(0)
  • 2020-11-22 05:35

    If you are using C++ and not C, then remember that in technical report 1 (TR1) and in the C++0x draft they have added facilities for a random number generator in the header file, I believe it is identical to the Boost.Random library and definitely more flexible and "modern" than the C library function, rand.

    This syntax offers the ability to choose a generator (like the mersenne twister mt19937) and then choose a distribution (normal, bernoulli, binomial etc.).

    Syntax is as follows (shameless borrowed from this site):

      #include <iostream>
      #include <random>
    
      ...
    
      std::tr1::mt19937 eng;  // a core engine class 
      std::tr1::normal_distribution<float> dist;     
    
      for (int i = 0; i < 10; ++i)        
          std::cout << dist(eng) << std::endl;
    
    0 讨论(0)
  • 2020-11-22 05:38

    In modern c++ you may use the <random> header that came with c++11.
    To get random float's you can use std::uniform_real_distribution<>.

    You can use a function to generate the numbers and if you don't want the numbers to be the same all the time, set the engine and distribution to be static.
    Example:

    float get_random()
    {
        static std::default_random_engine e;
        static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
        return dis(e);
    }
    

    It's ideal to place the float's in a container such as std::vector:

    int main()
    {
        std::vector<float> nums;
        for (int i{}; i != 5; ++i) // Generate 5 random floats
            nums.emplace_back(get_random());
    
        for (const auto& i : nums) std::cout << i << " ";
    }
    

    Example output:

    0.0518757 0.969106 0.0985112 0.0895674 0.895542
    
    0 讨论(0)
  • 2020-11-22 05:39

    C++11 gives you a lot of new options with random. The canonical paper on this topic would be N3551, Random Number Generation in C++11

    To see why using rand() can be problematic see the rand() Considered Harmful presentation material by Stephan T. Lavavej given during the GoingNative 2013 event. The slides are in the comments but here is a direct link.

    I also cover boost as well as using rand since legacy code may still require its support.

    The example below is distilled from the cppreference site and uses the std::mersenne_twister_engine engine and the std::uniform_real_distribution which generates numbers in the [0,10) interval, with other engines and distributions commented out (see it live):

    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <map>
    #include <random>
    
    int main()
    {
        std::random_device rd;
    
        //
        // Engines 
        //
        std::mt19937 e2(rd());
        //std::knuth_b e2(rd());
        //std::default_random_engine e2(rd()) ;
    
        //
        // Distribtuions
        //
        std::uniform_real_distribution<> dist(0, 10);
        //std::normal_distribution<> dist(2, 2);
        //std::student_t_distribution<> dist(5);
        //std::poisson_distribution<> dist(2);
        //std::extreme_value_distribution<> dist(0,2);
    
        std::map<int, int> hist;
        for (int n = 0; n < 10000; ++n) {
            ++hist[std::floor(dist(e2))];
        }
    
        for (auto p : hist) {
            std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                      << p.first << ' ' << std::string(p.second/200, '*') << '\n';
        }
    }
    

    output will be similar to the following:

    0 ****
    1 ****
    2 ****
    3 ****
    4 *****
    5 ****
    6 *****
    7 ****
    8 *****
    9 ****
    

    The output will vary depending on which distribution you choose, so if we decided to go with std::normal_distribution with a value of 2 for both mean and stddev e.g. dist(2, 2) instead the output would be similar to this (see it live):

    -6 
    -5 
    -4 
    -3 
    -2 **
    -1 ****
     0 *******
     1 *********
     2 *********
     3 *******
     4 ****
     5 **
     6 
     7 
     8 
     9 
    

    The following is a modified version of some of the code presented in N3551 (see it live) :

    #include <algorithm>
    #include <array>
    #include <iostream>
    #include <random>
    
    std::default_random_engine & global_urng( )
    {
        static std::default_random_engine u{};
        return u ;
    }
    
    void randomize( )
    {
        static std::random_device rd{};
        global_urng().seed( rd() );
    }
    
    int main( )
    {
      // Manufacture a deck of cards:
      using card = int;
      std::array<card,52> deck{};
      std::iota(deck.begin(), deck.end(), 0);
    
      randomize( ) ;  
    
      std::shuffle(deck.begin(), deck.end(), global_urng());
      // Display each card in the shuffled deck:
      auto suit = []( card c ) { return "SHDC"[c / 13]; };
      auto rank = []( card c ) { return "AKQJT98765432"[c % 13]; };
    
      for( card c : deck )
          std::cout << ' ' << rank(c) << suit(c);
    
       std::cout << std::endl;
    }
    

    Results will look similar to:

    5H 5S AS 9S 4D 6H TH 6D KH 2S QS 9H 8H 3D KC TD 7H 2D KS 3C TC 7D 4C QH QC QD JD AH JC AC KD 9D 5C 2H 4H 9C 8C JH 5D 4S 7C AD 3S 8S TS 2C 8D 3H 6C JS 7S 6S

    Boost

    Of course Boost.Random is always an option as well, here I am using boost::random::uniform_real_distribution:

    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <map>
    #include <boost/random/mersenne_twister.hpp>
    #include <boost/random/uniform_real_distribution.hpp>
    
    int main()
    {
        boost::random::mt19937 gen;
        boost::random::uniform_real_distribution<> dist(0, 10);
    
        std::map<int, int> hist;
        for (int n = 0; n < 10000; ++n) {
            ++hist[std::floor(dist(gen))];
        }
    
        for (auto p : hist) {
            std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                      << p.first << ' ' << std::string(p.second/200, '*') << '\n';
        }
    }
    

    rand()

    If you must use rand() then we can go to the C FAQ for a guides on How can I generate floating-point random numbers? , which basically gives an example similar to this for generating an on the interval [0,1):

    #include <stdlib.h>
    
    double randZeroToOne()
    {
        return rand() / (RAND_MAX + 1.);
    }
    

    and to generate a random number in the range from [M,N):

    double randMToN(double M, double N)
    {
        return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
    }
    
    0 讨论(0)
  • 2020-11-22 05:39

    Call the code with two float values, the code works in any range.

    float rand_FloatRange(float a, float b)
    {
        return ((b - a) * ((float)rand() / RAND_MAX)) + a;
    }
    
    0 讨论(0)
  • 2020-11-22 05:40

    drand48(3) is the POSIX standard way. GLibC also provides a reentrant version, drand48_r(3).

    The function was declared obsolete in SVID 3 but no adequate alternative was provided so IEEE Std 1003.1-2013 still includes it and has no notes that it's going anywhere anytime soon.

    In Windows, the standard way is CryptGenRandom().

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