可种子JavaScript随机数生成器

scu*_*ffe 134 javascript random seed

JavaScript Math.random()函数返回0到1之间的随机值,根据当前时间自动播种(类似于Java我相信).但是,我认为没有办法为你设置自己的种子.

如何创建一个随机数生成器,我可以提供自己的种子值,以便我可以生成一个可重复的(伪)随机数序列?

Dav*_*Bau 113

一个选项是http://davidbau.com/seedrandom,这是一个基于RC4的可种子Math.random()替换,具有很好的属性.

  • 大卫·鲍的种子随后变得非常受欢迎,他维持着它[在github上](https://github.com/davidbau/seedrandom).令人遗憾的是ECMAScript已经如此长时间以至于这样的事情并没有包含在语言中.说真的,没有播种! (16认同)
  • 对于任何寻找该项目的 npm 页面的人:https://www.npmjs.com/package/seedrandom (4认同)
  • @EatatJoes,这既是必需的也是可能的,这既是JS的耻辱又是荣耀。可以包含一个文件并获得对Math对象的向后兼容更改,这很酷。布兰丹·艾希(Brendan Eich),可以工作10天,还不错。 (2认同)

ori*_*rip 24

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

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

就像Starkii所说,Mersenne Twister是一个很好的PRNG,但实施起来并不容易.如果你想自己尝试实现一个LCG - 它很容易,具有良好的随机性质(不如Mersenne Twister),你可以使用一些流行的常量.

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));
Run Code Online (Sandbox Code Playgroud)

  • -1这个LCG实现破坏了JavaScript中精确整数的限制,因为`this.a*this.state`可能会导致数字大于2 ^ 53.结果是产量范围有限,对于某些种子可能是非常短的时间.进一步通常使用2的幂来表示"m"导致一些非常明显的模式,当你花费模数运算而不是简单的截断时,无论如何都没有理由不使用素数. (4认同)
  • 模数不应该是2 ^ 31吗?我从[wiki](http://en.wikipedia.org/wiki/Linear_congruential_generator)中读到了这个算法. (2认同)
  • 请注意,从某种意义上说它没有输出数学要求,这是不正确的。换句话说,可以处理大量数字的语言将产生不同的结果。JS扼杀了大量数字并提高了精度(毕竟它们是浮点数)。 (2认同)

Sta*_*kii 22

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

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

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

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

"任何使用算术方法产生随机数的人都处于犯罪状态."
                                                                                                                                                          --- John von Neumann

  • @orip好的,不清楚.你正在考虑Mersenne Twister和下一句关于初始状态;) (3认同)
  • @TobiasP.我指的是使用getSeconds()和getMinutes(),60*60 == 3600种可能的初始状态的组合种子的建议.我不是指Mersenne Twister. (2认同)
  • 问问题者没有提及他们对于任何类型的密码敏感应用程序都需要“适当的”随机数生成。尽管所有答案都是正确的,但只有第一段实际上与所提出的问题有关。也许添加建议解决方案的代码段。 (2认同)

Chr*_*ann 11

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


mip*_*adi 8

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

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


aaa*_*aaa 7

以下是可以提供自定义种子的PRNG.调用SeedRandom将返回随机生成器函数.SeedRandom可以使用无参数调用,以便使用当前时间为返回的随机函数设定种子,或者可以使用1或2个非负片段作为参数调用它,以便使用这些整数对其进行种子处理.由于浮点精度,只有1个值的播种只允许发生器启动到2 ^ 53个不同状态之一.

返回的随机生成器函数采用1个整数参数命名limit,限制必须在1到4294965886范围内,该函数将返回0到limit-1范围内的数字.

function SeedRandom(state1,state2){
    var mod1=4294967087
    var mul1=65539
    var mod2=4294965887
    var mul2=65537
    if(typeof state1!="number"){
        state1=+new Date()
    }
    if(typeof state2!="number"){
        state2=state1
    }
    state1=state1%(mod1-1)+1
    state2=state2%(mod2-1)+1
    function random(limit){
        state1=(state1*mul1)%mod1
        state2=(state2*mul2)%mod2
        if(state1<limit && state2<limit && state1<mod1%limit && state2<mod2%limit){
            return random(limit)
        }
        return (state1+state2)%limit
    }
    return random
}
Run Code Online (Sandbox Code Playgroud)

使用示例:

var generator1=SeedRandom() //Seed with current time
var randomVariable=generator1(7) //Generate one of the numbers [0,1,2,3,4,5,6]
var generator2=SeedRandom(42) //Seed with a specific seed
var fixedVariable=generator2(7) //First value of this generator will always be
                                //1 because of the specific seed.
Run Code Online (Sandbox Code Playgroud)

