C++随机数在0和1之间

169 阅读5分钟

一个随机数是在一个范围内产生的,从一个最小数到一个最大数。假设这些最小和最大的数字都大于1,那么在这个范围内产生的数字就是num。让最小数为min,让最大数为max。有了这些,为了将数字转换到0和1之间,使用公式。

random_number= (num- min)/(max - min)

random_number现在应该在0和1之间。
接下来的问题是如何生成随机数以及如何决定min和max。事实上,C++20规范所描述的随机数,实际上是伪随机数。C++20规范给出了产生真正的随机数(非决定性随机数)的指南。这种真正的随机数生成器的问题是,编译器的责任,或者说程序员的责任,是提供被认为是非确定性随机数生成的算法。本文不涉及非确定性的随机数。

伪随机数是以数字的序列(一种顺序)产生的,看起来像随机数。随机数的生成需要一个所谓的种子。种子是一些起始值。这篇文章解释了C++20中随机数生成的基础知识。如果生成的数字大于1,就用上述公式将其降到0和1之间。必须在程序中包含C++ 库,才能有一个随机的或者说随机的数字序列。

文章内容

分布
均匀分布

均匀分布是指一个数字的概率是序列中的数字总数的1。考虑下面的序列。

0, 10,20,30,40,50,60,70,80,90,100

如果这11个数字是一个随机数字序列,每个数字在11次出现中出现过一次。这意味着它是一个均匀分布。在实践中,不一定都会出现一次。一个或两个或三个可能会出现不止一次,而且它们不会按常规顺序出现。

如果返回的随机数是40,那么程序必须用以下方法将随机数转换为0和1之间的数字

random_number= (40-0)/(100- 0)

= 4/10 = 0.4

这里,num是40;min是0,max是100。

二项式分布

二项分布不是一种均匀分布。"Bi "是二项式的前缀,意味着两个。二项分布中的值的数量在C++中用t表示。如果该分布所涉及的bi数是2和3,如果t是1,那么序列就是。

2,3

如果t是2,同样的双数(2和3),那么序列就变成了。

4,12,9

如果t是3,对于相同的双数(2和3),那么这个序列就变成了。

8,36,54,27

如果t是4,同样的双数(2和3),那么序列就变成了。

16,96,216,216,81

t是一个正整数,可以超过4。对于t的每个值,序列中有t+1个元素。一个序列取决于所选择的双数和t的值。双数可以是任何一对,例如,13和17。双数的总和也很重要。一个序列是由所谓的二项式定理发展而来的。

在C++的随机库中还有其他分布。

线性_共轭_引擎

C++中有许多随机数引擎。 linear_congruential_engine就是其中之一。这个引擎取一个种子,用一个乘法器相乘,并在乘积上加上一个常数c,从而得到第一个随机数。第一个随机数成为新的种子。这个新的种子被乘以同样的'a',其乘积被添加到同样的c,从而得到第二个随机数。这第二个随机数成为下一个随机数的新种子。根据程序员的要求,这个过程可以重复多少个随机数。

这里的种子有一个索引的作用。默认的种子是1。

linear_congruential_engine的语法是。

linear_congruential_engine<classUIntType, UIntType a, UIntType c, UIntType m> lce

lce是程序员选择的名称。这个语法使用默认的1的种子。这里的第一个模板参数应该用 "无符号int "来专门化。第二个和第三个应该有 "a "和c的实际值,第四个应该有预期最大随机数的实际值,再加上1。

假设需要一个值为2的种子,那么语法将是。

linear_congruential_engine<classUIntType, UIntType a, UIntType c, UIntType m> lce(2)

注意lce后面的括号里的种子。

下面的程序,说明了 linear_congruential_engine 的使用,默认的种子为 1。

#include

#include

using namespacestd;

int main()

{

linear_congruential_engine<unsigned int,3,1,500>lce;

cout <<lce() <<endl;

cout <<lce() <<endl;

cout <<lce() <<endl;

cout <<lce()<<endl;

cout <<lce() <<endl;

cout <<endl;

cout <<lce.min () <<endl;

cout <<lce. max () <<endl;

return 0;

}

输出结果是。

4

13

40

121

364

0

499

注意引擎的lce对象被实例化的方式。这里,'a'是3,c是1,最大的、希望达到的数字m是500。m实际上是一个模子--见后面。它是一个运算符,用于返回输出序列中引擎所需的下一个随机数。这个方案的最小值是0,最大值是499,这些可以用来将返回的数字转换为0和1之间--见下文。

返回的第一个随机数是4,它等于1 X 3 + 1 = 4。4成为新的种子。下一个随机数是13,它等于4 X 3 + 1 = 13。13成为新的种子。下一个随机数是40,它等于13 X 3 + 1 = 40。这样一来,后面的随机数是121和364。

下面的代码,说明了Linear_congruential_engine的使用情况,种子为2。

linear_congruential_engine<unsigned int,3,1,1000>lce(2);

cout <<lce() <<endl;

cout <<lce() <<endl;

cout <<lce() <<endl;

cout <<lce() <<endl;

cout <<lce() <<endl;

cout <<endl;

cout <<lce.min () <<endl;

cout <<lce. max () <<endl;

输出结果是。

7

22

67

202

607

0

999

这里希望的最大随机数是1000。这个方案的最小值仍然是0,最大值现在是999,这些可以用来将返回的数字转换为0和1之间--见下文。

第一个返回的随机数是7,它等于2 X 3 + 1 = 7,7成为新的种子。下一个随机数是22,它等于7 X 3 + 1 = 22。22成为新的种子。下一个随机数是67,它等于22 X 3 + 1 = 67。这样一来,后面的随机数是202和607。

