今 天在微博上到一篇如何使用随机数的文章,让我回忆起刚上大一时学C语言时,书后有道调用rand()函数的练习题,当时以为好神奇,想知道它是怎么实现 的,大二时候学Java又遇到了random()函数,恰巧当时上机课我有机会问老师,遗憾的是老师只是告诉我那是伪随机数,课后查查资料才了解。现在来 一篇关于随机数发生器博文来回忆一下神奇的随机数。java
众所周知,咱们平时所使用的不管什么编程语言都会提供一个随机数函数,并且它是伪随机数(Pseudo Random Number),它是由算法计算得出的,是能够预测的,也就是说当随机种子相同时,对于同一个随机函数,得出的随机数列是固定不变的,亚裔惟一图灵奖得主姚期智就是研究的就是伪随机数生成论;与之对应的就是真随机数(True Random Number)它是真正的随机数,没法预测且无周期性;还有一种是产生随机数的发生器是密码学伪随机数发生器(Cryptographically Secure Pseudo-Random Number Generator)经常使用的算法有 MD5 ,SHA1 等标准, 这里不作过多讨论,说说最基本的前两种:python
1、真随机数发生器
像没法实现永动机同样,想要实现真随机数靠程序是永远没法实现的,不少状况下只能看老天的眼色,好比布朗运动,量子效应,放射性衰变等。第一个真随机数发生器是1955年由Rand公司创造的,而在1999年,intel发布Intel810芯片组时,就配备了硬件随机数发生器,基于IntelRNG的真随机数生成器能够生成知足独立性和分布均匀性的真随机数,目前大部分芯片厂商都集成了硬件随机数发生器,只要安装相应驱动,了解读取寄存器地址,能够直接调用发生器。Intel810RNG的原理大概是:利用热噪声(是由导体中电子的热震动引发的)放大后,影响一个由电压控制的振荡器,经过另外一个高频振荡器来收集数据。TRNG的类型主要有:算法
1.基于电路的TRNG:
i.振荡器采样:就是上述Intel采用的方式。编程
ii.直接放大电路噪声:利用电路中各类噪声,如上述的热噪声做为随机源,因为强度小,因此先要对其放大,而后对必定时间内超过阈值的数据进行统计,这样就产生的随机数。.数组
iii.电路亚稳态:亚稳态表示触发器没法在规定时间内达到一个可确认状态,必定条件下,触发器达到两个稳态的概率为50%,因此先使电路进入亚稳态,以后根据状态转化为随机数。dom
iv.混沌电路:不可预测,对初始条件的敏感的依赖性。以及混沌电路在芯片中易于实现的特色,能够产生效果不错的随机数。编程语言
2.基于其余物理源的TRNG
如宇宙射线,粒子衰变,空气噪声等做为随机源,来产生随机数。函数
3.其余物理信息TRNG
人为能够产生随机数吗?固然能!据说一个HR拆选简历的方式是往天上一扔,掉在桌子上的简历就经过,这个HR确认懂随机啊,并且是真随机。这类随机生活中随处可见,掷骰子,抓麻将,或者统计一个月内帝都PM2.5的数值。测试
对于真随机发生器我我的认为将来是能够经过生物计算机来获取的。
2、伪随机数发生器
通 过程序获得的随机数不管什么算法都必定是经过递推公式获得的序列,这自己就违反了随机的定义,因此它们都不是真正的随机数。伪随机数中一个很重要的概念就 是“种子”,种子决定了随机数的固定序列,例如在C语言rand函数获得的序列每次都是相同的,若是想获得不一样序列须要调用srand设置种子;同理在 Java中 new Random(1)的构造函数参数来设置种子。下面介绍生成PRNG的几种常见方法:
1.取中法:
i.平方取中法:
这个方法是由冯·诺伊曼在1946年提出的,思想很简单:
选择一个m位数Ni做为种子,作平方运算(记为Ni+ 1 = (Ni * Ni)...),结果若不足2m个位,在前补0。在这个数选中间m个位的数做为Ni+1。这个算法明显又很大弊端,不只周期短并且分布不均匀,好比10000平方取中结果就一直为00000了。
- public class CustomRandom {
-
- static final int FIGURES = 10000;
- static long mRandom;
-
- public static void main(String[] args) {
- long seed = System.currentTimeMillis();
- mRandom = seed % FIGURES;
- for (int i = 0; i < 10; i++)
- System.out.println(getRandom(seed));
- }
-
- private static long getRandom(long seed) {
- return mRandom = (mRandom * mRandom / (long) Math.pow(10, 5/2)) % FIGURES;
- }
- }
ii:常数取中法
此方法与平方取中法稍有不一样,只是把一个随机数的平方换成了随机数与常数的乘积(记为Ni+1 = (K * Ni)...),对于随机分布等没有什么提高。
iii:乘法取中法:
此方法是对平方取中法的必定优化,公式记为Ni+1 = (Ni * Ni-1)...
2.同余法
同余是啥不知道的同窗见我《素性测试》中的wilson检测中有解释
同余法是大部分变成语言的RNG所采用的算法,线性同余方程为:Ni+1 = a Ni + C (mod m),其中a为乘子,C为增量,m为膜。产生的随机序列Rn = Ni / m。
当 a = 1 而且 C != 0时,此同余法称为加法同余法
当a != 1 而且 C = 0时,此同余法称为乘法同余法
当a != 1 而且 C != 0时,此同余法称为混合同余法
同余法当m越大,Ni的范围也就越大,随机分布的也就越均匀,Rn也就分布的更均匀,因此m取值应尽量的大,充分利用计算机字长。对于如何得到满周期随机数是存在断定定理的,当且仅当知足下列条件时,践行同余法是满周期的:
1.C与m互质
2.对于m的每个质因子p,(a-1)为p的倍数
3.若m可被4整除, (a-1)也可被4整除。
示例代码:
- public class CustomRandom {
-
- static final int A = 3;
- static final int M = (1 << 31) - 1 ;
-
- private static long mRandom;
-
- public static void main(String[] args) {
- mRandom = System.currentTimeMillis() / Integer.MAX_VALUE;
- for (int i = 0; i < 10; i++) {
- mRandom = (mRandom * A) % M;
- System.out.println(mRandom);
- }
- }
- }
除此以外还有二次同余,三次同余等,原理差很少。
因为计算机特有的逻辑移位运算,能够对种子N0左移n位获得M1,右移n位获得M2,将M1与M2作逻辑相加运算获得随机数N1,
公式为Ni+1 = Ni >> n + Ni << n.移位法速度很是快,但对初始值要求较高,很可贵到满意的随机序列。
示例代码:
- public class CustomRandom {
-
- static final int N = 5;
- static long mRandom;
-
- public static void main(String[] args) {
- long mRandom = System.currentTimeMillis();
- for (int i = 0; i < 10; i++) {
- mRandom = Math.abs((mRandom >> N) + (mRandom << N));
- System.out.println(mRandom);
- }
- }
- }
4.梅森旋转算法
梅森旋转算法是当此生成随机数质量最好的算法,如php,python,perl等流行编程语言内置的PRNG都是采用该算法实现。
梅森旋转算法(Mersenne twister)是一个伪随机数生成算法。由松本真和西村拓士在1997年开发,基于有限二进制字段上的矩阵线性地鬼
。能够快速产生高质量的伪随机数, 修正了古典随机数发生算法的不少缺陷。
下面的一段伪代码使用MT19937算法生成范围在[0, 232 − 1]的均匀分布的32位整数
- <span style="font-size:10px;"> //建立一个长度为624的数组来存储发生器的状态</span>
- int[0..623] MT
- int index = 0
-
- //用一个种子初始化发生器
- function initialize_generator(int seed) {
- i := 0
- MT[0] := seed
- for i from 1 to 623 { // 遍历剩下的每一个元素
- MT[i] := last 32 bits of(1812433253 * (MT[i-1] xor (right shift by 30 bits(MT[i-1]))) + i) // 0x6c078965
- }
- }
-
- // Extract a tempered pseudorandom number based on the index-th value,
- // calling generate_numbers() every 624 numbers
- function extract_number() {
- if index == 0 {
- generate_numbers()
- }
-
- int y := MT[index]
- y := y xor (right shift by 11 bits(y))
- y := y xor (left shift by 7 bits(y) and (2636928640)) // 0x9d2c5680
- y := y xor (left shift by 15 bits(y) and (4022730752)) // 0xefc60000
- y := y xor (right shift by 18 bits(y))
-
- index := (index + 1) mod 624
- return y
- }
-
- // Generate an array of 624 untempered numbers
- function generate_numbers() {
- for i from 0 to 623 {
- int y := (MT[i] & 0x80000000) // bit 31 (32nd bit) of MT[i]
- + (MT[(i+1) mod 624] & 0x7fffffff) // bits 0-30 (first 31 bits) of MT[...]
- MT[i] := MT[(i + 397) mod 624] xor (right shift by 1 bit(y))
- if (y mod 2) != 0 { // y is odd
- MT[i] := MT[i] xor (2567483615) // 0x9908b0df
- }
- }
- }
这里有完整的源码实现:http://www.cs.gmu.edu/~sean/research/mersenne/MersenneTwister.java
固然对于随机质量的好坏咱们要的是具备均匀性、独立性,周期性好的序列,对于随机数检测比较简单的方式能够输出在一张二维表上,直观的看出随机性的好坏,也能够采起《积分算法》中的蒙特卡洛方法来具体测试随机性;详细的检测能够采起x^2检测,k-s检测,poker检测等对随机性各个指标的具体检测这里就不细说了。
至此咱们只讨论了无规则分布和简单均匀分布,还有像拉普拉斯分布,正态分布,泊松分布,贝努里分布,高斯分布等高级分布本文就再也不讨论了;如今咱们再回到来头思考一个问题:世界上真存在真随机发生器吗?若是存在,假设时间倒流,再走一边,中彩票的会是另外一我的,仍是冥冥之中,自有天意,固然这只有上帝知道。
==================================================================================================
转载自nash_ :http://blog.csdn.net/zmazon/article/details/17383521
===================================================================================================