JavaScript根據種子生成隨機數實現方法

在前端開發中,尤其是遊戲開發,經常會用到隨機數,那麼我們會第一時間想到:Math.random,大家略微的看看如下代碼:

for (var i= 0; i<10; i++) { document.writeln(Math.random() +"<br />"); }

運行如上代碼,也確實生成了10個不同的數字,當然你可以生成更多,看起來挺不錯的,如果僅僅如此,那麼本文就沒必要寫了。

試着想一下,如果在某一個場景,我們做一個遊戲,用戶玩到一半的時候退出了,這樣用戶下次進來可以選擇繼續上一次的進度繼續玩,那麼現在問題來了:用戶玩的進度以及用戶的積分等簡單的描述數據,我們都可以記錄下來,但是遊戲裏繪製的障礙物、飛行物以及很多裝飾類的小玩意兒,他們甚至是每次用戶點開始隨機輸出的,要把畫布上所有的東西以及它們的大小,位置等都記錄下來,實在是沒必要。

於是種子隨機數就閃亮登場了,我們如果在畫布上元素隨機繪製的時候,有一個種子值,頁面上所有元素的位置、大小等都是根據這個種子來算的,那麼等到第二次繪製的時候只需要傳入這個種子,就可以重現之前未完成的畫布元素。

那麼這個時候,你會發現JS裏面自帶的 Math.random 就不好使了,無法滿足需求,我們繼續看這段代碼:

Math.seed = 5;
Math.seededRandom = function(max, min) {
    max = max || 1;
    min = min || 0;

    Math.seed = (Math.seed * 9301 + 49297) % 233280;
    var rnd = Math.seed / 233280.0;

    return Math.ceil( min + rnd * (max - min) );   // Math.ceil實現取整功能,可以根據需要取消取整
 };

運行如上代碼你會發現如果種子 Math.seed 不變,那麼生成的隨機數是不會變化的,O了,如果引入這個函數,那麼重現遊戲場景可以實現了,雖然還需要做更多的細節處理,但機制上是能保證的,本文的重點不是實現一個這樣的遊戲。

本文的重點是:(Math.seed * 9301 + 49297) % 233280,爲什麼會是這三個值,而不是其它的到底這三個數字有什麼神祕的來歷呢?

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

In+1=aIn+c(mod m)

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

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

以上三條被稱爲Hull-Dobell定理。作爲一個僞隨機數生成器,週期不夠大是不好意思混的,所以這是要求之一。因此纔有了:a=9301, c = 49297, m = 233280這組參數,以上三條全部滿足。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章