CC++中如何产生伪随机数
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
CC++中如何产⽣伪随机数
本节主要参考⾃⼀及.
我们知道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之间的随机数程序
1 #include <iostream>
2 #include <stdlib.h>
3 #include <time.h>
4using namespace std;
5int main()
6 {
7 srand((unsigned)time(NULL));
8for (int i = 0; i < 10; i++)
9 cout << rand() << '\t';
10 cout << endl;
11
12return0;
13 }
View Code
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)。
本节主要参考⾃⼀及和。
其中,该博⽂是对cplusplus上该伪随机数条⽬的翻译,下⽂中会参考调整。
C++中伪随机数库Random是C++11才开始添加的。
它允许我们结合⽣成器(Generators)和分布器(Distributions)来⽣成伪随机数。
⽣成器(Generators):能够产⽣离散的等可能分布数值。
分布器(Distributions): 能够把⽣成器产⽣的均匀分布值映射到其他各种各样的分布,如均匀分布uniform,正态分布normal,⼆项分布binomial,泊松分布poisson。
2.1 ⼀个简单的例⼦
下边就是⼀个简单的例⼦:
1 #include <iostream>
2 #include <random>
3using namespace std;
4
5int main()
6 {
7 default_random_engine generator;
8 uniform_int_distribution<int> dis(0, 6);
9for (int i = 0; i < 6; i++)
10 {
11 std::cout << dis(generator) << std::endl; // 1 0 1 1 2 6
12 }
13
14return0;
15 }
View Code
如果我们嫌每次都要传⼊⽣成器对象⿇烦,我们可以使⽤std::bind来绑定⽣成器对象和分布器对象(注意bind在头⽂件functional中)。
如下所⽰:
1 #include <iostream>
2 #include <random>
3 #include <functional> // std::bind
4using namespace std;
5
6int main()
7 {
8 default_random_engine generator;
9 uniform_int_distribution<int> dis(0, 6);
10 auto dice = bind(dis, generator);
11for (int i = 0; i < 6; i++)
12 {
13 std::cout << dice() << std::endl; // 1 0 1 1 2 6
14 }
15
16return0;
17 }
View Code
其实我们会发现上边两个代码的结果不管跑多少次都是⼀样的,很奇怪!我们可以试着给⽣成器⼀个种⼦,如下:
1 #include <iostream>
2 #include <random>
3 #include <functional> // std::bind
4using namespace std;
5
6int main()
7 {
8 default_random_engine generator(10); // seed = 10
9 uniform_int_distribution<int> dis(0, 6);
10 auto dice = bind(dis, generator);
11for (int i = 0; i < 6; i++)
12 {
13 std::cout << dice() << std::endl; // 2 0 4 1 2 4
14 }
15
16return0;
17 }
View Code
现在我们看到结果不⼀样了。
但是,不管我们再跑多少次,其结果还是⼀样的,这就更加奇怪了。
究其原因,在于我们设定的种⼦是固定的。
对于计算机⽽⾔,当输⼊确定时,输出也⼀定是确定的。
因为⽣成器算法和分布器算法都是确定的,所以当输⼊是确定的时候,输出也必然是确定的。
因此,为了得到不⼀样的结果,我们可以将系统时间当作种⼦:
1 #include <iostream>
2 #include <random>
3 #include <functional> // std::bind
4 #include "time.h"// time
5using namespace std;
6
7int main()
8 {
9 default_random_engine generator(time(NULL));
10 uniform_int_distribution<int> dis(0, 6);
11 auto dice = bind(dis, generator);
12for (int i = 0; i < 6; i++)
13 {
14 std::cout << dice() << std::endl;
15 }
16
17return0;
18 }
View Code
2.2 关于⽣成器和分布器
关于⽣成器和分布器,请参照⼿册。
下边是⼀个来⾃cppreferrence的例⼦:
1 #include <iostream>
2 #include <iomanip>
3 #include <string>
4 #include <map>
5 #include <random>
6 #include <cmath>
7using namespace std;
8
9int main()
10 {
11// Seed with a real random value, if available
12 std::random_device rd;
13 cout << rd() << endl;
14// Choose a random mean between 1 and 6
15 std::default_random_engine e1(rd());
16 std::uniform_int_distribution<int> uniform_dist(1, 6);
17int mean = uniform_dist(e1);
18 std::cout << "Randomly-chosen mean: " << mean << '\n';
19
20// Generate a normal distribution around that mean
21 std::mt19937 e2(rd());
22 std::normal_distribution<> normal_dist(mean, 2); // default: double
23
24 std::map<int, int> hist;
25for (int n = 0; n < 10000; ++n) {
26 ++hist[std::round(normal_dist(e2))];
27 }
28 std::cout << "Normal distribution around " << mean << ":\n";
29for (auto p : hist) {
30 std::cout << std::fixed << std::setprecision(1) << std::setw(2)
31 << p.first << '' << std::string(p.second / 200, '*') << '\n';
32 }
33
34return0;
35 }
View Code
我们可以发现上述例⼦⽤到了random_device这个⽣成器。
该⽣成器产⽣的是⾮确定性随机数。
在Linux的实现中,是读
取/dev/urandom设备;在Windows的实现居然是⽤rand_s。
random_device提供()操作符,⽤来返回⼀个min()到max()之间的⼀个数字(min()、max()均为该⽣成器成员函数)。
如果是Linux(Unix Like或者Unix)下,都可以使⽤这个来产⽣⾼质量的随机数,可以理解为真随机数。
⼀个例⼦如下:
1// random_device example
2 #include <iostream>
3 #include <random>
4
5int main ()
6 {
7 std::random_device rd;
8
9 std::cout << "default random_device characteristics:" << std::endl;
10 std::cout << "minimum: " << rd.min() << std::endl;
11 std::cout << "maximum: " << rd.max() << std::endl;
12 std::cout << "entropy: " << rd.entropy() << std::endl;
13 std::cout << "a random number: " << rd() << std::endl;
14
15return0;
16 }
View Code
可能的输出是:
1default random_device characteristics:
2 minimum: 0
3 maximum: 4294967295
4 entropy: 32
5 a random number: 4230014324
View Code
有些情况下,随了拿时间当种⼦,random_device也经常被⽤来⽣成种⼦。