c++中常用的随机函数
1.低性能随机函数
详见博客:https://blog.youkuaiyun.com/sinat_41909065/article/details/82960361
低性能的随机函数rand()是基于LCG算法实现的,LCG随机数产生器算法可以描述为:X(n+1)=(a*X(n)+c)%m,其中种子就是X(0)的值,当a、c、m固定的情况下改变X(0)就可以改变随机序列,srand()函数就是起到这个作用,time()函数则是辅助srand()函数产生种子的。相关的函数和作用如下表所示。
函数名称 | 作用 |
---|---|
int rand(void) | 返回0~RAND_MAX(32767)的整数,伪随机函数,返回值随种子变化而变化,默认种子为1,种子不变产生的随机序列不变 |
void srand(unsigned int seed) | 设置rand()函数的种子,参数不同设置的种子不同 |
time(NULL) | 函数会返回1970年1月1日至今所经历的时间(以秒为单位),需要引入头文件<time.h>,用时间值做种子,就可以产生随机数了,因为时间总是在变的 |
2.高性能随机函数
相较于古老的rand()函数,c++11提供的mt19937随机数生成器具有更好的性能,其是基于Mersenne Twister算法实现的,有兴趣的可以自行了解,这里仅介绍mt19937随机数生成器的用法。
先给出官方文档中给出的例子:
#include <iostream>
#include <random>
using namespace std;
int main()
{
mt19937 mt_rand(time(0)); //创建mt19937对象,并利用time(0)作为随时间变化的种子
for(int i=0;i<10;i++)
cout << mt_rand() << endl; //输出随机数的值,由于time(0)随时间变化,因此每次的运行结果产生的随机数都不同。
return 0;
}
如果想要输出特定范围内的随机数,可以用以下方法:
mt19937::result_type seed = time(0);
auto dice_rand = std::bind(std::uniform_int_distribution<int>(1,6),mt19937(seed));
//uniform_int_distribution<int>(1,6):根据[1,6]区间内的均匀分布产生随机数
mt19937::result_type seed = time(0);
auto real_rand = std::bind(std::uniform_real_distribution<double>(0,1),mt19937(seed));
//uniform_real_distribution<double>(0,1):根据[0,1)区间内的均匀分布产生随机数
在第一个例子中dice_rand()将输出一个[1,6]范围内的整数,在第二个例子中real_rand将会输出一个[0,1)范围内的double类型的数。需要注意的是std::bind包含在头文件#include 中。
且uniform_int_distribution和uniform_double_distribution均可改成其他的概率分布函数。也可单独使用这些分布,具体看后面的例子。
此外当你不想使用time(0)来设置种子时,可以用如下方法来设置种子,需要注意的是chrono包含于头文件中,因此使用时需要加上#include 。
//用流逝的时间(秒)得到一个种子
auto seed = chrono::high_resolution_clock::now().time_since_epoch().count();
mt19937 mt_rand(seed);
综合例子:
#include <iostream>
#include <random>
#include <chrono>
using namespace std ;
int main(int argc , char *argv[] )
{
unsigned seed = chrono::high_resolution_clock::now().time_since_epoch().count() ;
mt19937 rand_generator(seed) ;
uniform_int_distribution<int> dist(6,666) ;
for(int cnt = 0 ; cnt < 10 ; ++cnt) cout << dist(rand_generator) << endl ;
return 0 ;
}
3.leetcode随机数实例
题目描述: 给定圆的半径和圆心的 x、y 坐标,写一个在圆中产生均匀随机点的函数 randPoint 。
说明:
- 输入值和输出值都将是浮点数。
- 圆的半径和圆心的 x、y 坐标将作为参数传递给类的构造函数。
- 圆周上的点也认为是在圆中。
- randPoint 返回一个包含随机点的x坐标和y坐标的大小为2的数组。
解题(注:此为在leetcode编辑器上编写,如要写在常用的IDE上需要稍作修改):
class Solution {
public:
Solution(double radius, double x_center, double y_center) {
m_radius=radius;
m_x_center=x_center;
m_y_center=y_center;
}
vector<double> randPoint() {
vector<double> VecRandPoint(2);
double x_min=m_x_center-m_radius;
double x_max=m_x_center+m_radius;
double y_min=m_y_center-m_radius;
double y_max=m_y_center+m_radius;
mt19937::result_type seed1 = chrono::high_resolution_clock::now().time_since_epoch().count();
mt19937::result_type seed2 = chrono::high_resolution_clock::now().time_since_epoch().count();
double x=x_min-10;
double y=y_min-10;
auto real_rand_x = std::bind(std::uniform_real_distribution<double>(x_min,x_max),mt19937(seed1));
auto real_rand_y = std::bind(std::uniform_real_distribution<double>(y_min,y_max),mt19937(seed2));
while(sqrt((x-m_x_center)*(x-m_x_center)+(y-m_y_center)*(y-m_y_center))>m_radius)
{
x=real_rand_x();
y=real_rand_y();
}
VecRandPoint[0]=x;
VecRandPoint[1]=y;
return VecRandPoint;
}
private:
double m_radius;
double m_x_center;
double m_y_center;
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(radius, x_center, y_center);
* vector<double> param_1 = obj->randPoint();
*/