C/C++中如何产生伪随机数
1. C语言中的伪随机数产生函数
本节主要参考自一博文及cppreferrence.
我们知道rand()函数可以用来产生随机数,但是这不是真正意义上的随机数,是一个伪随机数,是根据一个数(我们可以称它为种子)为基准以某个递推公式推算出来的一系列数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数,但这不是真正的随机数,当计算机正常开机后,这个种子的值是定了的,除非你破坏了系统。
1.1 rand()
功能:随机数发生器
用法:int rand(void)
所在头文件: stdlib.h
rand()的内部实现是用线性同余法做的,它不是真的随机数,因其周期特别长,故在一定的范围里可看成是随机的。
rand()返回一随机数值的范围在0至RAND_MAX 间。RAND_MAX的范围最少是在32767之间(int)。用unsigned int 双字节是65535,四字节是4294967295的整数范围。0~RAND_MAX每个数字被选中的机率是相同的。
用户未设定随机数种子时,系统默认的随机数种子为1。
rand()产生的是伪随机数字,每次执行时是相同的;若要不同,用函数srand()初始化它。
1.2 srand()
功能:初始化随机数发生器
用法: void srand(unsigned int seed)
所在头文件: stdlib.h
srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。
1.3 使用当前时钟作为随机数种子
rand()产生的随机数在每次运行的时候都是与上一次相同的。若要不同,用函数srand()初始化它。可以利用srand((unsigned int)(time(NULL))的方法,产生不同的随机数种子,因为每一次运行程序的时间是不同的。
1.4 产生随机数的用法
1)给srand()提供一个种子,它是一个unsigned int类型;
2)调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到RAND_MAX之间);
3)根据需要多次调用rand(),从而不间断地得到新的随机数;
4)无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的输出结果。
0~RAND_MAX之间的随机数程序
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
int main()
{
srand((unsigned)time(NULL));
for (int i = ; i < ; i++)
cout << rand() << '\t';
cout << endl; return ;
}
1.5 产生一定范围随机数的通用表示公式
要取得[a, b)的随机整数,使用(rand() % (b-a))+ a;
要取得[a, b]的随机整数,使用(rand() % (b-a+1))+ a;
要取得(a, b]的随机整数,使用(rand() % (b-a))+ a + 1;
通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。
要取得a到b之间的随机整数,另一种表示:a + (int)b * rand() / (RAND_MAX + 1)。
要取得0~1之间的浮点数,可以使用rand() / double(RAND_MAX)。
2. C++语言中的伪随机数产生器
本节主要参考自一博文及cplusplus和cppreferrence。其中,该博文是对cplusplus上该伪随机数条目的翻译,下文中会参考调整。
C++中伪随机数库Random是C++11才开始添加的。它允许我们结合生成器(Generators)和分布器(Distributions)来生成伪随机数。
2.1 一个简单的例子
#include <iostream>
#include <random>
using namespace std; int main()
{
default_random_engine generator;
uniform_int_distribution<int> dis(, );
for (int i = ; i < ; i++)
{
std::cout << dis(generator) << std::endl; // 1 0 1 1 2 6
} return ;
}
如果我们嫌每次都要传入生成器对象麻烦,我们可以使用std::bind来绑定生成器对象和分布器对象(注意bind在头文件functional中)。如下所示:
#include <iostream>
#include <random>
#include <functional> // std::bind
using namespace std; int main()
{
default_random_engine generator;
uniform_int_distribution<int> dis(, );
auto dice = bind(dis, generator);
for (int i = ; i < ; i++)
{
std::cout << dice() << std::endl; // 1 0 1 1 2 6
} return ;
}
其实我们会发现上边两个代码的结果不管跑多少次都是一样的,很奇怪!我们可以试着给生成器一个种子,如下:
#include <iostream>
#include <random>
#include <functional> // std::bind
using namespace std; int main()
{
default_random_engine generator(); // seed = 10
uniform_int_distribution<int> dis(, );
auto dice = bind(dis, generator);
for (int i = ; i < ; i++)
{
std::cout << dice() << std::endl; // 2 0 4 1 2 4
} return ;
}
现在我们看到结果不一样了。但是,不管我们再跑多少次,其结果还是一样的,这就更加奇怪了。究其原因,在于我们设定的种子是固定的。对于计算机而言,当输入确定时,输出也一定是确定的。因为生成器算法和分布器算法都是确定的,所以当输入是确定的时候,输出也必然是确定的。因此,为了得到不一样的结果,我们可以将系统时间当作种子:
#include <iostream>
#include <random>
#include <functional> // std::bind
#include "time.h" // time
using namespace std; int main()
{
default_random_engine generator(time(NULL));
uniform_int_distribution<int> dis(, );
auto dice = bind(dis, generator);
for (int i = ; i < ; i++)
{
std::cout << dice() << std::endl;
} return ;
}
2.2 关于生成器和分布器
关于生成器和分布器,请参照cplusplus手册。
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>
#include <cmath>
using namespace std; int main()
{
// Seed with a real random value, if available
std::random_device rd;
cout << rd() << endl;
// Choose a random mean between 1 and 6
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> uniform_dist(, );
int mean = uniform_dist(e1);
std::cout << "Randomly-chosen mean: " << mean << '\n'; // Generate a normal distribution around that mean
std::mt19937 e2(rd());
std::normal_distribution<> normal_dist(mean, ); // default: double std::map<int, int> hist;
for (int n = ; n < ; ++n) {
++hist[std::round(normal_dist(e2))];
}
std::cout << "Normal distribution around " << mean << ":\n";
for (auto p : hist) {
std::cout << std::fixed << std::setprecision() << std::setw()
<< p.first << ' ' << std::string(p.second / , '*') << '\n';
} return ;
}
我们可以发现上述例子用到了random_device这个生成器。该生成器产生的是非确定性随机数。在Linux的实现中,是读取/dev/urandom设备;在Windows的实现居然是用rand_s。random_device提供()操作符,用来返回一个min()到max()之间的一个数字(min()、max()均为该生成器成员函数)。如果是Linux(Unix Like或者Unix)下,都可以使用这个来产生高质量的随机数,可以理解为真随机数。
// random_device example
#include <iostream>
#include <random> int main ()
{
std::random_device rd; std::cout << "default random_device characteristics:" << std::endl;
std::cout << "minimum: " << rd.min() << std::endl;
std::cout << "maximum: " << rd.max() << std::endl;
std::cout << "entropy: " << rd.entropy() << std::endl;
std::cout << "a random number: " << rd() << std::endl; return ;
}
可能的输出是:
default random_device characteristics:
minimum:
maximum:
entropy:
a random number:
有些情况下,随了拿时间当种子,random_device也经常被用来生成种子。
C/C++中如何产生伪随机数的更多相关文章
- 【numpy】新版本中numpy(numpy>1.17.0)中的random模块
numpy是Python中经常要使用的一个库,而其中的random模块经常用来生成一些数组,本文接下来将介绍numpy中random模块的一些使用方法. 首先查看numpy的版本: import nu ...
- PHP的加密伪随机数生成器的使用
今天我们来介绍的是 PHP 中的加密伪随机数生成器(CSPRNG 扩展).随机数的生成其实非常简单,使用 rand() 或者 mt_rand() 函数就可以了,但是我们今天说的这个则是使用了更复杂算法 ...
- 【原创】开源Math.NET基础数学类库使用(13)C#实现其他随机数生成器
本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新 开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 前言 ...
- 开源Math.NET基础数学类库使用(13)C#实现其他随机数生成器
原文:[原创]开源Math.NET基础数学类库使用(13)C#实现其他随机数生成器 本博客所有文章分类的总目录:http://www.cnblogs.com/asxiny ...
- Python之数据加密与解密及相关操作(hashlib模块、hmac模块、random模块、base64模块、pycrypto模块)
本文内容 数据加密概述 Python中实现数据加密的模块简介 hashlib与hmac模块介绍 random与secrets模块介绍 base64模块介绍 pycrypto模块介绍 总结 参考文档 提 ...
- python--random库基本介绍
random库是使用随机数的Python标准库 python中用于生成伪随机数的函数库是random 因为是标准库,使用时候只需要import random random库包含两类函数,常用的共9个 ...
- Go Rand小结
对于Random的使用,在业务中使用频率是非常高的,本文就小结下常用的方法: 在Golang中,有两个包提供了rand,分别为 "math/rand" 和 "crypto ...
- Python之数据加密与解密及相关操作(hashlib、hmac、random、base64、pycrypto)
本文内容 数据加密概述 Python中实现数据加密的模块简介 hashlib与hmac模块介绍 random与secrets模块介绍 base64模块介绍 pycrypto模块介绍 总结 参考文档 提 ...
- Python__random库基本介绍
random库是使用随机数的Python标准库 从概率论角度来说,随机数是随机产生的数据(比如抛硬币),但时计算机是不可能产生随机值,真正的随机数也是在特定条件下产生的确定值,只不过这些条件我们没有理 ...
随机推荐
- 查看4k对齐,激活.net framework 3.5
查看是否4k对齐 Win+R,打开运行窗口,在窗口中输入“msinfo32",组件”--“存储”--“磁盘”.然后可以在右边栏看到“分区起始偏移”,我们图例中有2个数值,分别是:32256字 ...
- [shiro学习笔记]第三节 使用myeclipse导入apache shiro中的QuikStart example例子
本文地址:http://blog.csdn.net/sushengmiyan/article/details/40149131 shiro官网:http://shiro.apache.org/ shi ...
- 【环境配置】配置maven
Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具. Maven 除了以程序构建能力为特色之外,还提供高级项目管理工具.由于 Maven 的缺 ...
- UNIX网络编程——客户/服务器程序设计示范(八)
TCP预先创建线程服务器程序,主线程统一accept 最后一个使用线程的服务器程序设计示范是在程序启动阶段创建一个线程池之后只让主线程调用accept并把每个客户连接传递给池中某个可用线程. ...
- Sql Server服务远程过程调用失败
由于开发系统,需要vs版本统一,于是经过了昨天一整天艰苦卓绝的斗争,小编终于成功的写在了13版本的vs,重新装上了12版本的vs,本来想着,12版本的vs搭建成功了,就赶快搭建框架,然后敲例子,可是天 ...
- SpriteBuilder中返回的对象类型不正确的原因
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 最近在码代码的时候,发现一个问题,特此写出来和大家分享,希望遇到 ...
- 03_NoSQL数据库之Redis数据库:list类型
lists类型及操作 List是一个链表结构,主要功能室push,pop.获取一个范围的所有值等等,操作中key理解为链表的名字.Redis的list类型其实就是一个每个元素都是string类型 ...
- 1060. Are They Equal (25)
题目如下: If a machine can save only 3 significant digits, the float numbers 12300 and 12358.9 are consi ...
- UNIX网络编程——网络层:IP
一.IP数据报格式 IP数据报格式如下: 版本:IP协议版本号,长度为4位,IPv4此字段值为4,IPv6此字段值为6 首部长度:以32位的字为单位,该字段长度为4位,最小值为5,即不带任何选项的IP ...
- android 中List转换String,String转换List 改进版本
原来博客地址http://blog.csdn.net/qq7342272/article/details/6830907 使用原作者贴的代码不是很好用,不能正常运行,所以我稍微改进了一下,特来分享给大 ...