如何在C++中生成随机浮点数?
我以为可以使用整数rand并除以一些值,这样是否足够呢?
rand()
函数可以在C++中生成伪随机数。结合RAND_MAX
和一些数学知识,您可以生成任意区间内的随机数。这对于学习目的和玩具程序是足够的。如果您需要带有正态分布的真正的随机数,则需要使用更高级的方法。
这将生成一个从0.0到1.0(包括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>
头文件。
rand()
生成随机浮点数的机制。这个问题和我的答案都专门聚焦于学习基础,而不关心高度精度。你必须先学会走,才能学会奔跑。 - John DiblingC++11给你带来了很多新的选择,包括random
。该主题的经典论文是N3551,《C++11中的随机数生成》
要了解为什么使用rand()
可能会有问题,请参阅Stephan T. Lavavej在GoingNative 2013活动期间提供的rand()被视为有害演示资料。幻灯片在评论中,这里是一个直接链接。
我还涵盖了boost
以及使用rand
,因为旧代码可能仍需要其支持。
#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
的值的std::normal_distribution来表示mean和stddev,例如dist(2, 2)
,则输出将类似于这个(查看实例):
-6
-5
-4
-3
-2 **
-1 ****
0 *******
1 *********
2 *********
3 *******
4 ****
5 **
6
7
8
9
#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 / (N-M) ) ) ;
}
randMToN
函数?你可以在函数中注明参数为[M,N]
,或者将之前的+1
加回到randZeroToOne
里。例如,在调用时输入randMToN(0.0, 1.0)
。 - BeyelerStudios(N-M)
处进行除零操作时要小心。在此处找到处理此错误的好方法:https://dev59.com/ulwY5IYBdhLWcg3wEEN2#50539103 - DrBeco看看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对象比构建新对象更好,但希望你能明白这个想法。
max
但可以使用开放式的 min
,则可以轻松地反转区间:return min + max - gen();
。 - Mark Ransom<random>
头文件。float
,您可以使用std::uniform_real_distribution<>
。static
。float get_random()
{
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(0, 1); // range [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
std::uniform_real_distribution<> dis(0, 1); // rage 0 - 1
is technically incorrect, 1.0 will never be generated, see http://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution To create a distribution over the closed interval [a,b], std::nextafter(b, std::numeric_limits<RealType>::max()) may be used as the second parameter.
- Troyseph使用两个 float
值调用代码,该代码适用于任何范围。
float rand_FloatRange(float a, float b)
{
return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}
fmaf()
(或C++中的浮点fma()
重载)的潜在用例,它可能会保留更多的精度。例如:fmaf((float)rand() / RAND_MAX, b - a, a)
。 - Tim Čas #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;
在某些系统上(比如使用VC编译的Windows),RAND_MAX
非常小,只有15位二进制数。因此,当你将随机数除以RAND_MAX
时,实际上只生成了15位的尾数,而不是可能的23位。这可能或可能不会对你造成问题,但在这种情况下你会错过一些值。
顺便提一下,我注意到已经有人针对这个问题发表了评论。以下是可能解决这个问题的一些代码:
float r = (float)((rand() << 15 + rand()) & ((1 << 24) - 1)) / (1 << 24);
drand48(3)
是POSIX标准的方法。GLibC还提供了可重入版本drand48_r(3)
。
该函数在SVID 3中被声明为过时,但没有提供足够的替代方案,因此IEEE Std 1003.1-2013仍然将其包含在内,并且没有注明它会很快消失。
在Windows中,标准方法是CryptGenRandom()。
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;
}
return (float)random23 / (1 << 23)
得到完全相同的分布。(是的,我刚刚测试过,修改了你的函数以接受random32
作为参数,并对从零到(1 << 23)-1
的所有值运行它。而且,你的方法确实给出与除以1 << 23
相同的结果。) - Ilmari Karonen迄今为止,我对所有答案都不满意,所以我编写了一个新的随机浮点数函数。该函数对浮点数据类型进行位假设。它仍需要一个至少有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));
}
rand()
,主要是出于历史考虑,但也意识到遗留应用程序确实存在。 - Shafik Yaghmour<random>
头文件来避免每次得到相同的数字。 - Andreas DM