如何在C ++中生成随机浮点数?
我以为我可以拿整数rand并将其除以某种东西,这足够了吗?
答案 0 :(得分:344)
rand()
可用于在C ++中生成伪随机数。结合RAND_MAX
和一点点数学,您可以在您选择的任意间隔中生成随机数。这足以用于学习目的和玩具程序。如果您需要具有正态分布的真正的随机数,则需要采用更高级的方法。
这将生成一个从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>
。
答案 1 :(得分:116)
C ++ 11为random
提供了许多新选项。关于这个主题的规范性论文将是N3551, Random Number Generation in C++11
要了解使用rand()
可能有问题的原因,请参阅 GoingNative 2013 活动期间提供的 Stephan T. Lavavej 的rand() Considered Harmful演示材料。幻灯片位于评论中,但这里是direct link。
我还介绍了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 ****
输出会根据您选择的分配而有所不同,因此,如果我们决定使用值为2
的{{3}}同时使用 mean 和 stddev 例如dist(2, 2)
而输出与此类似( std::normal_distribution ):
-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
升压
当然see it live也是一个选项,我在这里使用Boost.Random:
#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常见问题获取boost::random::uniform_real_distribution上的指南,其中基本上提供了一个类似于此的示例,用于生成间隔[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) ) ) ;
}
答案 2 :(得分:60)
看看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();
}
玩转,你可能会更好地传递相同的mt19937对象而不是每次构建一个新对象,但希望你能得到这个想法。
答案 3 :(得分:23)
使用两个float
值调用代码,代码适用于任何范围。
float rand_FloatRange(float a, float b)
{
return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}
答案 4 :(得分:19)
如果您使用的是C ++而不是C,那么请记住,在技术报告1(TR1)和C ++ 0x草案中,他们为头文件中的随机数生成器添加了设施,我相信它与Boost.Random库,肯定比C库函数更灵活和“现代”,rand。
此语法提供了选择生成器的能力(如mersenne twister mt19937),然后选择分布(普通,bernoulli,二项式等)。
语法如下(从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;
答案 5 :(得分:19)
在现代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
答案 6 :(得分:5)
在某些系统上(当前,我想到了VC的Windows),RAND_MAX
非常小,i。即只有15位。除以RAND_MAX
时,您只生成15位的尾数,而不是23位可能的位。这对你来说可能是一个问题,也可能不是问题,但在这种情况下你错过了一些值。
哦,只是注意到已经有人对此问题发表评论。无论如何,这里有一些代码可以解决这个问题:
float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);
未经测试,但可能有效: - )
答案 7 :(得分:4)
drand48(3)
是POSIX标准方式。 GLibC还提供可重入版本drand48_r(3)
。
该功能在SVID 3中被宣布为过时,但没有提供足够的替代方案,因此IEEE Std 1003.1-2013仍然包含该功能,并且没有任何记录表明它很快就会到达任何地方。
在Windows中,标准方式是CryptGenRandom()。
答案 8 :(得分:2)
到目前为止我对任何答案都不满意所以我写了一个新的随机浮动函数。它对浮点数据类型做出逐位假设。它仍然需要一个至少有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, 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));
}
答案 9 :(得分:2)
在我看来,上面的答案确实给出了一些'随机'浮点数,但它们都不是真正的随机浮点数(即它们错过了浮点表示的一部分)。在我急于实现之前,让我们首先看一下浮点数的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之间的随机数
答案 10 :(得分:2)
如果您知道您的浮点格式为IEEE 754(几乎所有现代CPU,包括Intel和ARM),那么您可以使用逐位方法从随机整数构建随机浮点数。只有在您无权访问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;
}
这比使用师的分配更好。
答案 11 :(得分:1)
对于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);
}
答案 12 :(得分:0)
rand()返回0到RAND_MAX之间的int。要获得介于0.0和1.0之间的随机数,首先将rand()的int返回值转换为float,然后除以RAND_MAX。
答案 13 :(得分:0)
#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));
...