簡體   English   中英

Math.random() 如何在 javascript 中工作?

[英]How does Math.random() work in javascript?

我最近想出了如何通過 google 獲取隨機數,這讓我開始思考Math.random()如何工作的。 所以我在這里我無法弄清楚他們是如何做 Math.random() 的,除非他們使用了類似時間的東西 有沒有人知道 JavaScript 的Math.random()是如何工作的或等效的?

Math.random() 返回一個帶有正號的數字值,大於或等於 0 但小於 1,使用依賴於實現的算法或策略隨機或偽隨機選擇並在該范圍內近似均勻分布。

這是 V8 的實現:

uint32_t V8::Random() {

    // Random number generator using George Marsaglia's MWC algorithm.
    static uint32_t hi = 0;
    static uint32_t lo = 0;

    // Initialize seed using the system random(). If one of the seeds
    // should ever become zero again, or if random() returns zero, we
    // avoid getting stuck with zero bits in hi or lo by reinitializing
    // them on demand.
    if (hi == 0) hi = random();
    if (lo == 0) lo = random();

    // Mix the bits.
    hi = 36969 * (hi & 0xFFFF) + (hi >> 16);
    lo = 18273 * (lo & 0xFFFF) + (lo >> 16);
    return (hi << 16) + (lo & 0xFFFF);
}

來源: http : //dl.packetstormsecurity.net/papers/general/Google_Chrome_3.0_Beta_Math.random_vulnerability.pdf

以下是 StackOverflow 上的幾個相關線程:

請參閱: 有 Math.random(),然后有 Math.random()

直到最近(直到版本 4.9.40),V8 對 PRNG 的選擇還是 MWC1616(乘以進位,結合兩個 16 位部分)。 它使用 64 位內部狀態,大致如下所示:

uint32_t state0 = 1;
uint32_t state1 = 2;
uint32_t mwc1616() {
  state0 = 18030 * (state0 & 0xffff) + (state0 >> 16);
  state1 = 30903 * (state1 & 0xffff) + (state1 >> 16);
  return state0 << 16 + (state1 & 0xffff);

然后根據規范將 32 位值轉換為介於 0 和 1 之間的浮點數。

MWC1616 使用很少的內存並且計算速度非常快,但不幸的是提供低於標准的質量:

  • 它可以生成的隨機值的數量限制為 232,而不是雙精度浮點可以表示的 0 和 1 之間的 252 個數字。
  • 結果的更重要的上半部分幾乎完全取決於 state0 的值。 周期長度最多為 232,但不是幾個大的置換周期,而是許多短的置換周期。 如果初始狀態選擇不當,循環長度可能小於 4000 萬。
  • 它未能通過 TestU01 套件中的許多統計測試。

已經向我們指出了這一點,並且了解了問題並經過一些研究后,我們決定基於稱為 xorshift128+ 的算法重新實現 Math.random。 它使用 128 位內部狀態,周期長度為 2^128 - 1,並通過了 TestU01 套件的所有測試。

uint64_t state0 = 1;
uint64_t state1 = 2;
uint64_t xorshift128plus() {
  uint64_t s1 = state0;
  uint64_t s0 = state1;
  state0 = s0;
  s1 ^= s1 << 23;
  s1 ^= s1 >> 17;
  s1 ^= s0;
  s1 ^= s0 >> 26;
  state1 = s1;
  return state0 + state1;
}

在我們意識到這個問題的幾天內,新的實現就登陸 V8 4.9.41.0。 它將在 Chrome 49 中可用。Firefox 和 Safari 也都切換到 xorshift128+。

他們使用“類似時間的東西”是正確的。 偽隨機生成器通常使用系統時鍾作為種子,因為這是一個不總是相同的數字的良好來源。

一旦隨機生成器被植入一個數字,它將生成一系列數字,這些數字都取決於初始值,但它們看起來是隨機的。

一個簡單的隨機生成器(之前在編程語言中實際使用過)是在這樣的算法中使用素數:

rnd = (rnd * 7919 + 1) & 0xffff;

這將產生一系列來回跳躍的數字,看似隨機。 例如:

seed = 1337
36408
22089
7208
63833
14360
11881
41480
13689
6648

Javascript 中的隨機生成器稍微復雜一些(以提供更好的分布)並使用更大的數字(因為它必須生成一個大約 60 位而不是 16 位的數字),但它遵循相同的基本原則。

您可能需要這篇文章作為參考: https : //hackernoon.com/how-does-javascripts-math-random-generate-random-numbers-ef0de6a20131

順便說一句,最近我也很好奇這個問題,然后閱讀了NodeJS的源代碼。 我們可以從 Google V8 中知道一種可能的實現:

隨機的主要入口( MathRandom::RefillCache函數): https : //github.com/v8/v8/blob/master/src/math-random.cc

種子是如何初始化的? 另見此處: https : //github.com/v8/v8/blob/master/src/base/utils/random-number-generator.cc#L31

關鍵函數是( XorShift128函數): https : //github.com/v8/v8/blob/master/src/base/utils/random-number-generator.h#L119

在這個頭文件中,引用了一些論文:

// See Marsaglia: http://www.jstatsoft.org/v08/i14/paper
// And Vigna: http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf
<script>

function generateRandom(){  //  Generate and return a random number
    var num = Math.random();
    num = (Math.round((num*10)))%10;
    return num;
}

function generateSum(){ //  Generate a problem
    document.getElementById("ans").focus();
    var num1 = generateRandom();
    var num2 = generateRandom();
    document.getElementById("num1").innerHTML = num1;
    document.getElementById("num2").innerHTML = num2;
    document.getElementById("pattern1").innerHTML = printPattern(num1);
    document.getElementById("pattern2").innerHTML = printPattern(num2);

}

function printPattern(num){ //  Generate the star pattern with 'num' number of stars
    var pattern = "";
    for(i=0; i<num; i++){
        if((i+1)%4 == 0){
            pattern = pattern+"*<br>";
        }
        else{
            pattern = pattern+"*";
        }
    }
    return pattern;
}

function checkAns(){    //  Check the answer and give the response
    var num1 = parseInt(document.getElementById("num1").innerHTML);
    var num2 = parseInt(document.getElementById("num2").innerHTML);
    var enteredAns = parseInt(document.getElementById("ans").value);
    if ((num1+num2) == enteredAns){
        document.getElementById("patternans").innerHTML = printPattern(enteredAns);
        document.getElementById("patternans").innerHTML += "<br>Correct";
    }
    else{
        document.getElementById("patternans").innerHTML += "Wrong";
        //remove + mark to remove the error

    }
}

function newSum(){
    generateSum();
    document.getElementById("patternans").innerHTML = "";
    document.getElementById("ans").value = "";
}

</script>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM