作者:Alon Zakai
編譯:胡子大哈
翻譯原文:http://huziketang.com/blog/posts/detail?postId=58cfc3dda6d8a07e449fdd29
英文原文:A Brief History of Random Numbers
** 轉載請注明出處,保留原文鏈接以及作者信息**

(羅馬 12mm 骰子,大英博物館便攜式文物保護方案-CC BY-SA 2.0)
“在所有的產生隨機數的事物中,我認為沒有什么能夠超越骰子了”,這是統計學家 Francis Galton 在 1890 年的《自然》雜志中寫道。它們在容器中不斷地翻滾、互相撞擊,以各種形式和角度與容器壁發生碰撞,在容器中的位置和形態在外界看來都是那么不可預知,容器哪怕只發生一次晃動,外界都不可能知道里面到底是什么形態。
古已有之的隨機數
到底如何才能生成均勻的隨機數列呢?自然界中隨機性大量而近乎完美的存在,人類并不能準確地預知和量化這種隨機性。迄今為止發現最早的骰子(4 個面)是來自中東的一座公元前 24 世紀的墳墓里。再近一些的歷史是在公元前 1100 年的中國,利用火燒龜殼產生的隨機龜裂現象,一些“先知”會根據龜裂情況來對未來做判斷。又過了幾個世紀,在中國誕生了易經占卜法,利用 49 蓍草法進行占卜,其操作的分裂過程很類似于拋硬幣。
機器生成隨機數的第一次觸碰

