可种子 JavaScript 随机数生成器

IT技术 javascript random seed
2021-02-08 11:11:51

JavaScriptMath.random()函数返回一个 0 到 1 之间的随机值,根据当前时间自动播种(类似于我相信的 Java)。但是,我认为没有任何方法可以为您设置种子。

我如何制作一个随机数生成器,我可以为其提供我自己的种子值,以便我可以让它产生一个可重复的(伪)随机数序列?

6个回答

一种选择是http://davidbau.com/seedrandom,它是一个基于 RC4 的可种子 Math.random() 替代品,具有很好的属性。

@EatatJoes,这是 JS 的耻辱和荣耀,这既需要又可能。您可以包含一个文件并获得对 Math 对象所做的向后兼容更改,这是非常酷的。10 天的工作还不错,Brendan Eich。
2021-03-23 11:11:51
对于寻找此项目的 npm 页面的任何人:npmjs.com/package/seedrandom
2021-03-25 11:11:51
David Bau 的 seedrandom 从此变得非常流行,以至于他将其保存在 github 上很遗憾 ECMAScript 这么久以来一直处于幕后,这样的东西没有包含在语言中。说真的,没有播种!!!
2021-04-09 11:11:51

如果您不需要播种功能,只需使用Math.random()并围绕它构建辅助函数(例如randRange(start, end))。

我不确定您使用的是什么 RNG,但最好了解并记录它,以便您了解它的特性和局限性。

正如 Starkii 所说,Mersenne Twister 是一个很好的 PRNG,但它并不容易实现。如果您想自己做,请尝试实现LCG - 它非常简单,具有不错的随机性(不如 Mersenne Twister),并且您可以使用一些流行的常量。

编辑:考虑在这个答案中为短可种子 RNG 实现提供很好的选择,包括 LCG 选项。

function RNG(seed) {
  // LCG using GCC's constants
  this.m = 0x80000000; // 2**31;
  this.a = 1103515245;
  this.c = 12345;

  this.state = seed ? seed : Math.floor(Math.random() * (this.m - 1));
}
RNG.prototype.nextInt = function() {
  this.state = (this.a * this.state + this.c) % this.m;
  return this.state;
}
RNG.prototype.nextFloat = function() {
  // returns in range [0,1]
  return this.nextInt() / (this.m - 1);
}
RNG.prototype.nextRange = function(start, end) {
  // returns in range [start, end): including start, excluding end
  // can't modulu nextInt because of weak randomness in lower bits
  var rangeSize = end - start;
  var randomUnder1 = this.nextInt() / this.m;
  return start + Math.floor(randomUnder1 * rangeSize);
}
RNG.prototype.choice = function(array) {
  return array[this.nextRange(0, array.length)];
}

var rng = new RNG(20);
for (var i = 0; i < 10; i++)
  console.log(rng.nextRange(10, 50));

var digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
for (var i = 0; i < 10; i++)
  console.log(rng.choice(digits));

模数不应该是 2^31 吗?我从wiki阅读了这个算法
2021-03-16 11:11:51
只是让您知道,这不是“正确”,因为它不输出数学要求的内容。换句话说,一种可以处理这些大数字的语言会产生不同的结果。JS 因大数字而窒息并降低精度(毕竟它们是浮点数)。
2021-03-19 11:11:51
-1 这个 LCG 实现打破了 JavaScript 中精确整数的限制,因为this.a * this.state它可能会导致大于 2^53 的数字。结果是输出范围有限,对于某些种子来说可能是很短的时间。此外,通常使用 2 的幂会m导致一些非常明显的模式,当您花费模数运算而不是简单的截断时,没有理由不使用素数。
2021-04-02 11:11:51

如果您希望能够指定种子,您只需要替换对getSeconds()的调用getMinutes()你可以传入一个 int 并使用它的一半 mod 60 作为秒值,另一半 mod 60 给你另一部分。

话虽如此,这种方法看起来很垃圾。进行适当的随机数生成非常困难。一个明显的问题是随机数种子是基于秒和分钟的。猜测种子并重新创建随机数流只需要尝试 3600 种不同的秒和分钟组合。这也意味着只有 3600 种不同的可能种子。这是可以纠正的,但我从一开始就怀疑这个 RNG。

如果您想使用更好的 RNG,请尝试使用Mersenne Twister它是一个经过良好测试且相当强大的 RNG,具有巨大的轨道和出色的性能。

编辑:我真的应该是正确的,并将其称为伪随机数生成器或 PRNG。

“任何使用算术方法产生随机数的人都处于犯罪状态。”
                                                                                                                                                          ---约翰·冯·诺依曼

@orip 好的,不清楚。您正在谈论 Mersenne Twister 以及下一句关于初始状态的内容;)
2021-03-18 11:11:51
@托比亚斯P。我指的是使用 getSeconds() 和 getMinutes() 组合播种的建议,60 * 60 == 3600 个可能的初始状态。我不是指 Mersenne Twister。
2021-03-26 11:11:51
提问者没有提到他们需要为任何类型的加密敏感应用程序生成“适当的”随机数。虽然所有答案都是正确的,但只有第一段与所提出的问题实际相关。也许添加建议解决方案的代码片段。
2021-03-30 11:11:51
2021-04-02 11:11:51
@orip 你有 3600 个初始状态的来源吗?Mersenne Twister 以 32 位数字作为种子,因此 PRNG 应该有 40 亿个初始状态——前提是初始种子确实是随机的。
2021-04-07 11:11:51

我使用 Mersenne Twister 的 JavaScript 端口:https : //gist.github.com/300494 它允许您手动设置种子。此外,正如其他答案中提到的,Mersenne Twister 是一个非常好的 PRNG。

您列出的代码有点像Lehmer RNG如果是这种情况,则2147483647是最大的 32 位有符号整数,2147483647是最大的 32 位素48271数, 是用于生成数字的全周期乘数。

如果这是真的,您可以修改RandomNumberGenerator以接收额外的参数seed,然后设置this.seedseed; 但是你必须小心确保种子会导致随机数的良好分布(莱默可能会很奇怪) - 但大多数种子会很好。