随机浮点数生成

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

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

534710 次浏览

看看提振。随机。你可以这样做:

float gen_random_float(float min, float max)
{
boost::mt19937 rng;
boost::uniform_real<float> u(min, max);
boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > gen(rng, u);
return gen();
}

尝试一下,您可能会更好地传递相同的mt19937对象,而不是每次都构造一个新的对象,但希望您能理解。

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


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

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

这将生成一个从0.0到任意floatX的数字:

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

为了调用randsrand,你必须#include <cstdlib>

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

rand()返回一个介于0和RAND_MAX之间的int值。要获得0.0到1.0之间的随机数,首先将rand()返回的int转换为浮点数,然后除以RAND_MAX。

调用带有两个float值的代码,代码可在任何范围内工作。

float rand_FloatRange(float a, float b)
{
return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}

在某些系统上(目前想到的是带有VC的Windows), RAND_MAX小得可笑,即只有15位。当除以RAND_MAX时,你只会生成一个15位的尾数,而不是可能的23位。这对您来说可能是问题,也可能不是问题,但在这种情况下,您会遗漏一些值。

哦,刚才注意到已经有关于这个问题的注释了。不管怎样,这里有一些代码可以帮你解决这个问题:

float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);

未经测试,但可能工作:-)

到目前为止,我对任何答案都不满意,所以我写了一个新的随机浮点函数。它对浮点数据类型进行了按位假设。它仍然需要一个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));
}

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

此语法提供了选择生成器(如梅森素数捻线机 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;

在我看来,上面的答案确实给出了一些“随机”浮点数,但它们都不是真正的随机浮点数(即它们错过了浮点数表示的一部分)。在我进入我的实现之前,让我们先看看浮点数的ANSI/IEEE标准格式:

|符号(1位)| e(8位)| f(23位)|

这个词表示的数字是 (-1 *号)* 2^e * 1.f

注意“e”是一个有偏差的数字(偏差为127),因此范围从-127到126。最简单的(实际上也是最随机的)函数是将随机int类型的数据写入浮点数,因此

int tmp = rand();
float f = (float)*((float*)&tmp);

注意,如果执行float f = (float)rand();,它将把整数转换为浮点数(因此10将变成10.0) 所以现在如果你想限制最大值,你可以这样做(不确定是否有效)

int tmp = rand();
float f = *((float*)&tmp);
tmp = (unsigned int)f       // note float to int conversion!
tmp %= max_number;
f -= tmp;

,但是如果你看一下浮点数的结构,你会发现浮点数的最大值(大约)是2^127,这比int数的最大值(2^32)要大得多,因此排除了可以用浮点数表示的很大一部分数字。 这是我的最终实现:

/**
* Function generates a random float using the upper_bound float to determine
* the upper bound for the exponent and for the fractional part.
* @param min_exp sets the minimum number (closest to 0) to 1 * e^min_exp (min -127)
* @param max_exp sets the maximum number to 2 * e^max_exp (max 126)
* @param sign_flag if sign_flag = 0 the random number is always positive, if
*              sign_flag = 1 then the sign bit is random as well
* @return a random float
*/
float randf(int min_exp, int max_exp, char sign_flag) {
assert(min_exp <= max_exp);


int min_exp_mod = min_exp + 126;


int sign_mod = sign_flag + 1;
int frac_mod = (1 << 23);


int s = rand() % sign_mod;  // note x % 1 = 0
int e = (rand() % max_exp) + min_exp_mod;
int f = rand() % frac_mod;


int tmp = (s << 31) | (e << 23) | f;


float r = (float)*((float*)(&tmp));


/** uncomment if you want to see the structure of the float. */
//    printf("%x, %x, %x, %x, %f\n", (s << 31), (e << 23), f, tmp, r);


return r;
}

使用randf(0, 8, 0)函数将返回一个介于0.0到255.0之间的随机数

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

要了解为什么使用rand()会有问题,请参阅Stephan T. LavavejGoingNative 2013事件中给出的兰特()认为有害演示材料。幻灯片在评论中,但这里有一个直接的联系

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

下面的例子是从cppreference站点提炼出来的,使用了std:: mersenne_twister_engine引擎和std:: uniform_real_distribution,它们在[0,10)区间内生成数字,其他引擎和分布被注释掉(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';
}
}

输出将类似如下:

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

输出将取决于你选择的分布,所以如果我们决定使用std:: normal_distribution的意思是stddev的值都为2,例如dist(2, 2),输出将类似于下面(see it live):

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

以下是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;
}

结果将类似于:

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:::: 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 ()

如果你必须使用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) ) ) ;
}

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

drand48(3)是POSIX的标准方式。GLibC还提供了一个可重入版本drand48_r(3)

该函数在SVID 3中被声明为过时,但没有提供足够的替代方案,因此IEEE Std 1003.1-2013仍然包含它,并且没有说明它将很快消失。

在Windows中,标准方法是CryptGenRandom ()

如果你知道你的浮点数格式是IEEE 754(几乎所有现代cpu,包括Intel和ARM),那么你可以使用逐位方法从一个随机整数构建一个随机浮点数。只有当你无法访问c++ 11的randomBoost.Random时,才应该考虑这一点,这两个都要好得多。

float rand_float()
{
// returns a random value in the range [0.0-1.0)


// start with a bit pattern equating to 1.0
uint32_t pattern = 0x3f800000;


// get 23 bits of random integer
uint32_t random23 = 0x7fffff & (rand() << 8 ^ rand());


// replace the mantissa, resulting in a number [1.0-2.0)
pattern |= random23;


// convert from int to float without undefined behavior
assert(sizeof(float) == sizeof(uint32_t));
char buffer[sizeof(float)];
memcpy(buffer, &pattern, sizeof(float));
float f;
memcpy(&f, buffer, sizeof(float));


return f - 1.0;
}

这将比使用除法得到更好的分布。

在现代的c++中,你可以使用c++11附带的<random>头文件 要获得随机的float,可以使用std::uniform_real_distribution<>.

你可以使用一个函数来生成数字,如果你总是不希望数字相同,将引擎和分布设置为static 例子:< / p >
float get_random()
{
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
return dis(e);
}

float放在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 << " ";
}

示例输出:

0.0518757 0.969106 0.0985112 0.0895674 0.895542
#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));
...