![](https://i-blog.csdnimg.cn/blog_migrate/4fb54df5970f7b20c09ec4ec48d6d249.png)
打开开发工具(Mac:cmd +选项+ i / Windows:ctrl + shift + i),转到控制台,键入Math.random()
,然后按回车键。
am 您得到一个随机数。
我得到了0.6199322557631561。
我一直想知道这些数字来自何处。 而且,更重要的是,它们如何可能是随机的? 毕竟,难道计算机不只是接受一些输入,用一些数学方法旋转它,然后将其吐出来吗? 似乎是一个可预测的过程。 那么当您想生成一个“随机”数字时会发生什么呢? 甚至如何工作以及幕后发生了什么?
对于初学者来说,这并不是随机的
令人惊讶的是,答案是Math.random()
并未真正生成随机数。 不完全是。 它只是在模拟随机性方面做得非常好。
本质上,算法随机数生成不能完全是随机的。 这就是为什么将它们更恰当地称为伪随机数生成器(PRNG)的原因。 如果您使用数学和公式来创建一个数字序列,尽管看起来可能是随机的,但这些数字最终会重复出现并显示出非随机模式。
但是有些PRNG比其他的要好。 PRNG的质量取决于许多因素,一个非常重要的因素是所谓的周期 。 PRNG开始重复自身之前经过的迭代次数。 不仅具有长周期的PRNG 似乎更随意我们人类,但它也更难(即资源较多)对计算机的裂缝/预测; 即使没有人应该使用Math.random()
进行加密,这也会带来安全隐患,但是无论如何都会发生。
所以现在的问题是: JavaScript使用什么PRNG?
答案:没有。
这取决于浏览器
JavaScript不会决定Math.random()
的实现方式,而浏览器会确定。 JavaScript没有附带硬编码的PRNG算法。 相反,创建浏览器的工程师决定使用符合ECMAScript规范的算法,如下所示:
[Math.random]使用依赖于实现的算法或策略,返回一个具有正号的Number值,该值大于或等于0但小于1,在该范围内以近似均匀的分布随机或伪随机选择。 此函数不带参数。
为不同代码领域创建的每个Math.random函数必须从连续调用中产生不同的值序列。
这些就是说明,由浏览器决定如何遵循它们。 直到最近,不同的浏览器才使用略有不同的方法来实现此目的。 他们使用的算法名称很性感,例如Marsenne-Twister , 带进位乘或线性同 余生成 器。 不过,请放心,了解所有这些内容的含义对您来说并不重要(尽管您确实对我印象深刻)。
了解所有这些重要的事情是(1)浏览器决定他们要使用哪种算法来计算Math.random()
,(2)在2015年几乎每个浏览器(至少是主要的浏览器)都放弃了旧的PRNG算法现在它们都使用相同的名称: xorshift128 + 。
事实证明,xorshift128 +在伪装随机性方面比旧算法做得更好。 加上重量极轻且计算速度快。 因此,它几乎被广泛采用,当您考虑到以前对此事有很多不同意见时,就充分说明了它的有效性。
但是它到底如何工作?
尽管每个浏览器实现算法的方式略有不同,但我们可以查看其“原始”版本以了解其工作原理。
一些有趣的数学
首先,我将向您展示该算法,以便您可以将其全部浸泡(如C所示),然后我们将仔细研究一下:
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;
}
如果您像我一样(具有前端背景并且没有计算机科学学位),请看一下它,然后思考“好吧,变量赋值,变量赋值,函数……足够简单……”,但是您会得出s1 ^= s1 << 23;
并说“该死的是什么?”
这些是按位运算符。 它们以位级别(1和0)操作数据,并且构成了我们正在研究的算法的核心和灵魂。 它们也是普通的Web开发人员很少(如果有的话)可以使用的机会。 因此,为了解释该算法的作用,我将快速介绍上面显示的三个按位运算符以及它们如何工作。
第一个运算符<<
称为左移。 这是一个示例: 12 << 4
。 在此示例中,您将采用数字12的二进制表示形式,并将其向左移动4个位置。 因此向左移动。 运作方式如下:
![](https://i-blog.csdnimg.cn/blog_migrate/cac82b109b1d71ddd0125fad11bdd6d1.jpeg)
与此相反的函数称为right shift >>
,其作用相同,但右移而不是左移。
第二个运算符=^
是xor赋值运算符。 Xor( exclusive或的缩写)比较两个数字的二进制表示形式,并在对应位匹配的地方输出0,在对应位不同的地方输出1。 您可以将xor视为“一个或另一个,但不能同时存在 ”。 这是一个随机xor 53^18
的可视化效果(xor没有分配)
![](https://i-blog.csdnimg.cn/blog_migrate/72e6bf740f7ec6793fa7cc4ad6a16f26.png)
既然您知道所有运算符都在做什么,那么您就可以开始理解上面的xorshift算法了。 我刚才提到的令人困惑的位( s1 ^= s1 << 23;
)只是将s1左移23位,然后将结果与s1异或,从而得出s1的新分配值。 或者,换句话说,这是异或。
因此,为了完全简化,该算法采用了两个种子值,将它们切换过来,对它们的位值进行混洗,将它们的位值通过逻辑门放置,重复几次,然后将它们加在一起,然后…
am 您会得到一个“随机”数字。
结论(tl; dr)
为了将所有内容整齐打包,这里有一个概述。
问题: JavaScript的Math.random()
如何生成随机数?
回答:
- JS不会执行任何操作,这取决于浏览器
- 截至2015年,大多数浏览器都使用称为xorshift128 +的算法
- xorshift128 +生成的数字并不是真正随机的,序列需要很长时间才能重复,并且在期望的值范围内相对均匀地分布。
因此,事实证明,我们在这里真正要做的就是获取一些输入,并通过一些数学运算将其旋转,然后吐出一个结果。 一个完全可预测的非随机过程。 但是,对于我们来说,随机性足以使它达到其目的的目的是使我们JavaScript混乱不堪。
对于感兴趣的任何人,我都可以在GitHub上获得xorshift128 +的JS实现(在下面链接),它使您可以直观地看到算法的“随机性”,并可以处理 种子 值 和 移位 值。 谢谢阅读!
翻译自: https://hackernoon.com/how-does-javascripts-math-random-generate-random-numbers-ef0de6a20131