// Create cyrb128 state:
var seed = cyrb128("apples");
// Four 32-bit component hashes provide the seed for sfc32.
var rand = sfc32(seed[0], seed[1], seed[2], seed[3]);
// Only one 32-bit component hash is needed for mulberry32.
var rand = mulberry32(seed[0]);
// Obtain sequential random numbers like so:
rand();
rand();
var seed = 1337 ^ 0xDEADBEEF; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
var rand = sfc32(0x9E3779B9, 0x243F6A88, 0xB7E15162, seed);
for (var i = 0; i < 15; i++) rand();
function sfc32(a, b, c, d) {
return function() {
a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0;
var t = (a + b) | 0;
a = b ^ b >>> 9;
b = c + (c << 3) | 0;
c = (c << 21 | c >>> 11);
d = d + 1 | 0;
t = t + d | 0;
c = c + t | 0;
return (t >>> 0) / 4294967296;
}
}
function mulberry32(a) {
return function() {
var t = a += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
}
function xoshiro128ss(a, b, c, d) {
return function() {
var t = b << 9, r = b * 5; r = (r << 7 | r >>> 25) * 9;
c ^= a; d ^= b;
b ^= c; a ^= d; c ^= t;
d = d << 11 | d >>> 21;
return (r >>> 0) / 4294967296;
}
}
function jsf32(a, b, c, d) {
return function() {
a |= 0; b |= 0; c |= 0; d |= 0;
var t = a - (b << 27 | b >>> 5) | 0;
a = b ^ (c << 17 | c >>> 15);
b = c + d | 0;
c = d + t | 0;
d = a + t | 0;
return (d >>> 0) / 4294967296;
}
}
不,不可能播种
Math.random()
。 ECMAScript 规范在这个主题上故意含糊不清,没有提供播种方法,也没有要求浏览器甚至使用相同的算法。所以这样的功能必须由外部提供,幸运的是这并不太困难。我用纯 JavaScript 实现了许多优秀、简短且快速的伪随机数生成器 (PRNG) 函数。所有这些都可以播种并提供高质量的数字。这些并非出于安全目的 - 如果您需要可种子的 CSPRNG,请查看ISAAC。 p>
首先,请注意正确初始化您的 PRNG。为了简单起见,下面的生成器没有内置种子生成过程,但接受一个或多个 32 位数字作为PRNG 的初始种子状态。相似或稀疏的种子(例如 1 和 2 的简单种子)具有低熵,并且可能导致相关性或其他随机性质量问题,有时会导致输出具有相似的属性(例如随机生成的级别相似)。为了避免这种情况,最好的做法是使用分布均匀的高熵种子和/或前进到超过前 15 个左右的数字来初始化 PRNG。
有很多方法可以做到这一点,但这里有两种方法。首先,哈希函数非常擅长从短字符串生成种子。即使两个字符串相似,一个好的哈希函数也会生成非常不同的结果,因此您不必对字符串投入太多考虑。这是一个哈希函数示例:
调用
cyrb128
将从字符串中生成一个128位哈希值,该值可用于种子PRNG。以下是您可以如何使用它:注意:如果您想要稍微更强大的 128 位哈希,请考虑 MurmurHash3_x86_128,它更彻底,但旨在用于大型数组。
或者,只需选择一些虚拟数据来填充种子,并预先将生成器推进几次(12-20 次迭代)以彻底混合初始状态。这样做的好处是更简单,并且经常用于 PRNG 的参考实现中,但它确实限制了初始状态的数量:
注意:这些 PRNG 函数的输出会生成一个正 32 位数字(0 到 232-1),然后将其转换为 0-1 之间的浮点数(包括 0) ,1 除外)相当于
Math.random()
,如果您想要特定范围的随机数,请阅读 MDN 上的这篇文章。如果您只想要原始位,只需删除最后的除法运算即可。JavaScript 数字只能表示最高 53 位分辨率的整数。当使用按位运算时,这个值会减少到 32。其他语言中的现代 PRNG 通常使用 64 位运算,这在移植到 JS 时需要填充程序,这会大大降低性能。这里的算法只使用32位操作,因为它直接兼容JS。
sfc32(简单快速计数器)
sfc32 是 PractRand 随机数测试套件的一部分(它当然通过)。 sfc32 具有 128 位状态,并且在 JS 中速度非常快。
您可能想知道
| 是什么? 0
和>>>= 0
是用于。这些本质上是 32 位整数转换,用于性能优化。 JS 中的Number
基本上都是浮点数,但是在按位运算时,它们会切换到 32 位整数模式。这种模式被 JS 解释器处理得更快,但任何乘法或加法都会导致它切换回浮点数,从而导致性能下降。桑树32
Mulberry32 是一个具有 32 位状态的简单生成器,但速度极快且具有良好的随机性(作者表示它通过了 gjrand 测试套件,有完整的 232 周期,但我尚未验证)。
如果您只需要一个简单但体面的 PRNG 并且不需要数十亿个随机数,我会推荐这个(请参阅生日问题)。
xoshiro128**
截至 2018 年 5 月,xoshiro128** 是 的新成员Xorshift 系列,由 Vigna 和 Blackman 开发(Vigna 教授还负责 Xorshift128+ 算法,为大多数
Math.random
实现提供支持)。它是提供 128 位状态的最快生成器。作者声称它很好地通过了随机性测试(尽管有警告)。其他研究人员指出,它未通过 TestU01 中的一些测试(特别是 LinearComp 和 BinaryRank)。实际上,当使用浮点数时(例如在这些实现中),它不应该导致问题,但如果依赖原始最低阶位,则可能会导致问题。
JSF(詹金斯的小快)
这是 Bob Jenkins (2007) 的第一个 JSF 或“smallprng”。 和 幽灵哈希。它通过 PractRand 测试并且应该虽然没有 sfc32 那么快,但相当快。
不,不可能播种
Math.random()
,但编写自己的生成器相当容易,或者更好的是,使用现有的生成器。查看:此相关问题。
此外,请参阅 David Bau 的博客,了解有关播种的更多信息.