在Javascript中种子随机数生成器

577

在JavaScript中,是否可能对随机数生成器 (Math.random) 进行种子初始化?


4
不清楚你是想使用相同的种子来重复获得相同的测试结果,还是想为每个用户提供“独特的东西”以获得更好的随机性。 - simbo1905
5
抱歉,很遗憾它是不可能的。jsrand 是我写的一个小型库,用于生成可种子化的伪随机数。你也可以在谷歌上搜索到其他更复杂的库。 - Domenico De Felice
14
补充问题:提供没有种子生成方法的伪随机数生成器可能是个好主意吗?这样做有什么合理的原因吗? - Alan
1
请参见 https://dev59.com/KHRC5IYBdhLWcg3wD8xV - Palimondo
2
这是此页面上一些生成器的可视化 https://observablehq.com/@tidwall/hello-randomness - tidwall
7
我认为可能没有种子是因为底层算法取决于浏览器 - 如果 Math.random() 有一个种子,那么这些种子不能保证在不同的浏览器中产生相同的结果。 - codeulike
21个回答

2

1
不,就像他们说的那样,无法对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


1
这里有很多好的答案,但我遇到了类似的问题,即我希望在Java的随机数生成器和我最终在JavaScript中使用的任何东西之间具有可移植性。
我发现了java-random package
假设种子相同,这两个代码片段的输出完全相同:
Java:
Random randomGenerator = new Random(seed);
int randomInt;
for (int i=0; i<10; i++) {
    randomInt = randomGenerator.nextInt(50);
    System.out.println(randomInt);
}

JavaScript:

let Random = require('java-random');
let rng = new Random(seed);
for (let i=0; i<10; i++) {
    let val = rng.nextInt(50);
    console.log(val);
}

1

这是Jenkins哈希函数的采用版本,参考自此处

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

1

1

按照bryc的建议去做...但在使用他的cyrb128哈希函数进行初始化之前,请注意返回语句会丢弃32位熵。将四个值进行异或运算,结果应该等于0。你可能需要将第一个元素(h2^h3^h4) >>> 0。


那是一个错误。已修复。不过你应该留下评论而不是单独回答。 ;) - bryc
1
谢谢承认,bryc,我同意但是没有足够的声望来评论。 - Boddle

0

这里大多数答案都会产生偏见的结果。因此,这是一个基于github上的seedrandom库测试过的函数:

!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/


-3
我编写了一个函数,它返回一个种子随机数,它使用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)
      }

1
这种方式产生的数字序列并不真正近似于随机数序列的特性。使用它生成15个数字,得到的字符串几乎总是以7开头,而且对于任何密钥都是如此。 - Gabriel

-4
一个固定种子的简单方法:
function fixedrandom(p){
    const seed = 43758.5453123;
    return (Math.abs(Math.sin(p)) * seed)%1;
}

-6
在PHP中,有一个函数srand(seed),它为特定的种子生成固定的随机值。但是,在JS中,没有这样的内置函数。
然而,我们可以编写简单而短小的函数。
步骤1:选择一些种子(固定数字)。 var seed = 100; 数字应为正整数且大于1,在步骤2中有进一步解释。
步骤2:对种子执行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())移除小数点后的值。

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(任意数字, 数字位数)
任意数字 是必须的,且应大于1。
数字位数 是可选参数,如果未传递任何值,则返回1位数字。

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

2
这个函数是有偏差的,因为abs(Math.sin(random_number))本身就是一个围绕正弦函数斜率为零点偏差的函数。以下是可以在js控制台中运行的代码进行实验:(https://pastebin.com/kJyHaQYY) - penduDev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接