隨機數發生器


淺談隨機數發生器

 

      今 天在微博上到一篇如何使用隨機數的文章,讓我回憶起剛上大一時學C語言時,書后有道調用rand()函數的練習題,當時覺得好神奇,想知道它是怎么實現 的,大二時候學Java又遇到了random()函數,恰巧當時上機課我有機會問老師,遺憾的是老師只是告訴我那是偽隨機數,課后查查資料才了解。如今來 一篇關於隨機數發生器博文來回憶一下神奇的隨機數。

     眾所周知,我們平時所使用的無論什么編程語言都會提供一個隨機數函數,而且它是偽隨機數(Pseudo Random Number),它是由算法計算得出的,是可以預測的,也就是說當隨機種子相同時,對於同一個隨機函數,得出的隨機數列是固定不變的,亞裔唯一圖靈獎得主姚期智就是研究的就是偽隨機數生成論;與之對應的就是真隨機數(True Random Number)它是真正的隨機數,無法預測且無周期性;還有一種是產生隨機數的發生器是密碼學偽隨機數發生器(Cryptographically Secure Pseudo-Random Number Generator)常用的算法有 MD5 ,SHA1 等標准, 這里不做過多討論,說說最基本的前兩種:


一、真隨機數發生器

    像無法實現永動機一樣,想要實現真隨機數靠程序是永遠無法實現的,很多情況下只能看老天的眼色,比如布朗運動,量子效應,放射性衰變等。第一個真隨機數發生器是1955年由Rand公司創造的,而在1999年,intel發布Intel810芯片組時,就配備了硬件隨機數發生器,基於IntelRNG的真隨機數生成器可以生成滿足獨立性和分布均勻性的真隨機數,目前大部分芯片廠商都集成了硬件隨機數發生器,只要安裝相應驅動,了解讀取寄存器地址,可以直接調用發生器。Intel810RNG的原理大概是:利用熱噪聲(是由導體中電子的熱震動引起的)放大后,影響一個由電壓控制的振盪器,通過另一個高頻振盪器來收集數據。TRNG的類型主要有:

1.基於電路的TRNG:

 

i.振盪器采樣:就是上述Intel采用的方式。

ii.直接放大電路噪聲:利用電路中各種噪聲,如上述的熱噪聲作為隨機源,由於強度小,所以先要對其放大,然后對一定時間內超過閾值的數據進行統計,這樣就產生的隨機數。.

iii.電路亞穩態:亞穩態表示觸發器無法在規定時間內達到一個可確認狀態,一定條件下,觸發器達到兩個穩態的幾率為50%,所以先使電路進入亞穩態,之后根據狀態轉化為隨機數。

iv.混沌電路:不可預測,對初始條件的敏感的依賴性。以及混沌電路在芯片中易於實現的特點,可以產生效果不錯的隨機數。

2.基於其他物理源的TRNG

如宇宙射線,粒子衰變,空氣噪聲等作為隨機源,來產生隨機數。

3.其他物理信息TRNG

人為可以產生隨機數嗎?當然能!聽說一個HR拆選簡歷的方式是往天上一扔,掉在桌子上的簡歷就通過,這個HR確認懂隨機啊,而且是真隨機。這類隨機生活中隨處可見,擲骰子,抓麻將,或者統計一個月內帝都PM2.5的數值。

對於真隨機發生器我個人認為未來是可以通過生物計算機來獲取的。


二、偽隨機數發生器

    通 過程序得到的隨機數無論什么算法都一定是通過遞推公式得到的序列,這本身就違反了隨機的定義,所以它們都不是真正的隨機數。偽隨機數中一個很重要的概念就 是“種子”,種子決定了隨機數的固定序列,例如在C語言rand函數得到的序列每次都是相同的,如果想得到不同序列需要調用srand設置種子;同理在 Java中 new Random(1)的構造函數參數來設置種子。下面介紹生成PRNG的幾種常見方法:

1.取中法:


i.平方取中法:

這個方法是由馮·諾伊曼在1946年提出的,思想很簡單:

選擇一個m位數Ni作為種子,做平方運算(記為Ni+ 1 = (Ni * Ni)...),結果若不足2m個位,在前補0。在這個數選中間m個位的數作為Ni+1。這個算法明顯又很大弊端,不僅周期短而且分布不均勻,比如10000平方取中結果就一直為00000了。
在CODE上查看代碼片派生到我的代碼片
  1. public class CustomRandom {  
  2.       
  3.     static final int FIGURES = 10000;  
  4.     static long mRandom;  
  5.       
  6.     public static void main(String[] args) {  
  7.         long seed = System.currentTimeMillis();  
  8.         mRandom = seed % FIGURES;  
  9.         for (int i = 0; i < 10; i++)  
  10.             System.out.println(getRandom(seed));  
  11.     }  
  12.   
  13.     private static long getRandom(long seed) {  
  14.         return mRandom = (mRandom * mRandom / (long) Math.pow(10, 5/2)) % FIGURES;  
  15.     }  
  16. }  

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整除。
示例代碼:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. public class CustomRandom {  
  2.       
  3.     static final int A = 3;  
  4.     static final int M = (1 << 31) - 1 ;  
  5.       
  6.     private static long mRandom;  
  7.       
  8.     public static void main(String[] args) {  
  9.         mRandom = System.currentTimeMillis() / Integer.MAX_VALUE;  
  10.         for (int i = 0; i < 10; i++) {  
  11.             mRandom = (mRandom * A) % M;  
  12.             System.out.println(mRandom);  
  13.         }  
  14.     }  
  15. }  
