是否有可能在JavaScript中播种随机数生成器(Math.random) ?
当前回答
请看Pierre L'Ecuyer在20世纪80年代末和90年代初的作品。还有其他的。如果你不是专家,自己创建一个(伪)随机数生成器是相当危险的,因为结果很可能不是统计随机的,或者有一个很小的周期。Pierre(和其他人)组合了一些很容易实现的(伪)随机数生成器。我用的是他的LFSR发电机。
https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf
其他回答
结合之前的一些答案,这是你正在寻找的可种子随机函数:
Math.seed = function(s) {
var mask = 0xffffffff;
var m_w = (123456789 + s) & mask;
var m_z = (987654321 - s) & mask;
return function() {
m_z = (36969 * (m_z & 65535) + (m_z >>> 16)) & mask;
m_w = (18000 * (m_w & 65535) + (m_w >>> 16)) & mask;
var result = ((m_z << 16) + (m_w & 65535)) >>> 0;
result /= 4294967296;
return result;
}
}
var myRandomFunction = Math.seed(1234);
var randomNumber = myRandomFunction();
不,不可能给Math.random()添加种子。ECMAScript规范故意在这个主题上含糊不清,既不提供播种的方法,也不要求浏览器使用相同的算法。因此,这样的函数必须由外部提供,谢天谢地,这并不太难。
我已经在纯JavaScript中实现了许多好的、短的、快速的伪随机数生成器(PRNG)函数。所有这些都可以播种,并提供高质量的数字。这些并不用于安全目的——如果您需要一个可播种的CSPRNG,请查看ISAAC。
First of all, take care to initialize your PRNGs properly. To keep things simple, the generators below have no built-in seed generating procedure, but accept one or more 32-bit numbers as the initial seed state of the PRNG. Similar or sparse seeds (e.g. a simple seed of 1 and 2) have low entropy, and can cause correlations or other randomness quality issues, sometimes resulting in the output having similar properties (such as randomly generated levels being similar). To avoid this, it is best practice to initialize PRNGs with a well-distributed, high entropy seed and/or advancing past the first 15 or so numbers.
有很多方法可以做到这一点,但这里有两种方法。首先,哈希函数非常擅长从短字符串中生成种子。即使两个字符串相似,一个好的哈希函数也会产生非常不同的结果,所以你不必在字符串上花太多心思。下面是一个哈希函数的例子:
function cyrb128(str) {
let h1 = 1779033703, h2 = 3144134277,
h3 = 1013904242, h4 = 2773480762;
for (let i = 0, k; i < str.length; i++) {
k = str.charCodeAt(i);
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
}
h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
return [(h1^h2^h3^h4)>>>0, (h2^h1)>>>0, (h3^h1)>>>0, (h4^h1)>>>0];
}
调用cyrb128将从一个可用于PRNG种子的字符串中产生一个128位哈希值。下面是你如何使用它:
// 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();
注意:如果您想要稍微健壮一点的128位哈希,可以考虑MurmurHash3_x86_128,它更彻底,但适用于大型数组。
或者,简单地选择一些虚拟数据来填充种子,并预先将生成器推进几次(12-20次迭代),以彻底混合初始状态。这样做的好处是更简单,并且经常在prng的参考实现中使用,但它确实限制了初始状态的数量:
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();
注意:这些PRNG函数的输出产生一个32位正数(0到232-1),然后转换为一个0-1(0包含,1不包含)之间的浮点数,等效于Math.random(),如果您想要特定范围的随机数,请阅读MDN上的这篇文章。如果您只想要原始位,只需删除最后的除法操作。
JavaScript数字只能表示53位分辨率的整数。而当使用位操作时,它被减少到32。其他语言中的现代prng通常使用64位操作,这在移植到JS时需要shims,这会大大降低性能。这里的算法只使用32位操作,因为它与JS直接兼容。
现在,我们来谈谈发电机。(我在这里保留了完整的参考文献和许可信息列表)
sfc32(简单快速计数器)
sfc32是PractRand随机数测试套件的一部分(当然它通过了测试)。sfc32有128位的状态,在JS中非常快。
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;
}
}
你可能想知道| 0和>>>= 0是干什么用的。它们本质上是32位整数强制转换,用于性能优化。JS中的Number基本上是浮点数,但在按位操作时,它们切换到32位整数模式。JS解释器可以更快地处理这种模式,但任何乘法或加法都会导致它切换回浮点数,从而导致性能下降。
Mulberry32
Mulberry32是一个32位状态的简单生成器,但是速度非常快,并且具有良好的随机性(作者声明它通过了gjrand测试套件的所有测试,并且具有完整的232周期,但我还没有验证)。
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;
}
}
如果你只是需要一个简单但体面的PRNG,并且不需要数十亿个随机数(参见生日问题),我会推荐这个方法。
xoshiro128 * *。
截至2018年5月,xoshiro128**是Xorshift家族的新成员,由Vigna和Blackman开发(Vigna教授还负责为大多数数学提供支持的Xorshift128+算法。底层的随机实现)。它是最快的生成器,提供128位状态。
function xoshiro128ss(a, b, c, d) {
return function() {
var t = b << 9, r = a * 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;
}
}
作者声称它很好地通过了随机性测试(尽管有一些警告)。其他研究人员指出,它在TestU01中失败了一些测试(特别是LinearComp和BinaryRank)。在实践中,当使用浮点数时(例如在这些实现中),它应该不会引起问题,但如果依赖于原始最低阶位,则可能会引起问题。
JSF (Jenkins的小而快)
这是Bob Jenkins(2007)的JSF或“smallprng”,他还制作了ISAAC和SpookyHash。它通过了PractRand测试,应该非常快,尽管没有sfc32快。
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;
}
}
SIN(id + seed)是一个非常有趣的替代RANDOM函数,不能像SQLite一样播种:
https://stackoverflow.com/a/75089040/7776828
不,就像他们说的,不可能播种Math.random() 但你可以安装外部包,为这做准备。我使用这些包,可以安装使用这些命令
npm i random-seed
该示例来自包文档。
var seed = 'Hello World',
rand1 = require('random-seed').create(seed),
rand2 = require('random-seed').create(seed);
console.log(rand1(100), rand2(100));
点击链接获取文档https://www.npmjs.com/package/random-seed
数学。不是随机的,但是ran库解决了这个问题。它几乎拥有你能想象到的所有分布,并支持种子随机数生成。例子:
ran.core.seed(0)
myDist = new ran.Dist.Uniform(0, 1)
samples = myDist.sample(1000)
推荐文章
- 如何使用Jest测试对象键和值是否相等?
- 将长模板文字行换行为多行,而无需在字符串中创建新行
- 如何在JavaScript中映射/减少/过滤一个集?
- Bower: ENOGIT Git未安装或不在PATH中
- 添加javascript选项选择
- 在Node.js中克隆对象
- 为什么在JavaScript的Date构造函数中month参数的范围从0到11 ?
- 使用JavaScript更改URL参数并指定默认值
- 在window.setTimeout()发生之前取消/终止
- 如何删除未定义和空值从一个对象使用lodash?
- 检测当用户滚动到底部的div与jQuery
- 在JavaScript中检查字符串包含另一个子字符串的最快方法?
- 检测视口方向,如果方向是纵向显示警告消息通知用户的指示
- ASP。NET MVC 3 Razor:在head标签中包含JavaScript文件
- 禁用从HTML页面中拖动图像