第一个例子
#include <random>
#include <iostream>
#include <algorithm>
#include <vector>
int main(){
for (int i = 0; i < 10; ++i) {
std::default_random_engine e;
std::uniform_int_distribution<int> di(10, 20);
for (int i = 0; i < 15; ++i) {
std::cout <<di(e) <<" ";
}
std::cout <<"\n";
std::uniform_real_distribution<double > dr(10, 20);
for (int i = 0; i < 8; ++i) {
std::cout <<dr(e) <<" ";
}
std::cout <<"\n";
_sleep(1);
std::cout <<"\n\r\n";
}
}
产生随机数的方法是,将引擎e分别和两个分布di和dr组合起来:
- 引擎作为随机性的源头,它们是functioon object,能够产生随机的无正负值,并均匀分布于一个预定义的最小和最大值之间
- 分布表示以何种手法,将这些随机值转换为随机数,后者分布于一个由使用者给定的参数决定的区间内
C++标准库提供了很多引擎,上面的:
std::default_random_engine e;
表示“在效能、大小、质量等综合考量下—至少是个可被接受的引擎”,但究竟是哪一种引擎,由实现决定,不同的平台可能不同。
针对不同的随机值类型,C++标准库也提供了多种分布:线性、正态/高斯、指数等。上面例子是线性分布,只是一个用于整数、一个用于浮点数
std::uniform_int_distribution<int> di(10, 20);
std::uniform_real_distribution<double > dr
(1)std::uniform_int_distribution
会产生整数:short、int、long、long long以及响应的unsigned类型。如果不指定则默认为int。
- 构造函数:
- 构造函数的第一个参数是:最小值,默认为0
- 构造函数的第二个参数是:最大值,默认为numeric_limits< type>::max
- 注意,这是个闭合区间。比如
di(10, 20)
;,包括了10和20
- 如何产生一个随机数:
- 调用operator(),并将引擎作为实参传入,比如:
di(e)
- 调用operator(),并将引擎作为实参传入,比如:
- 注意,任何引擎的初始状态都有明确定义,并非随机的。因此,下面语句会输出相同的数值:
std::uniform_int_distribution<int> di(10, 20);
std::default_random_engine e1;
std::default_random_engine e2;
std::cout <<di(e1) <<" ";
std::cout <<"\n";
std::cout <<di(e2) <<" ";
std::cout <<"\n";
- 如果你需要一个不可预期的随机值,有两个方法:
-
(1)以随机形式设定生成器的状态,也就是处理传入一个“种子(seed)”给引擎构造函数,比如:
-
(2)调用成员函数,使用seed修改某个生成器的资源
-
(2)std::uniform_real_distribution
和std::uniform_int_distribution
几乎一样。不同点在于:
- 构造函数传入的最小和最大值是个半开区间,默认为[0.0, 1.0),可能的类型包括float、double(默认)、long double
我们还可以用随机数生成器来弄乱(shuffle)容器或者区间内的元素。std::shuffle利用一个均匀随机数生成器比如std::default_random_engine 来重排元素
std::default_random_engine e;
std::vector<int> v {1, 2, 3, 4, 5, 6, 7};
std::shuffle(v.begin(), v.end(), e);
for(int i = 0; i < v.size(); i++){
std::cout << v[i]<<" ";
}
你可以但是不应该传入一个临时引擎,原因是每次初始化一个引擎,其初始状态是相同的
#include <random>
#include <iostream>
#include <algorithm>
#include <vector>
int main(){
for (int i = 0; i < 10; ++i) {
std::vector<int> v {1, 2, 3, 4, 5, 6, 7};
std::shuffle(v.begin(), v.end(), std::default_random_engine());
for(int i = 0; i < v.size(); i++){
std::cout << v[i]<<" ";
}
_sleep(1);
std::cout <<"\n\r\n";
}
}
...
应该这样写:
#include <random>
#include <iostream>
#include <algorithm>
#include <vector>
int main(){
std::default_random_engine e;
for (int i = 0; i < 10; ++i) {
std::vector<int> v {1, 2, 3, 4, 5, 6, 7};
std::shuffle(v.begin(), v.end(), e);
for(int i = 0; i < v.size(); i++){
std::cout << v[i]<<" ";
}
_sleep(1);
std::cout <<"\n\r\n";
}
}
为什么不只用引擎产生的值作为随机数呢?如果区间不吻合,就 % n不就行了吗?
使用rand()%n计算随机数,失败原因:
- 当商被当前n而且为小整数时,很多伪随机数生成器制造出来的余数并不很随机,比如rand()的连续执行结果非偶即奇
- 如果n为大数,而且被生成的最大值并非均匀的可被n除尽,那么某些余数的出现率会远远高于其他
所以我们应该用引擎 + 分布来生成随机数
引擎
C++标准库提供了16个随机数引擎,可以使用它们来搭配某个分布来实现随机数或者重新洗牌
随机数引擎是一个带有状态的随机性源头,其状态决定了它将生成哪一个随机值序列,注意这不是随机数。每次以operator()调用它,就可以产出一个随机的无正负号值,并且内部状态会改变,使得可以此后再产出一个新随机值。
注意,通常“状态的转换”和“被生成值”被精确的指定,因此,在任何平台上,带有相同状态的同类型引擎将产生相同的随机值。唯一例外是default_random_engine,它依赖于实现,但是还是会产生可预期值。
如果你真的想要一个不可预期值,那么就必须随机设定生成器的状态,例如运用某些“你无法以代码加以影响”的行为,比如“两次鼠标点击之间的毫秒数”
使用cstdlib库
生成 [0, num) 范围的随机数
系统:linux
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
// Provide random number from 0..(num-1)
#define randof(num) (int) ((float) (num) * random () / (RAND_MAX + 1.0))
int main()
{
srandom ((unsigned) time (NULL)); // 生成随机数种子,必要,否则每次程序的运行结果都是一样的
for(int i = 0; i < 10; i++){
printf("%d\n", randof (100000));
}
return 0;
}