该生成器具有以下属性:

  • 它有大约2 ^ 64种不同的可能内部状态.
  • 它的周期约为2 ^ 63,比任何人在JavaScript程序中实际需要的都要多.
  • 由于mod值是素数,因此无论选择的限制如何,输出中都没有简单的模式.这与一些简单的PRNG不同,后者表现出一些相当系统的模式.
  • 它会丢弃一些结果,以便无论极限如何都能获得完美的分布.
  • 它相对较慢,在我的机器上每秒运行大约10000万次.

  • 为什么这会产生一种模式?`for(var i = 0; i <400; i ++){console.log("input:("+ i*245 +","+ i*553 +")| output:"+ SeedRandom(i*245, i*553)(20)); }` (2认同)

ben*_*nyl 6

如果您在 Typescript 中编程,我将 Christoph Henkelmann 对此线程的回答中引入的 Mersenne Twister 实现改编为 typescript 类:

/**
 * copied almost directly from Mersenne Twister implementation found in https://gist.github.com/banksean/300494
 * all rights reserved to him.
 */
export class Random {
    static N = 624;
    static M = 397;
    static MATRIX_A = 0x9908b0df;
    /* constant vector a */
    static UPPER_MASK = 0x80000000;
    /* most significant w-r bits */
    static LOWER_MASK = 0x7fffffff;
    /* least significant r bits */

    mt = new Array(Random.N);
    /* the array for the state vector */
    mti = Random.N + 1;
    /* mti==N+1 means mt[N] is not initialized */

    constructor(seed:number = null) {
        if (seed == null) {
            seed = new Date().getTime();
        }

        this.init_genrand(seed);
    }

    private init_genrand(s:number) {
        this.mt[0] = s >>> 0;
        for (this.mti = 1; this.mti < Random.N; this.mti++) {
            var s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30);
            this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253)
                + this.mti;
            /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
            /* In the previous versions, MSBs of the seed affect   */
            /* only MSBs of the array mt[].                        */
            /* 2002/01/09 modified by Makoto Matsumoto             */
            this.mt[this.mti] >>>= 0;
            /* for >32 bit machines */
        }
    }

    /**
     * generates a random number on [0,0xffffffff]-interval
     * @private
     */
    private _nextInt32():number {
        var y:number;
        var mag01 = new Array(0x0, Random.MATRIX_A);
        /* mag01[x] = x * MATRIX_A  for x=0,1 */

        if (this.mti >= Random.N) { /* generate N words at one time */
            var kk:number;

            if (this.mti == Random.N + 1)   /* if init_genrand() has not been called, */
                this.init_genrand(5489);
            /* a default initial seed is used */

            for (kk = 0; kk < Random.N - Random.M; kk++) {
                y = (this.mt[kk] & Random.UPPER_MASK) | (this.mt[kk + 1] & Random.LOWER_MASK);
                this.mt[kk] = this.mt[kk + Random.M] ^ (y >>> 1) ^ mag01[y & 0x1];
            }
            for (; kk < Random.N - 1; kk++) {
                y = (this.mt[kk] & Random.UPPER_MASK) | (this.mt[kk + 1] & Random.LOWER_MASK);
                this.mt[kk] = this.mt[kk + (Random.M - Random.N)] ^ (y >>> 1) ^ mag01[y & 0x1];
            }
            y = (this.mt[Random.N - 1] & Random.UPPER_MASK) | (this.mt[0] & Random.LOWER_MASK);
            this.mt[Random.N - 1] = this.mt[Random.M - 1] ^ (y >>> 1) ^ mag01[y & 0x1];

            this.mti = 0;
        }

        y = this.mt[this.mti++];

        /* Tempering */
        y ^= (y >>> 11);
        y ^= (y << 7) & 0x9d2c5680;
        y ^= (y << 15) & 0xefc60000;
        y ^= (y >>> 18);

        return y >>> 0;
    }

    /**
     * generates an int32 pseudo random number
     * @param range: an optional [from, to] range, if not specified the result will be in range [0,0xffffffff]
     * @return {number}
     */
    nextInt32(range:[number, number] = null):number {
        var result = this._nextInt32();
        if (range == null) {
            return result;
        }

        return (result % (range[1] - range[0])) + range[0];
    }

    /**
     * generates a random number on [0,0x7fffffff]-interval
     */
    nextInt31():number {
        return (this._nextInt32() >>> 1);
    }

    /**
     * generates a random number on [0,1]-real-interval
     */
    nextNumber():number {
        return this._nextInt32() * (1.0 / 4294967295.0);
    }

    /**
     * generates a random number on [0,1) with 53-bit resolution
     */
    nextNumber53():number {
        var a = this._nextInt32() >>> 5, b = this._nextInt32() >>> 6;
        return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以按如下方式使用它:

var random = new Random(132);
random.nextInt32(); //return a pseudo random int32 number
random.nextInt32([10,20]); //return a pseudo random int in range [10,20]
random.nextNumber(); //return a a pseudo random number in range [0,1]
Run Code Online (Sandbox Code Playgroud)

检查源以获取更多方法。