在 Javascript 中播種隨機數產生器
P粉006977956
P粉006977956 2023-08-23 23:03:23
0
2
564
<p>是否可以在 JavaScript 中為隨機數產生器 (<code>Math.random</code>) 提供種子? </p>
P粉006977956
P粉006977956

全部回覆(2)
P粉482108310

不,不可能播種 Math.random()ECMAScript 規範在這個主題上故意含糊不清,沒有提供播種方法,也沒有要求瀏覽器甚至使用相同的演算法。所以這樣的功能必須由外部提供,幸運的是這並不太困難。

我用純 JavaScript 實作了許多優秀、簡短且快速的偽隨機數產生器 (PRNG) 函數。所有這些都可以播種並提供高品質的數字。這些並非出於安全目的 - 如果您需要可種子的 CSPRNG,請查看ISAAC。 p>

首先,請注意正確初始化您的 PRNG。 為了簡單起見,下面的生成器沒有內建種子產生過程,但接受一個或多個 32 位元數字作為PRNG 的初始種子狀態。相似或稀疏的種子(例如 1 和 2 的簡單種子)具有低熵,並且可能導致相關性或其他隨機性品質問題,有時會導致輸出具有相似的屬性(例如隨機生成的等級相似)。為了避免這種情況,最好的做法是使用分佈均勻的高熵種子和/或前進到超過前 15 個左右的數字來初始化 PRNG。

有很多方法可以做到這一點,但這裡有兩種方法。首先,雜湊函數非常擅長從短字串產生種子。即使兩個字串相似,一個好的雜湊函數也會產生非常不同的結果,因此您不必對字串投入太多考慮。這是一個哈希函數範例:

function cyrb128(str) {
    let h1 = 1779033703, h2 = 3144134277,
        h3 = 1013904242, h4 = 2773480762;
    for (let i = 0, k; i < str.length; i++) {
        k = str.charCodeAt(i);
        h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
        h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
        h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
        h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
    }
    h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
    h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
    h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
    h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
    h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1;
    return [h1>>>0, h2>>>0, h3>>>0, h4>>>0];
}

呼叫cyrb128將從字串中產生一個128位元雜湊值,該值可用於種子PRNG。以下是您可以如何使用它:

// Create cyrb128 state:
var seed = cyrb128("apples");
// Four 32-bit component hashes provide the seed for sfc32.
var rand = sfc32(seed[0], seed[1], seed[2], seed[3]);

// Only one 32-bit component hash is needed for mulberry32.
var rand = mulberry32(seed[0]);

// Obtain sequential random numbers like so:
rand();
rand();

注意:如果您想要稍微更強大的 128 位元哈希,請考慮 MurmurHash3_x86_128,它更徹底,但旨在用於大型陣列。

或者,只需選擇一些虛擬資料來填充種子,並預先將生成器推進幾次(12-20 次迭代)以徹底混合初始狀態。這樣做的好處是更簡單,並且經常用於 PRNG 的參考實作中,但它確實限制了初始狀態的數量:

var seed = 1337 ^ 0xDEADBEEF; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
var rand = sfc32(0x9E3779B9, 0x243F6A88, 0xB7E15162, seed);
for (var i = 0; i < 15; i++) rand();

注意:這些PRNG 函數的輸出會產生一個正32 位元數字(0 到232-1),然後將其轉換為0-1 之間的浮點數(包括0) , 1 除外)相當於Math.random(),如果您想要特定範圍的隨機數,請閱讀MDN 上的這篇文章。如果您只想要原始位,只需刪除最後的除法運算即可。

JavaScript 數字只能表示最高 53 位元解析度的整數。當使用位元運算時,這個值會減少到 32。其他語言中的現代 PRNG 通常使用 64 位元運算,這在移植到 JS 時需要填充程序,這會大大降低效能。這裡的演算法只使用32位元操作,因為它直接相容於JS。


sfc32(簡單快速計數器)

sfc32PractRand 隨機數測試套件的一部分(它當然通過)。 sfc32 具有 128 位元狀態,並且在 JS 中速度非常快。

function sfc32(a, b, c, d) {
    return function() {
      a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0; 
      var t = (a + b) | 0;
      a = b ^ b >>> 9;
      b = c + (c << 3) | 0;
      c = (c << 21 | c >>> 11);
      d = d + 1 | 0;
      t = t + d | 0;
      c = c + t | 0;
      return (t >>> 0) / 4294967296;
    }
}

您可能想知道 | 是什麼? 0>>>= 0 是用於。這些本質上是 32 位元整數轉換,用於效能最佳化。 JS 中的 Number 基本上都是浮點數,但是在位元運算時,它們會切換到 32 位元整數模式。這種模式被 JS 解譯器處理得更快,但任何乘法或加法都會導致它切換回浮點數,從而導致效能下降。

桑樹32

Mulberry32 是一個具有32 位元狀態的簡單生成器,但速度極快且具有良好的隨機性(作者表示它通過了gjrand 測試套件,有完整的232 週期,但我尚未驗證)。

function mulberry32(a) {
    return function() {
      var t = a += 0x6D2B79F5;
      t = Math.imul(t ^ t >>> 15, t | 1);
      t ^= t + Math.imul(t ^ t >>> 7, t | 61);
      return ((t ^ t >>> 14) >>> 0) / 4294967296;
    }
}

如果您只需要一個簡單但體面的 PRNG 並且不需要數十億個隨機數,我會推薦這個(請參閱生日問題)。

xoshiro128**

截至2018 年5 月,xoshiro128** 的新成員Xorshift 系列,由Vigna 和Blackman 開發(Vigna 教授也負責Xorshift128 演算法,為大多數 Math.random 實作提供支援)。它是提供 128 位元狀態的最快生成器。

function xoshiro128ss(a, b, c, d) {
    return function() {
        var t = b << 9, r = b * 5; r = (r << 7 | r >>> 25) * 9;
        c ^= a; d ^= b;
        b ^= c; a ^= d; c ^= t;
        d = d << 11 | d >>> 21;
        return (r >>> 0) / 4294967296;
    }
}

作者聲稱它很好地通過了隨機性測試(儘管有警告)。其他研究人員指出,它未通過 TestU01 中的一些測試(特別是 LinearComp 和 BinaryRank)。實際上,當使用浮點數時(例如在這些實作中),它不應該導致問題,但如果依賴原始最低階位,則可能會導致問題。

JSF(詹金斯的小快)

這是 Bob Jenkins (2007) 的第一個 JSF 或「smallprng」。 和 幽靈哈希。它通過 PractRand 測試並且應該雖然沒有 sfc32 那麼快,但相當快。

function jsf32(a, b, c, d) {
    return function() {
        a |= 0; b |= 0; c |= 0; d |= 0;
        var t = a - (b << 27 | b >>> 5) | 0;
        a = b ^ (c << 17 | c >>> 15);
        b = c + d | 0;
        c = d + t | 0;
        d = a + t | 0;
        return (d >>> 0) / 4294967296;
    }
}
P粉596161915

不,不可能播種 Math.random(),但寫自己的生成器相當容易,或者更好的是,使用現有的生成器。

檢視:此相關問題

此外,請參閱 David Bau 的博客,以了解有關播種的更多資訊.

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!