如何在c++中生成随机浮点数?

我想我可以用整数rand除以某个数,这足够了吗?


当前回答

到目前为止,我对任何答案都不满意,所以我写了一个新的随机浮点函数。它对浮点数据类型进行了按位假设。它仍然需要一个rand()函数,至少有15个随机位。

//Returns a random number in the range [0.0f, 1.0f).  Every
//bit of the mantissa is randomized.
float rnd(void){
  //Generate a random number in the range [0.5f, 1.0f).
  unsigned int ret = 0x3F000000 | (0x7FFFFF & ((rand() << 8) ^ rand()));
  unsigned short coinFlips;

  //If the coin is tails, return the number, otherwise
  //divide the random number by two by decrementing the
  //exponent and keep going. The exponent starts at 63.
  //Each loop represents 15 random bits, a.k.a. 'coin flips'.
  #define RND_INNER_LOOP() \
    if( coinFlips & 1 ) break; \
    coinFlips >>= 1; \
    ret -= 0x800000
  for(;;){
    coinFlips = rand();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    //At this point, the exponent is 60, 45, 30, 15, or 0.
    //If the exponent is 0, then the number equals 0.0f.
    if( ! (ret & 0x3F800000) ) return 0.0f;
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
    RND_INNER_LOOP(); RND_INNER_LOOP(); RND_INNER_LOOP();
  }
  return *((float *)(&ret));
}

其他回答

rand()可用于在c++中生成伪随机数。结合RAND_MAX和一点数学运算,您可以在任意选择的间隔内生成随机数。这对于学习目的和玩具程序来说是足够的。如果需要真正具有正态分布的随机数,则需要使用更高级的方法。


这将生成一个从0.0到1.0的数字。

float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);

这将生成一个从0.0到任意浮点数X的数字:

float r2 = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/X));

这将生成一个从任意LO到任意HI的数字:

float r3 = LO + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(HI-LO)));

注意,如果需要真正的随机数,rand()函数通常是不够的。


在调用rand()之前,必须首先通过调用srand()来“播种”随机数生成器。这应该在程序运行期间执行一次,而不是在每次调用rand()时执行一次。通常是这样做的:

srand (static_cast <unsigned> (time(0)));

为了调用rand或srand,你必须#include <cstdlib>。

为了调用time,你必须#include <ctime>。

c++ 11提供了很多随机的新选项。关于这个主题的权威论文是N3551, c++ 11中的随机数生成

要了解为什么使用rand()会有问题,请参阅Stephan T. Lavavej在2013年GoingNative活动期间所做的rand()被认为是有害的演示材料。幻灯片在评论中,这里有一个直接链接。

我还介绍了boost以及使用rand,因为遗留代码可能仍然需要它的支持。

下面的例子是从cppreference站点提取出来的,它使用了std::mersenne_twister_engine引擎和std::uniform_real_distribution引擎,后者在[0,10]区间内生成数字,其他引擎和分布被注释掉了(见现场):

#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';
    }
}

输出将类似如下:

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

输出将根据你选择的分布而变化,所以如果我们决定使用std::normal_distribution,将mean和stddev的值都设为2,例如dist(2,2),而不是输出将类似于此(参见现场):

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

以下是N3551中一些代码的修改版本(见现场):

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

结果将类似于:

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。随机总是一个选项,这里我使用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(),那么我们可以去C常见问题解答关于如何生成浮点随机数的指南?,它基本上给出了一个类似于在区间[0,1)上生成一个的例子:

#include <stdlib.h>

double randZeroToOne()
{
    return rand() / (RAND_MAX + 1.);
}

并生成一个范围为[M,N)的随机数:

double randMToN(double M, double N)
{
    return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
}
#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));
...

我不能给出两个答案,所以这里是第二个答案。Log2随机数,很大程度上偏向于0.0f但它实际上是一个随机浮动1.0f到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));
...

如果您使用的是c++而不是C,那么请记住,在技术报告1 (TR1)和c++ 0x草案中,他们在头文件中添加了用于随机数生成器的功能,我相信它与Boost是相同的。随机库和绝对更灵活和“现代”比C库函数,兰德。

该语法提供了选择生成器(如mersenne twister mt19937)然后选择分布(正态分布、伯努利分布、二项式分布等)的能力。

语法如下(无耻地借用本网站):

  #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;

对于c++,它可以在dist变量指定的范围内生成实浮点数

#include <random>  //If it doesnt work then use   #include <tr1/random>
#include <iostream>

using namespace std;

typedef std::tr1::ranlux64_base_01 Myeng; 
typedef std::tr1::normal_distribution<double> Mydist;

int main() { 
       Myeng eng; 
       eng.seed((unsigned int) time(NULL)); //initializing generator to January 1, 1970);
       Mydist dist(1,10); 

       dist.reset(); // discard any cached values 
       for (int i = 0; i < 10; i++)
       {
           std::cout << "a random value == " << (int)dist(eng) << std::endl; 
       }

       return (0);
}