(摘自:“ A Million Random Digits with 100,000 Normal Deviates”)
時間到了 20 世紀 40 年代中期,現代世界需要更多的隨機數,不再是骰子或者蓍草可以滿足的了。RAND 公司發明了一種機器,通過隨機脈沖發生器可以生成大量的隨機數。他們將這個機器運行所產生的數字聚合起來并發布成圖書“A Million Random Digits with 100,000 Normal Deviates”。這在現在看來是十分荒謬的,但是在當時卻是一個突破。這是人類第一次產生如此大量的、高質量的隨機數,并且對公眾是開放的。這本書 RAND 公司一直印刷到了 2001 年,現在在亞馬遜上也可以看得到。
于此類似的機器:搖獎機,是由著名的 Bletchley Park WWII 破譯小組在 20 世紀 40 年代發明的,當時被用來生成英國保險債券彩票所使用的隨機數。為了平息公眾對搖獎機的公平性和準確性的質疑和擔心,官方斥資制作了當時的巨型紀錄片:“搖獎機的重要性(The Importance of Being E.R.N.I.E.)”。下面給出視頻,很值得一看。
<iframe frameborder="0" width="640" height="498" src="https://v.qq.com/iframe/player.html?vid=n0385z4te91&tiny=0&auto=0" allowfullscreen></iframe>
(The Importance of Being E.R.N.I.E.)
1951 年隨機性終于被正式規范化并且整合到了計算機 Ferranti Mark 1 號中。Ferranti Mark 1 號內置了隨機數生成指令,利用電氣噪聲可以一次性生成 20 個隨機比特位。這一特性是由阿蘭·圖靈設計的。Christopher Strachey 利用這一特點,編寫了一套隨機情書生成器。下面這是情書例子,利用這個程序生成的 David Link 的 2009 復合計劃:
JEWEL LOVE
MY LIKING HUNGERS FOR YOUR ADORABLE INFATUATION.
YOU ARE MY EROTIC ARDOUR.: MY FOND RAPTURE. MY THIRST
SIGHS FOR YOUR INFATUATION. MY HEART SEDUCTIVELY WISHES YOUR
BREATHLESS LONGING.
YOURS CURIOUSLY
M. U. C.
(由于上面文字過于漏骨,譯者嘗試引申出譯文如下)
我對你的可愛迷戀至極。
你勾起了我所有對情愛的幻想。
我為你而狂熱。
你的魅力使我對你充滿了渴望。
我的心隨你在而讓我無法呼吸。
你的追求者
M.U.C
但是圖靈的隨機數指令幾乎是當時的開發人員崩潰的,因為這種隨機在本身就已經很不穩定的開發環境下又引入了不確定性。人們希望在軟件中得到一致性的結果,但是用這種指令的軟件永遠不可能得到可重復的一致性結果,這也使得軟件測試幾乎變的不可行。
那么如果隨機數生成器可以由一個確定性的函數來替代會怎樣呢?如果在給定一個確定的初始條件,每次可以生成同樣的隨機序列會怎樣呢?這就是偽隨機數生成器(PRNG)。
偽隨機數生成器(PRNG)
偽隨機數生成器是由馮諾依曼在 1946 年創造的。他的基本思想是從一個隨機數種子開始,對其平方,然后取中間值。接下來重復對得到的數取平方并取中間值的過程,就會得到一個具有統計意義屬性的隨機數序列了。這也就是廣為人知的平方取中法。
然而,馮諾依曼的方法并沒有經得住時間的考驗,因為不論從什么隨機種子開始,序列最終都會落入某個短循環序列,比如:8100,6100,4100,8100,6100,4100……。
序列中的數字是依賴于前一個數字的這種生成函數,上面的重復循環問題是不可避免的。但是如果說這個循環間隔非常非常大,對實際應用并不會產生影響,那會怎樣呢?
1949 年,數學家 D.H.Lehmer 利用線性同余生成器(LCG)實現了這一思路。下面給出的是基于 Lehmer 的方法所實現的一種樸素 PRNG,叫做中央隨機數生成器,使用 JavaScript 在 1995 年寫的。
// The Central Randomizer 1.3 (C) 1997 by Paul Houle (paul@honeylocust.com)
// See: http://www.honeylocust.com/javascript/randomizer.html
rnd.today=new Date();
rnd.seed=rnd.today.getTime();
function rnd() {
rnd.seed = (rnd.seed*9301+49297) % 233280;
return rnd.seed/(233280.0);
};
function rand(number) {
return Math.ceil(rnd()*number);
};
注意代碼中的魔法數字(如 9301 等),這些數字(通常是質數)是用來最大化重復區間的——上面所提到的自我重復的循環區間。這種 PRNG 使用當前時間作為種子值,重復區間可以達到 2 的 31 次方。
這種中央隨機生成器發明之初非常流行,因為那時的 JavaScript 1.0 還沒有內置 Math.random()
函數,當時的 Web 1.0 環境下,大家都想讓自己的 banner 廣告隨機旋轉。一個開發者 Paul Houle 說道:“它在很多情況下已經很好用了,但是不能使用它來做保密使用”。
對 PRNG 的更高要求
互聯網確實需要保密。SSL 誕生在 1995 年,它的加密方案需要高質量的 PRNG。它的發展也直接導致了一段時間的 PRNG 野蠻創新時期。如果你回頭看一下所有的隨機數生成器的專利,你可能會感受到就像現代版的第一次制造飛機的浪潮一樣。
20 世紀 90 年代中期的 CPU 是沒有內置隨機數生成指令的,這使得那時候好的隨機種子特別難得。本來這問題也不大,不過當飛利浦的 Hallam-Baker 發現 Netscape(當時市場上的巨頭)的 SSL web 服務器使用了“當前時間 + 一組特殊 ID”組合作為種子的時候,這個問題變成了一個切身體會到的安全問題了。Hallam-Baker 展示了一個攻擊者很容易猜到種子值,并且對他們所拿到的服務器流量進行解密的過程。猜種子值是一個非常常規的攻擊手段,盡管這種手法現在變得越來越困難。這里給出 2009 年在 Hacker News 上的一段非常經典的攻擊演練。
到了 1997 年,計算機科學家們厭倦了生成隨機數所受限的條件,來自 SGI 的一個團隊發明了 LavaRand,它是用一個網絡攝像頭來對著熔巖燈拍照。從攝像頭中過來的圖片數據是一個真實的熵源——像圖靈那樣的真實隨機數生成器(TRNG)——可以以 165kb/s 的速率生成隨機數。一如當時硅谷的風格,熔巖燈平臺很快拿到了專利。
AutoDesk 的創始人 John Walker 在全世界范圍內推廣他的 HotBits,這是一種“隨機數即服務”的應用,背后原理是蓋革計數器來保證其量子隨機性。1998 年成立的 Random.org 為互聯網提供真正的隨機數。他們提供的服務包括真正的拋硬幣隨機、骰子隨機和卡牌洗牌隨機等。
上面所提到的大多數算法后來都無人問津了,但是一個叫做梅森旋轉隨機數生成器(The Mersenne Twister)的軟件 PRNG 鶴立雞群,它是由松本真(Makoto Matsumoto)和西村 拓士(Takuji Nishimura)在 1997 年發明的。它完美地平衡了性能和隨機數的質量,并且經受住了時間的考驗。其基本思想是基于線性反饋移位寄存器(LFSR),產生一個循環周期非常長的確定性序列,循環周期能夠達到 21??3?? 1。在當前的編程語言中,這種算法依舊是默認的 PRNG。
在 1999 年,隨機數市場發生了一個巨大的變化,Intel 在其 i810 芯片組上集成了芯片級的隨機數生成器。這樣使得新的服務器都自帶熱噪聲的本地源隨機數生成能力——真正的隨機數生成器(TRNG)。這很偉大,但是它始終沒有軟件 PRNG 快,所以加密軟件依舊不得不依賴于偽隨機數生成器(PRNG)。
這就把我們帶到了“密碼安全 PRNG”(CSPRNG)(這些討厭的縮寫!難怪很多人認為計算機科學很煩人)。CSPRNG 對于 SSL 特別重要。那么 CSPRNG 的原理是什么呢?這里有一份 131 頁的論文來介紹 CSPRNG。祝你在里面閱讀愉快。
不言而喻,CSPRNG 是一個強需求。梅森旋轉隨機數生成器并不是一種 CSPRNG,因為如果可以給定大量的先前序列樣本,后面的數字是可以預計的出來。
時間再拉近一些,2012 年,Intel 為 TRNG 增加了 RDRAND
和 RDSEED
指令,具有 500MB/s 的生產效率。但是 RDRAND
的完整性一直被質疑,里面是不是有某些缺陷?或者是為美國國家安全局內置了什么東西?沒人確切地知道這個問題的答案,我猜某些地方的某些人一定知道,可是他們也一定不會公開。
開源硬件隨機數生成器

(由一種硬件隨機數生成器 PEDOUBLER 生成的隨機數據)
近些年開源硬件 TRNG 也逐漸顯露頭角。它們廣受歡迎得益于其設計的透明化:你可以自己構建線路,也可以用現有的組件搭建。完全的透明化使得對硬件隨機數生成沒有任何的擔心和疑慮。REDOUBLER和無限噪聲 TRNG是兩個開源硬件隨機數生成器,鏈接中給出他們的 Github 源碼地址。
結尾
今天,依舊有關于對隨機數生成方法選擇的爭論,在操作系統內核、編程語言和安全包(如 OpenSSL 或者 OpenSSH)方面均未停止。有許多不同的算法聚焦于不同的特點上,如速度、占用空間、安全性等方面,也有一些安全專家依舊在尋找攻破已有算法的方法。但是對于我們日常的使用來講,在大多數的操作系統中你可以放心地使用 /dev/random
,或者編程語言中你可以隨心地使用 rand()
函數,都能給你帶來很好的使用體驗,并且你這么做,阿蘭·圖靈也會很開心。
歡迎大家關注我的前端大哈 - 知乎專欄,定期發布高質量前端文章。
我最近正在寫一本《React.js 小書》,對 React.js 感興趣的童鞋,歡迎指點。