除此之外還有二次同余,三次同余等,原理差不多。

3.移位法:

由於計算機特有的邏輯移位運算,可以對種子N0左移n位得到M1,右移n位得到M2,將M1與M2做邏輯相加運算得到隨機數N1,
公式為Ni+1 = Ni  >> n + Ni << n.移位法速度非常快,但對初始值要求較高,很難得到滿意的隨機序列。
示例代碼:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. public class CustomRandom {  
  2.       
  3.     static final int N = 5;  
  4.     static long mRandom;  
  5.       
  6.     public static void main(String[] args) {  
  7.         long mRandom = System.currentTimeMillis();  
  8.         for (int i = 0; i < 10; i++) {  
  9.             mRandom = Math.abs((mRandom >> N) + (mRandom << N));  
  10.             System.out.println(mRandom);  
  11.         }  
  12.     }  
  13. }  

4.梅森旋轉算法

梅森旋轉算法是當今生成隨機數質量最好的算法,如php,python,perl等流行編程語言內置的PRNG都是采用該算法實現。

梅森旋轉算法(Mersenne twister)是一個偽隨機數生成算法。由松本真和西村拓士在1997年開發,基於有限二進制字段上的矩陣線性地鬼F_{2}。可以快速產生高質量的偽隨機數, 修正了古典隨機數發生算法的很多缺陷。

 

下面的一段偽代碼使用MT19937算法生成范圍在[0, 232 − 1]的均勻分布的32位整數

[plain] view plain copy 在CODE上查看代碼片派生到我的代碼片
  1. <span style="font-size:10px;"> //創建一個長度為624的數組來存儲發生器的狀態</span>  
  2.  int[0..623] MT  
  3.  int index = 0  
  4.    
  5.  //用一個種子初始化發生器  
  6.  function initialize_generator(int seed) {  
  7.      i := 0  
  8.      MT[0] := seed  
  9.      for i from 1 to 623 { // 遍歷剩下的每個元素  
  10.          MT[i] := last 32 bits of(1812433253 * (MT[i-1] xor (right shift by 30 bits(MT[i-1]))) + i) // 0x6c078965  
  11.      }  
  12.  }  
  13.    
  14.  // Extract a tempered pseudorandom number based on the index-th value,  
  15.  // calling generate_numbers() every 624 numbers  
  16.  function extract_number() {  
  17.      if index == 0 {  
  18.          generate_numbers()  
  19.      }  
  20.    
  21.      int y := MT[index]  
  22.      y := y xor (right shift by 11 bits(y))  
  23.      y := y xor (left shift by 7 bits(y) and (2636928640)) // 0x9d2c5680  
  24.      y := y xor (left shift by 15 bits(y) and (4022730752)) // 0xefc60000  
  25.      y := y xor (right shift by 18 bits(y))  
  26.   
  27.      index := (index + 1) mod 624  
  28.      return y  
  29.  }  
  30.    
  31.  // Generate an array of 624 untempered numbers  
  32.  function generate_numbers() {  
  33.      for i from 0 to 623 {  
  34.          int y := (MT[i] & 0x80000000)                       // bit 31 (32nd bit) of MT[i]  
  35.                         + (MT[(i+1) mod 624] & 0x7fffffff)   // bits 0-30 (first 31 bits) of MT[...]  
  36.          MT[i] := MT[(i + 397) mod 624] xor (right shift by 1 bit(y))  
  37.          if (y mod 2) != 0 { // y is odd  
  38.              MT[i] := MT[i] xor (2567483615) // 0x9908b0df  
  39.          }  
  40.      }  
  41.  }  
這里有完整的源碼實現:http://www.cs.gmu.edu/~sean/research/mersenne/MersenneTwister.java

當然對於隨機質量的好壞我們要的是具有均勻性、獨立性,周期性好的序列,對於隨機數檢測比較簡單的方式可以輸出在一張二維表上,直觀的看出隨機性的好壞,也可以采取《積分算法》中的蒙特卡洛方法來具體測試隨機性;詳細的檢測可以采取x^2檢測,k-s檢測,poker檢測等對隨機性各個指標的具體檢測這里就不細說了。
    至此我們只討論了無規則分布和簡單均勻分布,還有像拉普拉斯分布,正態分布,泊松分布,貝努里分布,高斯分布等高級分布本文就不再討論了;現在我們再回到來頭思考一個問題:世界上真存在真隨機發生器嗎?如果存在,假設時間倒流,再走一邊,中彩票的會是另一個人,還是冥冥之中,自有天意,當然這只有上帝知道。

 

==================================================================================================

  轉載自nash_ http://blog.csdn.net/zmazon/article/details/17383521

===================================================================================================

 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM