ChatGPT解决这个技术问题 Extra ChatGPT

在 Javascript 中播种随机数生成器

是否可以在 JavaScript 中播种随机数生成器 (Math.random)?

目前尚不清楚您是要播种它以便为不同的测试运行重复获得相同的结果,还是要为每个用户使用“独特的东西”播种它以获得更好的使用随机性。
不,很遗憾,这是不可能的。 jsrand 是我在需要可播种 PRNG 时编写的一个小库。还有其他更复杂的库,您可以通过谷歌搜索找到它。
增加一个问题:在没有播种方式的情况下提供 PRNG 怎么可能是个好主意?这有什么好的理由吗?
这是此页面上一些生成器的可视化observablehq.com/@tidwall/hello-randomness
@Alan我认为可能没有种子,因为底层算法取决于浏览器-如果 Math.random() 确实有种子,则不能保证种子在不同的浏览器中给出相同的结果。 hackernoon.com/…

b
bryc

不,不可能播种 Math.random()ECMAScript specification 在主题上故意含糊其辞,既不提供播种方式,也不要求浏览器甚至使用相同的算法。所以这样的功能必须由外部提供,幸好这并不难。

我已经用纯 JavaScript 实现了许多好的、短的和快速的 伪随机数生成器 (PRNG) 函数。所有这些都可以播种并提供高质量的数字。这些并非出于安全目的 - 如果您需要可播种的 CSPRNG,请查看 ISAAC

首先,注意正确初始化您的 PRNG。为简单起见,下面的生成器没有内置种子生成过程,但接受一个或多个 32 位数字作为 PRNG 的初始种子状态。相似或稀疏的种子(例如 1 和 2 的简单种子)具有低熵,并且可能导致相关性或其他随机性质量问题,有时会导致输出具有相似的属性(例如随机生成的级别相似)。为避免这种情况,最佳实践是使用分布良好的高熵种子和/或超过前 15 个左右的数字来初始化 PRNG。

有很多方法可以做到这一点,但这里有两种方法。首先,hash functions 非常擅长从短字符串生成种子。即使两个字符串相似,一个好的哈希函数也会产生非常不同的结果,因此您不必对字符串花太多心思。这是一个示例哈希函数:

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(),如果您想要特定范围的随机数,请阅读 this article on MDN。如果您只想要原始位,只需删除最后的除法操作。

JavaScript 数字只能表示分辨率高达 53 位的整数。而当使用按位运算时,这会减少到 32。其他语言中的现代 PRNG 通常使用 64 位运算,这在移植到 JS 时需要 shims,这会大大降低性能。这里的算法只使用 32 位操作,因为它直接兼容 JS。

现在,继续生成器。 (我在此处维护包含参考和许可证信息的完整列表)

sfc32(简单快速计数器)

sfc32PractRand 随机数测试套件的一部分(它当然通过了)。 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 解释器处理得更快,但是任何乘法或加法都会导致它切换回浮点数,从而导致性能下降。

桑树32

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 并且不需要数十亿个随机数(请参阅 Birthday problem),我会推荐这个。

xoshiro128**

自 2018 年 5 月起,xoshiro128** 成为 Xorshift family 的新成员,由 Vigna & Blackman(Vigna 教授还负责为大多数 Math.random 实现提供支持的 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;
    }
}

作者声称它很好地通过了随机性测试 (albeit with caveats)。其他研究人员指出,它未能通过 TestU01 中的一些测试(尤其是 LinearComp 和 BinaryRank)。在实践中,使用浮点数时(例如在这些实现中)不应该导致问题,但如果依赖原始最低位可能会导致问题。

JSF(詹金斯的小快)

这是 Bob Jenkins (2007) 的 JSF 或“smallprng”,他还制作了 ISAACSpookyHash。它 passes 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;
    }
}

我相信您从 Pierre L'ecuyer 的“线性同余生成器表...”中引用的值可能会超过 Javascript 中的最大整数大小。 (2^32-1) * 741103597 ≈ 3e18 的最大种子,大于 JavaScript 的最大 int 大小 ≈ 9e15。我认为 Pierre 书中的以下值在原生范围内具有最大的周期:seed = (seed * 185852 + 1) % 34359738337
@Lachmanski 是的,但它们受 32 位(和 Park-Miller 31 位)的约束。使用 Math.imul 允许它溢出,就像在 C 中对 32 位整数使用乘法一样。您所建议的是使用 JS 整数空间的全部范围的 LCG,这绝对是一个值得探索的有趣领域。 :)
这太棒了!我可以将您的 sfc32 复制到 LGPL 程序中吗?
当然,您可以随意将代码用于任何目的 :)
@blobber2 不确定您的意思,但原始代码来自此处(与其他人一起):github.com/bryc/code/blob/master/jshash/PRNGs.md。或多或少是存储库中的要点:-)
a
ashleedawg

不,不可能播种 Math.random(),但编写自己的生成器相当容易,或者更好的是,使用现有的生成器。

签出:this related question

此外,请参阅 David Bau 的博客以了解 more information on seeding


A
Antti Kissaniemi

注意:尽管(或者更确切地说,因为)简洁和明显的优雅,这个算法在随机性方面绝不是一个高质量的算法。寻找例如this answer中列出的那些以获得更好的结果。

(最初改编自对另一个答案的评论中提出的一个聪明的想法。)

var seed = 1;
function random() {
    var x = Math.sin(seed++) * 10000;
    return x - Math.floor(x);
}

您可以将 seed 设置为任意数字,但要避免零(或 Math.PI 的任意倍数)。

在我看来,这个解决方案的优雅之处在于缺少任何“神奇”数字(除了 10000,它表示为避免出现奇怪的模式而必须丢弃的最少数字数量 - 查看值为 10、{ 2},1000)。简洁也不错。

它比 Math.random() 慢一点(2 或 3 倍),但我相信它与任何其他用 JavaScript 编写的解决方案一样快。


有没有办法证明这个 RNG 生成的数字是均匀分布的?从实验上看,它似乎是:jsfiddle.net/bhrLT
6,000,000 ops/second 非常快,我不打算每次点击产生超过 3,000,000 次。开玩笑,这太棒了。
-1,这根本不是一个统一的采样器——它非常偏向于 0 和 1(参见 jsfiddle.net/bhrLT/17,这可能需要一段时间来计算)。连续的值是相关的——每 355 个值,甚至每 710 个值都是相关的。请使用更深思熟虑的东西!
问题不是关于创建一个加密安全的随机数生成器,而是在 javascript 中工作的东西,对于快速演示等很有用。我将采取一些快速简单的方法,为此目的提供超过一百万个随机数的漂亮分布。
当心。 Math.sin() 可以在客户端和服务器上给出不同的结果。我使用 Meteor(在客户端和服务器上使用 javascript)。
T
TankorSmash

不,但这是一个简单的伪随机生成器,是我改编自 WikipediaMultiply-with-carry 的实现(此后已被删除):

var m_w = 123456789;
var m_z = 987654321;
var mask = 0xffffffff;

// Takes any integer
function seed(i) {
    m_w = (123456789 + i) & mask;
    m_z = (987654321 - i) & mask;
}

// Returns number between 0 (inclusive) and 1.0 (exclusive),
// just like Math.random().
function random()
{
    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;
}

有没有人测试过这个函数的随机性?
这是具有相当长周期的 multiply-with-carry (MWC) 随机生成器。改编自wikipedia Random Number Generators
当我将它与我的随机颜色生成器 (HSL) 一起使用时,它只生成绿色和青色。原始随机生成器生成所有颜色。因此,它不一样或不起作用。
@Michael_Scharf 1)种子更改 m_w,而不是 m_z。 2) m_wm_z 都根据它们以前的值进行更改,因此它确实修改了结果。
当我使用这段代码时,我没有得到分布良好的结果。无论种子如何,输出序列都非常相似。这对我的游戏没有帮助。
g
ggorlen

Antti Sykäri 的算法既好又短。当您调用 Math.seed(s) 时,我最初做了一个替换 JavaScript 的 Math.random 的变体,但后来 Jason 评论说返回该函数会更好:

Math.seed = function(s) {
    return function() {
        s = Math.sin(s) * 10000; return s - Math.floor(s);
    };
};

// usage:
var random1 = Math.seed(42);
var random2 = Math.seed(random1());
Math.random = Math.seed(random2());

这为您提供了 JavaScript 没有的另一个功能:多个独立的随机生成器。如果您想同时运行多个可重复的模拟,这一点尤其重要。


如果您返回函数而不是设置 Math.random,这将允许您拥有多个独立的生成器,对吗?
如果这对您很重要,请务必查看上面关于随机分布的评论:stackoverflow.com/questions/521295/…
由此产生的随机数如何重复?它每次都会给出新的数字
每次执行 Math.seed(42); 时都会重置函数,因此如果执行 var random = Math.seed(42); random(); random();,则会得到 0.70...,然后是 0.38...。如果您通过再次调用 var random = Math.seed(42); 来重置它,那么下次您调用 random() 时,您将再次获得 0.70...,下一次您将再次获得 0.38...
请不要使用这个。请花点时间改用名为 random 的局部变量,而不是覆盖原生 javascript 函数。覆盖 Math.random 可能会导致 JIST 编译器未优化您的所有代码。
u
user2383235

请参阅 Pierre L'Ecuyer 可追溯到 1980 年代末和 1990 年代初的作品。还有其他的。如果您不是专家,那么您自己创建一个(伪)随机数生成器是非常危险的,因为结果很可能不是统计上随机的,或者周期很短。 Pierre(和其他人)已经组合了一些易于实现的好(伪)随机数生成器。我使用他的一台 LFSR 发生器。

https://www.iro.umontreal.ca/~lecuyer/myftp/papers/handstat.pdf

菲尔·特洛伊


很好的答案,但与 javascript 无关 :)
用于实现 L'Ecuyer 教授工作的代码可公开用于 Java,并且大多数程序员都可以轻松地将其翻译成 Javascript。
b
bryc

结合之前的一些答案,这是您正在寻找的可种子随机函数:

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.seed(0)() 返回 0.2322845458984375Math.seed(1)() 返回 0.23228873685002327。根据种子同时更改 m_wm_z 似乎有帮助。 var m_w = 987654321 + s; var m_z = 123456789 - s; 产生具有不同种子的第一个值的良好分布。
@undefined 您描述的问题在上次编辑时已修复,这是 MWC 实现中的一个错误。
截至 2020 年 1 月,现在运行良好。种子为 0,得到 0.7322976540308446。种子含 1,0.16818441334180534,含 2:0.6040864314418286,含 3:0.03998844954185188。谢谢你们俩!
L
Lajos Bodrogi

编写自己的伪随机生成器非常简单。

Dave Scotese 的建议很有用,但正如其他人所指出的,它的分布并不十分均匀。

然而,这不是因为 sin 的整数参数。这只是因为罪的范围,恰好是一个圆的一维投影。如果你取圆的角度,它会是均匀的。

所以代替 sin(x) 使用 arg(exp(i * x)) / (2 * PI)。

如果您不喜欢线性顺序,请将其与 xor 混合一下。实际因素也没有那么重要。

要生成 n 个伪随机数,可以使用以下代码:

function psora(k, n) {
  var r = Math.PI * (k ^ n)
  return r - Math.floor(r)
}
n = 42; for(k = 0; k < n; k++) console.log(psora(k, n))

另请注意,当需要真实熵时,您不能使用伪随机序列。


我不是专家,但顺序种子遵循 constant pattern。彩色像素>= 0.5。我猜它只是一遍又一遍地迭代半径?
M
Martin Omander

如今,许多需要 Javascript 中的可种子随机数生成器的人都在使用 David Bau's seedrandom module


U
Ulf Aslak

Math.random 没有,但 ran library 解决了这个问题。它几乎具有您可以想象的所有分布,并支持种子随机数生成。例子:

ran.core.seed(0)
myDist = new ran.Dist.Uniform(0, 1)
samples = myDist.sample(1000)

c
ccxvii

不可能播种内置的 Math.random 函数,但可以用很少的代码在 Javascript 中实现高质量的 RNG。

Javascript 数字是 64 位浮点精度,可以表示小于 2^53 的所有正整数。这对我们的算术设置了一个硬性限制,但在这些限制内,您仍然可以为高质量的 Lehmer / LCG 随机数生成器选择参数。

function RNG(seed) {
    var m = 2**35 - 31
    var a = 185852
    var s = seed % m
    return function () {
        return (s = s * a % m) / m
    }
}

Math.random = RNG(Date.now())

如果您想要更高质量的随机数,但要以慢约 10 倍为代价,您可以使用 BigInt 进行算术并选择 m 刚好适合双精度数的参数。

function RNG(seed) {
    var m_as_number = 2**53 - 111
    var m = 2n**53n - 111n
    var a = 5667072534355537n
    var s = BigInt(seed) % m
    return function () {
        return Number(s = s * a % m) / m_as_number
    }
}

有关上述实现中使用的参数,请参见 Pierre l'Ecuyer 的这篇论文:https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/S0025-5718-99-00996-5.pdf

无论您做什么,都要避免使用 Math.sin 的所有其他答案!


K
Kirill Groshkov

这是采用的 Jenkins 哈希版本,借用自 here

export function createDeterministicRandom(): () => number {
  let seed = 0x2F6E2B1;
  return function() {
    // Robert Jenkins’ 32 bit integer hash function
    seed = ((seed + 0x7ED55D16) + (seed << 12))  & 0xFFFFFFFF;
    seed = ((seed ^ 0xC761C23C) ^ (seed >>> 19)) & 0xFFFFFFFF;
    seed = ((seed + 0x165667B1) + (seed << 5))   & 0xFFFFFFFF;
    seed = ((seed + 0xD3A2646C) ^ (seed << 9))   & 0xFFFFFFFF;
    seed = ((seed + 0xFD7046C5) + (seed << 3))   & 0xFFFFFFFF;
    seed = ((seed ^ 0xB55A4F09) ^ (seed >>> 16)) & 0xFFFFFFFF;
    return (seed & 0xFFFFFFF) / 0x10000000;
  };
}

你可以像这样使用它:

const deterministicRandom = createDeterministicRandom()
deterministicRandom()
// => 0.9872818551957607

deterministicRandom()
// => 0.34880331158638

p
penduDev

这里的大多数答案都会产生有偏见的结果。所以这是一个基于 seedrandom library from github 的测试函数:

!function(f,a,c){var s,l=256,p="random",d=c.pow(l,6),g=c.pow(2,52),y=2*g,h=l-1;function n(n,t,r){function e(){for(var n=u.g(6),t=d,r=0;n<g;)n=(n+r)*l,t*=l,r=u.g(1);for(;y<=n;)n/=2,t/=2,r>>>=1;return(n+r)/t}var o=[],i=j(function n(t,r){var e,o=[],i=typeof t;if(r&&"object"==i)for(e in t)try{o.push(n(t[e],r-1))}catch(n){}return o.length?o:"string"==i?t:t+"\0"}((t=1==t?{entropy:!0}:t||{}).entropy?[n,S(a)]:null==n?function(){try{var n;return s&&(n=s.randomBytes)?n=n(l):(n=new Uint8Array(l),(f.crypto||f.msCrypto).getRandomValues(n)),S(n)}catch(n){var t=f.navigator,r=t&&t.plugins;return[+new Date,f,r,f.screen,S(a)]}}():n,3),o),u=new m(o);return e.int32=function(){return 0|u.g(4)},e.quick=function(){return u.g(4)/4294967296},e.double=e,j(S(u.S),a),(t.pass||r||function(n,t,r,e){return e&&(e.S&&v(e,u),n.state=function(){return v(u,{})}),r?(c[p]=n,t):n})(e,i,"global"in t?t.global:this==c,t.state)}function m(n){var t,r=n.length,u=this,e=0,o=u.i=u.j=0,i=u.S=[];for(r||(n=[r++]);e<l;)i[e]=e++;for(e=0;e<l;e++)i[e]=i[o=h&o+n[e%r]+(t=i[e])],i[o]=t;(u.g=function(n){for(var t,r=0,e=u.i,o=u.j,i=u.S;n--;)t=i[e=h&e+1],r=r*l+i[h&(i[e]=i[o=h&o+t])+(i[o]=t)];return u.i=e,u.j=o,r})(l)}function v(n,t){return t.i=n.i,t.j=n.j,t.S=n.S.slice(),t}function j(n,t){for(var r,e=n+"",o=0;o<e.length;)t[h&o]=h&(r^=19*t[h&o])+e.charCodeAt(o++);return S(t)}function S(n){return String.fromCharCode.apply(0,n)}if(j(c.random(),a),"object"==typeof module&&module.exports){module.exports=n;try{s=require("crypto")}catch(n){}}else"function"==typeof define&&define.amd?define(function(){return n}):c["seed"+p]=n}("undefined"!=typeof self?self:this,[],Math);

function randIntWithSeed(seed, max=1) {
  /* returns a random number between [0,max] including zero and max
  seed can be either string or integer */
  return Math.round(new Math.seedrandom('seed' + seed)()) * max
}

测试此代码的真正随机性:https://es6console.com/kkjkgur2/


S
Sagar Zala

我编写了一个返回种子随机数的函数,它使用 Math.sin 来获得一个长随机数,并使用种子从中挑选数字。

利用 :

seedRandom("k9]:2@", 15)

它将返回您的种子编号,第一个参数是任何字符串值;你的种子。第二个参数是返回多少位数。

     function seedRandom(inputSeed, lengthOfNumber){

           var output = "";
           var seed = inputSeed.toString();
           var newSeed = 0;
           var characterArray = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','y','x','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','U','R','S','T','U','V','W','X','Y','Z','!','@','#','$','%','^','&','*','(',')',' ','[','{',']','}','|',';',':',"'",',','<','.','>','/','?','`','~','-','_','=','+'];
           var longNum = "";
           var counter = 0;
           var accumulator = 0;

           for(var i = 0; i < seed.length; i++){
                var a = seed.length - (i+1);
                for(var x = 0; x < characterArray.length; x++){
                     var tempX = x.toString();
                     var lastDigit = tempX.charAt(tempX.length-1);
                     var xOutput = parseInt(lastDigit);
                     addToSeed(characterArray[x], xOutput, a, i); 
                }                  
           }

                function addToSeed(character, value, a, i){
                     if(seed.charAt(i) === character){newSeed = newSeed + value * Math.pow(10, a)}
                }
                newSeed = newSeed.toString();

                var copy = newSeed;
           for(var i=0; i<lengthOfNumber*9; i++){
                newSeed = newSeed + copy;
                var x = Math.sin(20982+(i)) * 10000;
                var y = Math.floor((x - Math.floor(x))*10);
                longNum = longNum + y.toString()
           }

           for(var i=0; i<lengthOfNumber; i++){
                output = output + longNum.charAt(accumulator);
                counter++;
                accumulator = accumulator + parseInt(newSeed.charAt(counter));
           }
           return(output)
      }

由此产生的数字序列并不能真正近似随机数序列的属性。用它生成 15 个数字,例如,对于几乎任何键,生成的字符串几乎总是以 7 开头。
C
Carlos Oliveira

固定种子的简单方法:

function fixedrandom(p){
    const seed = 43758.5453123;
    return (Math.abs(Math.sin(p)) * seed)%1;
}

m
manasGain

在 PHP 中,有一个函数 srand(seed) 为特定种子生成固定的随机值。但是,在 JS 中,没有这样的内置函数。

但是,我们可以编写简单而简短的函数。

步骤1:选择一些种子(修复编号)。
var seed = 100;
数字应为正整数且大于 1,步骤 2 中进一步说明。

第 2 步:对 Seed 执行 Math.sin() 函数,它将给出该数字的 sin 值。将此值存储在变量 x 中。

var x; 
x = Math.sin(seed); // Will Return Fractional Value between -1 & 1 (ex. 0.4059..)

sin() 方法返回一个介于 -1 和 1 之间的分数。我们不需要负值,因此,在第一步中选择大于 1 的数字。

第 3 步:返回值是介于 -1 和 1 之间的小数值。因此,将此值乘以 10 使其大于 1。

x = x * 10; // 10 for Single Digit Number

第 4 步:将值乘以 10 以获得更多位数

x = x * 10; // Will Give value between 10 and 99 OR
x = x * 100; // Will Give value between 100 and 999

根据位数的要求相乘。

结果将是十进制的。

第 5 步:通过 Math 的 Round (Math.round()) 方法删除小数点后的值。

x = Math.round(x); // This will give Integer Value.

第 6 步:通过 Math.abs 方法将负值转换为正值(如果有)

x = Math.abs(x); // Convert Negative Values into Positive(if any)

解释结束。最终代码

var seed = 111; // Any Number greater than 1
var digit = 10 // 1 => single digit, 10 => 2 Digits, 100 => 3 Digits and so. (Multiple of 10) 

var x; // Initialize the Value to store the result
x = Math.sin(seed); // Perform Mathematical Sin Method on Seed.
x = x * 10; // Convert that number into integer
x = x * digit; // Number of Digits to be included
x = Math.round(x); // Remove Decimals
x = Math.abs(x); // Convert Negative Number into Positive

干净和优化的功能代码

function random_seed(seed, digit = 1) {
    var x = Math.abs(Math.round(Math.sin(seed++) * 10 * digit));
    return x;
}

然后调用这个函数使用
random_seed(any_number, number_of_digits)
any_number 必须大于 1。
number_of_digits 是可选参数,如果没有传递,将返回 1 位。

random_seed(555); // 1 Digit
random_seed(234, 1); // 1 Digit
random_seed(7895656, 1000); // 4 Digit

这个函数是有偏差的,因为 abs(Math.sin(random_number)) 本身就是一个偏向于 sin 函数斜率为零的点的函数。以下是可以在 js 控制台中运行以进行实验的代码:(pastebin.com/kJyHaQYY)
G
Grimm Jack

对于 0 到 100 之间的数字。

Number.parseInt(Math.floor(Math.random() * 100))

问题是关于播种 Math.random,这样每当 Math.random 播种相同的种子时,它将产生相同的连续随机数系列。据说,这个问题与 Math.random 的实际使用/演示无关。