简体   繁体   English

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

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

I recently figured out how to get a random number via google, and it got me thinking how does Math.random() work.我最近想出了如何通过 google 获取随机数,这让我开始思考Math.random()如何工作的。 So here I am I can not figure out how they did Math.random() unless they used a time like thing does anyone know how JavaScript's Math.random() works or an equivalent?所以我在这里我无法弄清楚他们是如何做 Math.random() 的,除非他们使用了类似时间的东西 有没有人知道 JavaScript 的Math.random()是如何工作的或等效的?

Math.random() returns a Number value with a positive sign, greater than or equal to 0 but less than 1, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-dependent algorithm or strategy. Math.random() 返回一个带有正号的数字值,大于或等于 0 但小于 1,使用依赖于实现的算法或策略随机或伪随机选择并在该范围内近似均匀分布。

Here's V8's implementation:这是 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);
}

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

Here are a couple of related threads on StackOverflow:以下是 StackOverflow 上的几个相关线程:

See: There's Math.random(), and then there's Math.random()请参阅: 有 Math.random(),然后有 Math.random()

Until recently (up to version 4.9.40), V8's choice of PRNG was MWC1616 (multiply with carry, combining two 16-bit parts).直到最近(直到版本 4.9.40),V8 对 PRNG 的选择还是 MWC1616(乘以进位,结合两个 16 位部分)。 It uses 64 bits of internal state and looks roughly like this:它使用 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);

The 32-bit value is then turned into a floating point number between 0 and 1 in agreement with the specification.然后根据规范将 32 位值转换为介于 0 和 1 之间的浮点数。

MWC1616 uses little memory and is pretty fast to compute, but unfortunately offers sub-par quality: MWC1616 使用很少的内存并且计算速度非常快,但不幸的是提供低于标准的质量:

  • The number of random values it can generate is limited to 232 as opposed to the 252 numbers between 0 and 1 that double precision floating point can represent.它可以生成的随机值的数量限制为 232,而不是双精度浮点可以表示的 0 和 1 之间的 252 个数字。
  • The more significant upper half of the result is almost entirely dependent on the value of state0.结果的更重要的上半部分几乎完全取决于 state0 的值。 The period length would be at most 232, but instead of few large permutation cycles, there are many short ones.周期长度最多为 232,但不是几个大的置换周期,而是许多短的置换周期。 With a badly chosen initial state, the cycle length could be less than 40 million.如果初始状态选择不当,循环长度可能小于 4000 万。
  • It fails many statistical tests in the TestU01 suite.它未能通过 TestU01 套件中的许多统计测试。

This has been pointed out to us, and having understood the problem and after some research, we decided to reimplement Math.random based on an algorithm called xorshift128+.已经向我们指出了这一点,并且了解了问题并经过一些研究后,我们决定基于称为 xorshift128+ 的算法重新实现 Math.random。 It uses 128 bits of internal state, has a period length of 2^128 - 1, and passes all tests from the TestU01 suite.它使用 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;
}

The new implementation landed in V8 4.9.41.0 within a few days of us becoming aware of the issue.在我们意识到这个问题的几天内,新的实现就登陆 V8 4.9.41.0。 It will become available with Chrome 49. Both Firefox and Safari switched to xorshift128+ as well.它将在 Chrome 49 中可用。Firefox 和 Safari 也都切换到 xorshift128+。

It's correct that they use a "time like thing".他们使用“类似时间的东西”是正确的。 A pseudo random generator is typically seeded using the system clock, because that is a good source of a number that isn't always the same.伪随机生成器通常使用系统时钟作为种子,因为这是一个不总是相同的数字的良好来源。

Once the random generator is seeded with a number, it will generate a series of numbers that all depending on the initial value, but in such a way that they seem random.一旦随机生成器被植入一个数字,它将生成一系列数字,这些数字都取决于初始值,但它们看起来是随机的。

A simple random generator (that was actually used in programming languages a while back) is to use a prime number in an algorithm like this:一个简单的随机生成器(之前在编程语言中实际使用过)是在这样的算法中使用素数:

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

This will produce a series of numbers that jump back and forth, seemingly random.这将产生一系列来回跳跃的数字,看似随机。 For example:例如:

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

The random generator in Javascript is just a bit more complex (to give even better distribution) and uses larger numbers (as it has to produce a number that is about 60 bits instead of 16), but it follows the same basic principle. Javascript 中的随机生成器稍微复杂一些(以提供更好的分布)并使用更大的数字(因为它必须生成一个大约 60 位而不是 16 位的数字),但它遵循相同的基本原则。

you may want this article for a reference: https://hackernoon.com/how-does-javascripts-math-random-generate-random-numbers-ef0de6a20131您可能需要这篇文章作为参考: https : //hackernoon.com/how-does-javascripts-math-random-generate-random-numbers-ef0de6a20131

And btw, recently I am also curious about this question and then read the source code of NodeJS.顺便说一句,最近我也很好奇这个问题,然后阅读了NodeJS的源代码。 We can know one possible implementation from Google V8:我们可以从 Google V8 中知道一种可能的实现:

The main entry for the random ( MathRandom::RefillCache function): https://github.com/v8/v8/blob/master/src/math-random.cc随机的主要入口( MathRandom::RefillCache函数): https : //github.com/v8/v8/blob/master/src/math-random.cc

How the seed initialized?种子是如何初始化的? see also here: https://github.com/v8/v8/blob/master/src/base/utils/random-number-generator.cc#L31另见此处: https : //github.com/v8/v8/blob/master/src/base/utils/random-number-generator.cc#L31

The key function is ( XorShift128 function): https://github.com/v8/v8/blob/master/src/base/utils/random-number-generator.h#L119关键函数是( XorShift128函数): https : //github.com/v8/v8/blob/master/src/base/utils/random-number-generator.h#L119

in this header file, there are references to some papers:在这个头文件中,引用了一些论文:

// 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