下面的代码使用上述公式来产生一个介于0和1之间的随机数,用于这个引擎。

linear_congruential_engine<unsigned int,3,1,1000>lce(2);

无符号 intnum=lce();// 正常的随机数

无符号 intmin=lce.min();

unsigned intmax=lce.max();

floatrandom_number=((float)(num -min))/((float)(max- min));

cout <<random_number<<endl;

输出结果是

0.00700701

这里,num是7,所以

random_number= (7-0)/(999-0) = 7/999 = 0.00700701,四舍五入到小数点后8位。

linear_congruential_engine不是随机库中唯一的专门引擎;还有其他的。

default_random_engine

这就像一个通用的引擎。它产生随机数。序列的顺序不保证是不确定的。然而,程序员很可能不知道这个顺序。下面两行显示了如何使用这个引擎。

random_device rd;

default_random_engine eng(rd())。

random_device是一个类,rd已经被实例化了。注意引擎的参数列表中rd的括号。分配器需要这个引擎进行操作--见下文。

随机数分布类
uniform_int_distribution

uniform_int_distribution
任何数字出现的概率是1除以该类数字的总数。例如,如果有十个可能的输出数字,每个数字被显示的概率是1/10。下面的代码说明了这一点。

random_device rd;

default_random_engine eng(rd());

uniform_int_distributiondist(3, 12);

cout <<dist(eng ) <<' 'dist(eng ) <<' 'dist(eng )<<' ' <dist(eng ) <<' <dist(eng ) <<' <endl;

cout <<dist(eng )<<' '< dist(eng)<<dist(eng )<<' <dist(eng ) <<'<dist(eng )<< '<dist(eng )' <endl;

作者电脑的输出结果是。

9 8 3 5 12

7 4 11 7 6

不幸的是,7出现了两次,却牺牲了10。dist的参数是数字3和13(包括10个连续的整数)。 dist(eng)是一个返回下一个数字的运算符。它使用了引擎。注意使用int模板的特殊化。

在这种情况下,没有必要寻找num、min和max,然后使用上述公式来获得0和1之间的数字,这是因为这个类有一个使用float特殊化的float等价物。每次运行的输出结果都不会相同。

uniform_real_distribution

uniform_real_distribution与uniform_int_distribution类似。有了它,为了获得一个介于0和1之间的数字,只需使用0和1作为参数。下面的代码说明了这一点。

random_device rd;

default_random_engine eng(rd());

uniform_real_distributiondist(0, 1);

cout <<dist(eng)< ' <dist(eng )<' <dist(eng)<<' ' <dist(eng ) <<' <dist(eng ) <<' <endl;

cout <<dist(eng )<<' '< dist(eng)<<dist(eng )<<' <dist(eng ) <<'<dist(eng )<< '<dist(eng )' <endl;

作者电脑的输出结果是。

0.384051 0.745187 0.364855 0.122008 0.580874

0.745765 0.0737481 0.48356 0.184848 0.745821

注意使用浮动模板的特殊化。每次运行的输出结果都不会相同。

二项分布(binomial_distribution

有了这个分布,每个输出数字的概率就不一样了。上面已经说明了binomial_distribution。下面的代码显示了如何使用二项分布来产生10个随机数。

random_device rd;

default_random_engine eng(rd());

binomial_distributiondist(10);

cout <<dist(eng ) <<' ' <dist(eng ) <<'' <dist(eng )<<' ' <dist(eng ) <<'<dist(eng ) <<' <endl;

cout <<dist(eng )<<' '< dist(eng)<<dist(eng )<<' <dist(eng ) <<'<dist(eng )<< '<dist(eng ) ' <endl;

作者电脑的输出结果是。

5 3 5 5 7

6 6 5 8 3

每次运行的输出结果都不会相同。这里使用的模板特殊化是int。

下面的代码使用上述公式来产生一个介于0和1之间的随机数,对于这个分布。

random_device rd;

default_random_engine eng(rd());

binomial_distributiondist(10);

unsigned intnum=dist(eng);// normal random number

unsigned intmin=dist.min();

unsigned intmax=dist.max();

cout <<min<<endl;

cout << max << endl;<max<<endl;

cout <<endl;

cout <<num<<endl;

floatrandom_number=((float)(num -min))/((float)(max- min));

cout <<random_number<<endl;

作者电脑的输出结果是:。

0

10

7

0.7

更好的随机数

UNIX Epoch以来的秒数可以作为种子。黑客要知道种子就变得很困难。下面的程序用linear_congruential_engine说明了这一点。

#include

#include

#include

using namespacestd;

int main()

{

const autop1=chrono::system_clock::now();

unsigned intseed=chrono::duration_caststd::chronono::seconds(p1.time_since_epoch())。count();

linear_congruential_engine<unsigned int,3,1,1000>.lce(seed);

cout <<lce() <<'' <lce() <<<' ' <lce() <<'' <lce()<< '' <lce() <<''<<endl;

cout <<endl;

cout <<lce.min () <<endl;

cout <<lce. max () <<endl;

return 0;

}

作者电脑的输出结果是。

91 274 823 470 411

0

999

请注意,CHRONO库已被包括在内。每次运行的输出都是不同的。

结论

要想获得0到1之间的随机数,最简单的方法是使用random_device、default_random_engine和uniform_real_distribution(参数为0和1)。任何其他的引擎或分布可能需要使用公式,随机数=(num - min)/(max - min)。