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

IT技术 javascript math
2021-03-03 09:59:01

我最近想出了如何通过 google 获取随机数,这让我开始思考它是如何Math.random()工作的。所以我在这里我无法弄清楚他们是如何做 Math.random() 的,除非他们使用了类似的时间 有谁知道 JavaScript 是如何Math.random()工作的或等效的?

5个回答

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 上的几个相关线程:

这是旧的实现,请参阅stackoverflow.com/a/52191522/58961了解最新版本。
2021-04-28 09:59:01

请参阅:有 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 位的数字),但它遵循相同的基本原则。

你能提供一个来源吗?
2021-05-04 09:59:01

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

顺便说一句,最近我也很好奇这个问题,然后阅读了NodeJS的源代码。我们可以从 Google V8 中知道一种可能的实现:

random(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>