2017-01-03 358 views
3

我們一直使用的Math.random得到4000-64000之間:如何使用window.crypto.getRandomValues獲得特定範圍內的隨機值

Math.floor(Math.random() * 60000 + 4000); 

我們必須現在替換這個隨機數一個更加密碼安全的隨機數發生器。在搜索這個問題後,我們決定使用 window.crypto.getRandomValues。我無法弄清楚如何使用它來獲得特定範圍內的隨機數。有人可以幫忙嗎?

+0

另請參閱http://dimitri.xyz/random-ints-from-random-bits/和https://crypto.stackexchange.com/questions/8826/map-bytes-to-number和https:// crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-random-octet-string – caw

回答

5

對於給定的最小值和最大值,公式u \cdot \left (1 - {2^u \boldsymbol{\textup{mod}} (max-min) \over 2^u} \right) \sum_{i=0}^{\infty} \left(2^u \boldsymbol{\textup{mod}} (max-min) \over 2^u \right)^i (i + 1)描述瞭如果一次請求u位,平均使用多少位,如果返回結果會導致偏差,則重試。

幸運的是,最佳策略是簡單地請求ceil(log2(max - min + 1))位。我們只能得到充分字節與crypto.getRandomValues反正,所以如果我們有每個函數調用的crypto.getRandomValues一個電話,我們能做的最好的是:

// Generate a random integer r with equal chance in min <= r < max. 
function randrange(min, max) { 
    var range = max - min; 
    if (range <= 0) { 
     throw new Exception('max must be larger than min'); 
    } 
    var requestBytes = Math.ceil(Math.log2(range)/8); 
    if (!requestBytes) { // No randomness required 
     return min; 
    } 
    var maxNum = Math.pow(256, requestBytes); 
    var ar = new Uint8Array(requestBytes); 

    while (true) { 
     window.crypto.getRandomValues(ar); 

     var val = 0; 
     for (var i = 0;i < requestBytes;i++) { 
      val = (val << 8) + ar[i]; 
     } 

     if (val < maxNum - maxNum % range) { 
      return min + (val % range); 
     } 
    } 
} 

如果產生許多值,你可以考慮一些優化,即請求提前多個字節(即更大的數組)。如果你的範圍變小(比如說你想翻轉一個硬幣),那麼比以比特爲基礎的方式工作也是有益的,例如,先前請求多個比特,然後只用盡你需要的隨機比特。

+0

這真的很酷,但我注意到有點像'randrange(0,1)'無限循環' 。爲什麼? – Deele

+0

@Deele我很抱歉,如果'max - min == 1'有一個錯誤 - 之前的代碼沒有請求隨機性,然後下一步沒有任何意義。我添加了一個檢查來捕獲這個特殊情況。請注意''randrange(0,1)'總是返回0. – phihag

+0

您將如何增加數組大小(對於您的示例)以及大約應該在什麼條件下執行? – Deele

相關問題