今 天在微博上到一篇如何使用隨機數的文章,讓我回憶起剛上大一時學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
===================================================================================================