C rand() 函数
程序通常需要一个随机数源。C依赖于C库函数rand来生成随机数。此函数生成均匀分布的伪随机数,每个随机数的范围在0和一个系统相关的最大值(至少为32767)之间。
rand函数有一些问题:即使不是大多数,也有很多程序需要不同范围的随机数。一些应用需要随机浮点数。一些程序需要非均匀分布的数。而程序员为了解决这些问题而试图转换rand生成的随机数的范围、类型或分布时,常常会引入非随机性。
1.rand()
rand()函数用来产生随机数,但是,rand()的内部实现是用线性同余法实现的,是伪随机数,由于周期较长,因此在一定范围内可以看成是随机的。
rand()会返回一个范围在0到RAND_MAX(32767)之间的伪随机数(整数)。
在调用rand()函数之前,可以使用srand()函数设置随机数种子,如果没有设置随机数种子,rand()函数在调用时,自动设计随机数种子为1。随机种子相同,每次产生的随机数也会相同。
rand()函数需要的头文件是:<stdlib.h>
rand()函数原型:int rand(void);
使用rand()函数产生1-100 以内的随机整数:
int number1 = rand() % 100;
2. srand()
srand()函数需要的头文件仍然是:<stdlib.h>
srand()函数原型:
void srand (usigned int seed);
srand()用来设置rand()产生随机数时的随机数种子。参数seed是整数,通常可以利用time(0)或geypid(0)的返回值作为seed。使用rand()和srand()产生1-100以内的随机整数:
srand(time(0));
int number1 = rand() % 100;
3. 使用rand()和srand()产生指定范围内的随机整数的方法
“模除+加法”的方法,对于任意数,0<=rand()%(n-m+1)<=n-m,因此,0+m<=rand()%(n-m+1)+m<=n-m+m
因此,如要产生[m,n]范围内的随机数num,可用:
int num=rand()%(n-m+1)+m;
其中的rand()%(n-m+1)+m算是一个公式,比如产生10~30的随机整数:
srand(time(0));
int a = rand() % (21)+10;
C++随机数
定义在头文件random中的随机数库通过一组协作的类来解决这些问题:随机数引擎类和随机数分布类。
随机数引擎和分布
随机数引擎是函数对象类,它们定义了一个调用运算符,该运算符不接受参数并返回一个随机unsigned整数。我们可以通过一个随机数引擎对象来生成原始随机数:
default_random_engine e;//生成随机无符号数
for(size_t i = 0;i <10;i++)
cout<<e()<<endl;
我们定义了一个名为e的default_random_engine对象。在for循环内,我们调用对象e来获得下一个随机数。
标准库定义了多个随机数引擎类,区别在于性能和随机性质量不同。每个编译器都会指定其中一个作为default_random_engine类型。此类型一般具有最常用的特性。下表列出了随机数引擎操作:
类型 | 功能 |
Engine e | 默认构造函数;使用该引擎类型默认的种子 |
Engine e(s) | 使用整型值s作为种子 |
e.seed(s) | 使用种子s重置引擎的状态 |
e.min() / e.max() | 此引擎可生成的最小值和最大值 |
Engine::result_type | 此引擎生成的unsigned整型类型 |
e.discard(u) | 将引擎推进u步;u的类型为unsigned long long |
对于大多数场合,随机数引擎的输出是不能直接使用的,这也是为什么早先我们称之为原始随机数。问题在于生成的随机数的值范围通常与我们需要的不符合,而正确转换随机数的范围是极其困难的。
分布类型和引擎
为了得到一个指定范围内的数,我们使用一个分布类型的对象:
uniform_int_distribution<unsigned>u(0,9);
default_random_engine e;
for(size_t i=0;i<10;i++)
cout<<u(e)<<" ";
此处我们将u定义为uniform_int_distribution<unsigned>。此类型生成均匀分布的unsigned值。当我们定义一个这种类型的对象时,我们提供想要的最小值和最大值。在此程序中,u(0,9)表示我们希望得到0到9之间包含的数。随机数分布类会使用包含的范围,从而我们可以得到给定整型类型的每个可能值。
类似引擎类型,分布类型也是函数对象类。分布类型定义了一个调用运算符,它接受一个随机数引擎作为参数。分布对象使用它的引擎参数生成随机数,并将其映射到指定的分布。
我们传递给分布对象的是引擎对象本身,即u(e)。如果我们将调用写成u(e()),含义就变为将e生成下一个值传递给u,会导致一个编译错误。我们传递的是引擎本身,而不是它生成的下一个值,原因是某些分布可能需要调用引擎多次才能得到一个值。