C ++随机浮点数生成
如何在C ++中生成随机的花车?
我想我可以把整数兰德分成几个等级,这足够吗?
rand()
可用于在C ++中生成伪随机数。 结合RAND_MAX
和一些小math运算,您可以在您select的任意时间间隔内生成随机数。 这对于学习目的和玩具程序已经足够了。 如果你需要正态分布的真正的随机数,你需要采用更高级的方法。
这会生成一个从0.0到1.0的数字。
float r = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
这将产生一个从0.0到一些任意的float
, 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为你提供了很多random
的新选项。 关于这个主题的规范文件是N3551,C ++ 11中的随机数生成
要了解为什么使用rand()
可能会有问题,请参阅在2013年GoingNative活动期间由Stephan T. Lavavej提供的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 ****
输出将根据您select的分布而有所不同,所以如果我们决定使用均值和标准偏差 dist(2, 2)
例如dist(2, 2)
的值std :: normal_distribution ,则输出将类似于此( 请参阅住 ):
-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.Random总是一个选项,在这里我使用了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()
如果你必须使用rand()
那么我们可以去C FAQ的一个指南如何生成浮点随机数? ,它基本上给出了一个类似于这样的例子来生成一个区间[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 / (NM) ) ) ; }
看看Boost.Random 。 你可以做这样的事情:
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(); }
玩耍,你可能会更好地传递相同的物体,而不是每次都构build一个新的物体,但希望你能明白这一点。
用两个float
值调用代码,代码可以在任何范围内工作。
float rand_FloatRange(float a, float b) { return ((b - a) * ((float)rand() / RAND_MAX)) + a; }
如果您使用的是C ++而不是C,那么请记住,在技术报告1(TR1)和C ++ 0x草稿中,他们在头文件中为随机数生成器添加了设施,我相信它与Boost相同。随机库,肯定比C库函数rand更灵活,更“现代”。
这个语法提供了select一个生成器的能力(如mersenne twister mt19937),然后select一个分布(正常,伯努利,二项式等)。
语法如下(无耻借用本网站 ):
#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++
您可以使用随c++11
附带的<random>
头文件。
要获得随机的float
,你可以使用std::uniform_real_distribution<>
。
您可以使用函数来生成数字,如果您不希望数字始终保持不变 ,请将引擎和分布设置为static
。
例:
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
在一些系统上(当前,有VC的是Windows), RAND_MAX
是非常小的, 即 只有15位。 除以RAND_MAX
您只会生成15位的尾数,而不是23位可能的位。 这对你来说可能是也可能不是问题,但是在这种情况下你错过了一些值。
哦,只是注意到,这个问题已经有了一个评论。 无论如何,这里有一些代码可以为你解决这个问题:
float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);
未经testing,但可能工作:-)
drand48(3)
是POSIX的标准方式。 GLibC还提供了一个可重入版本drand48_r(3)
。
该函数在SVID 3中被宣布为废弃,但没有提供足够的替代方法,因此IEEE Std 1003.1-2013仍然包含它,并且没有注意到它即将在任何地方发生。
在Windows中,标准的方法是CryptGenRandom() 。
到目前为止,我还没有满意答案,所以我写了一个新的随机浮点函数。 它会对float数据types进行按位假设。 它仍然需要一个至less有15个随机位的rand()函数。
//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, aka '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)); }
如果你知道你的浮点格式是IEEE 754 (几乎所有现代的CPU包括Intel和ARM),那么你可以使用按位方法从随机整数中随机构build一个浮点数。 如果你不能访问C ++ 11的random
或Boost.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; }
这会比使用分区的分配更好。
在我看来,上面的答案确实给出了一些“随机”的浮动,但没有一个是真正的随机浮动(即他们错过了浮动表示的一部分)。 在我匆忙进入我的实现之前,先让我们看一下ANSI / IEEE标准格式的浮点数:
|符号(1位)| e(8位)| f(23位)|
这个词表示的数字是(-1 *符号)* 2 ^ e * 1.f
注意“e”数字是有偏差的(偏差为127),因此范围从-127到126.最简单的(也是最随机的)函数只是将随机int的数据写入一个float,从而
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 ++,它可以在由dist
variables指定的范围内生成实际的浮点数
#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); }
rand()在0和RAND_MAX之间返回一个int值。 要得到一个介于0.0和1.0之间的随机数,首先将rand返回的int返回值转换为一个float值,然后除以RAND_MAX。
完全随机的有效浮点数用以下方式产生:随机符号,随机指数和随机尾数。 这里是一个从0..MAXFLOAT生成随机数的例子,
static float frand(){ float f; UINT32 *fi = (UINT32*)&f; *fi = 0; const int minBitsRandGives = (1<<15); // RAND_MAX is at least (1<<15) UINT32 randExp = (rand()%254)+1; // Exponents are in range of [1..254] UINT32 randMantissa = ((rand() % minBitsRandGives) << 8) | (rand()%256); *fi = randMantissa | (randExp<<23); // Build a float with random exponent and random mantissa return f; }
重要说明: RAND_MAX默认等于2 ^ 16(在32位系统上),所以rand()最多可以产生15个随机位。 由于浮点总共有32位,我们必须激活rand()至less3次才能产生随机的32位。 我使用8位rand()来产生指数,另外2个调用rand()来产生23位尾数。
避免常见的错误:如果使用(float)rand()/MAX_RAND
获取范围[0..1]内的浮点数,则仍会得到均匀分布但精度较低的随机数。 例如,您的随机生成器可以生成0.00001和0.00002,但不能生成0.000017。 这样的随机比实际的浮点表示精度低256倍。
优化:我的function没有针对速度进行优化。 您可以通过按位逻辑运算replace“%”除法来改善它。 例如,而不是%256
使用&0xFF