随机数通过 随机数引擎类 和 随机数分布类。
引擎类可以生成 unsigned(无符号数) 随机数序列;分布类使用一个引擎类生成指定类型的、在给定范围内的、服从特定概率分布的随机数。
它们定义在头文件 random 中。
随机数引擎:
随机数引擎是函数对象类,它们定义了一个调用运算符,该运算符不接受参数并 返回一个随机 unsigned 整数。
上代码:
default_random_engine e;//生成随机无符号数(原始随机数) int i = 100; for (int i = 0; i < 10; i++) { cout << e( ) << endl;//调用对象 e 来生成10个随机数 }
上面程序输出 :
以下《C++ primer 》 :
分布类型:
uniform_int_distribution<unsigned> u(1, 100);//生成1到100(包含100)之间的均匀分布的随机数 default_random_engine e;//生成随机无符号数(原始随机数) for (int i = 0; i < 100; i++) { cout << u(e) << endl;//将 u 作为随机数源 }
分布类型也是函数对象类。它接受一个随机数引擎作为参数。
我们传递给分布对象的是引擎对象本身。不能写成 : u ( e() ).
随机数发生器就是指分布对象和引擎对象的组合。
cout << e.min() << " " << e.max() << endl;//获得随机数引擎的范围
我们上面所写的代码生成一次随机数,第二次再生成就会是相同的,这并不能达到我们想的真正的随机数。
我们可以把引擎和分布对象定义为 static 的,让它保持状态:
#include <iostream> #include<random> using namespace std; #include<vector> static default_random_engine e; static uniform_int_distribution<unsigned> u(1, 100); vector<unsigned> doo() { vector<unsigned> ret; for (int i = 0; i < 10; i++) { ret.push_back(u(e));//向容器中添加元素 } for (auto i : ret)//利用范围 for 输出 { cout << i<<" "; } return ret; } int main() { doo(); //13 3 35 86 5 92 30 86 99 4 cout << endl; doo(); //36 66 41 27 40 21 20 5 98 7 cout << '\n'; doo();// 10 10 44 40 47 27 37 54 61 72 system("pause"); return 0; }
一个给定的随机数发生器一直会生成相同的随机数序列。我们可以通过设置随机数发生器种子来实现真正的随机数。
每次运行程序都会生成不同的随机结果,我们可以通过提供一个种子来达到目的。
为引擎设置种子的两种方式:在创建引擎对象 时提供种子,或者调用引擎的 seed 成员。
default_random_engine e1;//使用默认种子值 default_random_engine e2(32767);//给定种子值 default_random_engine e3; for (int i = 0; i < 10; i++) { cout << e1() <<" "; } cout << endl; for (int i = 0; i < 10; i++) { cout << e2() << " "; } cout << endl; e3.seed(32767);//调用 seed 设置一个新种子值 for (int i = 0; i < 10; i++) { cout << e3() << " "; }
上面代码中本来 e1 和 e3 都是使用默认的种子值,它们两个生成的随机数序列将会是相同的。e2是我们自己给定的,它和 e1 和 e3 和种子值不同,因些 e2 生成的随机数序列不会是其他两个是一样的。后面我们通过调用 seed 函数为 e3 重新设置了一个种子值,和 e2的一样,又变成了 e2 和 e3 是生成相同的随机数序列。
选择一个好种子是极其困难的: 最常用的方法就是调用系统函数 time , 这个函数定义在头文件 ctime 中,它返回一个特定时刻到当前经过了多少秒。
default_random_engine e2(time(0));//给定种子值 uniform_int_distribution<int> u(0, 100); cout << endl; for (int i = 0; i < 10; i++) { cout <<u(e2) << " ";//第次调用的结果几乎都会不一样 }
time返回以秒计的时间,因此这种方式只适用于生成种子的间隔为秒级或更长的时间。
生成随机浮点数:
我们定义一个 uniform_real_distribution 类型的对象,让标准库来处理从随机数到随机浮点数的映射。
default_random_engine e2(time(0));//给定种子值 uniform_real_distribution<float> u(0, 1); for (int i = 0; i < 10; i++) { cout <<u(e2) << " ";//每次调用都会不同的浮点数 }
分布类型的操作:
分布类型都是模板,具有单一的模板类型参数,每个分布模板都有一个默认模板实参。
uniform_real_distribution < > u1(0, 1); //空尖表示我们想使用默认结果类型,默认生成 double 值 uniform_int_distribution <> u2(0, 100); // 默认生成 unsigned 值
生成非均匀分布的随机数:
normal_distribution 生成浮点值。
default_random_engine e2(time(0));//给定种子值 normal_distribution<> n(10, 1.3); for (int i = 0; i < 10; i++) { cout <<lround(n(e2)) << " ";//lround 函数浮点数舍入到最接近的整数。它定义在头文件cmath中 }
bernoulli_distribution类:
它是一个普通类,不接受模板参数。它总是返回一个 bool 值,它返回 true 的概率是一个常数,默认为 0.5;
default_random_engine e2(time(0));//给定种子值 bernoulli_distribution b; for (int i = 0; i < 10; i++) { cout <<b(e2) << " ";//它返回的真假的次数是相同的,因为默认的概率是一样的 }
使用它的原因是允许我们调整随机的概率:
bernoulli_distribution b; //默认是 50/50 的机会 bernoulli_distribution b(.55);//代表程序有55/45机会
引擎返回相同的随机数序列,所以我们必须要循环外声明引擎对象。不然第次循环都会创建一个新引擎,从而每次都会生成相同的值,分布对象在要保持状态,也应该在循环外面。
其他的扩展感兴趣可以自己看看(来自书《C++ primer》):