你真的懂随机数?
Author : Jasper Yang
School : Bupt
Q:为什么要写这篇文章?
A:因为我发现在最近的科学计算中,常常遇到随机数,所有的随机数都是基于0,1随机,而这个0,1随机怎么实现呢?下面我会娓娓道来~
这篇文章不同于网路上的杂散的技术文,我是针对 random 这么一个论题展开调研最后将所有相关的知识进行整理叙述,希望每个人看完都可以得到小小的提升~
& 什么是随机数
随机数:数学上产生的都是伪随机数,真正的随机数使用物理方法产生的
随机数种子:随机数的产生是由算术规则产生的,在c++中,srand(seed)的随机数种子不同,rand()的随机数值就不同,倘若每次的随机数种子一样,则rand()的值就一样。所以要产生随机数,则srand(seed)的随机数种子必须也要随机的。在 python 中就是 random.seed()来设置种子。
下面我讲的随机数不仅仅讲随机数生成的原理,也会讲在python中以及在c++中怎么去实现,当然,大部分资料也都是网上找的,我只是做了一个整理汇总,并用自己的语言加以叙述。
& 随机数的原理
这里我看了一篇博客,由于这篇博客是那个博主转的,但是该博主并没有表明是从哪里转来的,我就不po出链接了,大家往下看~
有位朋友问那博主关于一段程序的错误。
C/C++ code
for (int i =0;i< n;++i)
{
srand((unsigned)time( NULL ));
int r = rand()%100;
cout << r << ",";
}
这里很明显他是想输出一串小于100的随机的数列.可是运行结果输出的却是类似 97,97,97,97,....97,30,30,30,30,30,30,30,30,30,30,30,30,....,27,27,27,27,27,27,....的序列.很明显这样完全看不出有任何的随机性.这是由于他对C的rand函数不理解导致的错误用法.而这两天逛C#区我也同样看到了几个类似的错误用法(C和C#的rand从大体的原理上差不多).想想自己初学的时候类似的错误犯得也不少.所以自己下去查了写资料总结了在随机数使用上的一些错误的用法.希望能对初学者有所帮助。
现在各种语言中的随机数产生函数所产生的"随机数",实际上被称之为"伪随机数".可以将
整个随机数函数看做这样一个表达式:
$$A = R(s)$$
其中R是随机函数,s是种子.A是一个数列.即对于任意一个种子s,经过R的计算后,总有一个确定的数列A与之对应.而当在C#里调用var rnd = new Random (s)或在C里调用srand(s)实质上所做工作之一就是设定这个种子.而rnd.Next();或rand()只不过是在A上取下一个元素而已.当然实际的实现不可能事先计算一个数列A,所以rand()相当于由s计算出下一个数字s',然后将s'作为新的种子赋值给s,最后将s'作为结果返回。
往细了讲,就是这样。
如果约定:$a_1=f(seed),a_{n+1}=f(an)$
那你可以行到一个序列:$a_1,a_2,a_3...a_n$,那么要制作一个伪随机函数rand,只需要让它每调用一次就返回序列的下一个元素就行。
下面是两种常见的错误做法
C# code
for (int i=0;i
{
var rnd = new Random (s);//s是实先确定的一个数字
Console.Write ("{0},",rnd.Next());
}
这样,每次使用Random,都去申请了一个变量rnd,然后才用这个变量去找随机数(rnd.Next())。这样其实就是在随机数的序列中总是在找第一个。这样下来,第一个数肯定是固定的,就不存在什么随机数了。
第二种情况更加常见。
C# code
for (int i=0;i
{
var rnd = new Random ();//用系统时间作为种子
Console.Write ("{0},",rnd.Next());
}
之前的第一种情况使用了一个固定的常数s来做种子,这里选用了系统时间做种子,想要达到随机的效果,但是得到的结果往往就会是和博主那位朋友一样的结果97,97,97,97,....97,30,30,30,30,30,30,30,