1. 程式人生 > 實用技巧 >JS隨機數生成演算法淺析:9301、49297、233380做基數的原因

JS隨機數生成演算法淺析:9301、49297、233380做基數的原因

  見到這個隨機數生成演算法好幾次了,乍看有點雞肋,本來用Math.random()就可以的事,想不清楚為什麼他要用9301,49297,233280這三個數字?其中有道理嗎?還是僅是隨意選的三個數?但是這個組合貌似流傳很廣,好多網站原始碼裡都見到過。

// 生成隨機數
export function generateRandom (number) {
  function rnd (seed) {
    seed = ( seed * 9301 + 49297 ) % 233280
    return seed / ( 233280.0 )
  }
  let today = new Date()
  let seed 
= today.getTime() return Math.ceil(rnd(seed) * number) }

  很多人認為這是簡單的Magic Number,其實這背後有內在的原因,這三個數字並不是隨便亂選出來的。

1、入門級的選擇標準

  這種偽隨機數生成器叫做線性同餘生成器(LCG, Linear Congruential Generator),幾乎所有的執行庫提供的rand都是採用的LCG,形如:

  生成的偽隨機數序列最大週期m,範圍在0到m-1之間。要達到這個最大週期,必須滿足

  • c與m互質
  • a - 1可以被m的所有質因數整除
  • 如果m是4的倍數,a - 1也必須是4的倍數

  以上三條被稱為Hull-Dobell定理。

  作為一個偽隨機數生成器,週期不夠大是不好意思混的,所以這是要求之一。可以看到,a=9301, c = 49297, m = 233280這組引數,以上三條全部滿足。

2、進階級的選擇標準

  要在偽隨機數生成器界混,僅僅入門是不夠的。

  從工程的角度來講,的值要(在合理的範圍內)足夠小,以避免溢位的問題。

  從安全(實用)性的角度來講,還要滿足良好的隨機性,這一點可以通過Knuth's Spectral Test來評估(見[2][3]),要通過2,3,4,5以及6維的Spectral Test才行。

  Spectral Test考察的就是生成的偽隨機數序列在超空間的網格結構(lattice structure),當年IBM的RANDU子程式鬧出的烏龍,連3維的Spectral Test就不能通過。

  其中每個點代表三個連續的RANDU生成的偽隨機數值,可以看到所有偽隨機數分佈在了15個二維平面上。在這種要求面前,c的值最好:

  • 是質數 (c = 49297就是質數)
  • 接近,(m = 233280時為49297.86460172205)

  所以有了這樣一些基本的標準,能夠選擇的引數範圍就小了很多,弄個程式跑下Spectral Test,就能得到可選的引數組。

  如果想要更加詳盡的瞭解LCG偽隨機數生成器的性質以及引數選取、測試的數學理論,可以嘗試閱讀《計算機程式設計藝術》卷2第3章。