Seeding the random number generator in Javascript

Viewed 306983

Is it possible to seed the random number generator (Math.random) in JavaScript?

18 Answers

It's not possible to seed the builtin Math.random function, but it is possible to implement a high quality RNG in Javascript with very little code.

Javascript numbers are 64-bit floating point precision, which can represent all positive integers less than 2^53. This puts a hard limit to our arithmetic, but within these limits you can still pick parameters for a high quality Lehmer / LCG random number generator.

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())

If you want even higher quality random numbers, at the cost of being ~10 times slower, you can use BigInt for the arithmetic and pick parameters where m is just able to fit in a double.

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
    }
}

See this paper by Pierre l'Ecuyer for the parameters used in the above implementations: https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/S0025-5718-99-00996-5.pdf

And whatever you do, avoid all the other answers here that use Math.sin!

Math.random no, but the ran library solves this. It has almost all distributions you can imagine and supports seeded random number generation. Example:

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

Here's the adopted version of Jenkins hash, borrowed from 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;
  };
}

You can use it like this:

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

deterministicRandom()
// => 0.34880331158638

Most of the answers here produce biased results. So here's a tested function based on 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
}

test for true randomness of this code: https://es6console.com/kkjkgur2/

No, like they said it is not possible to seed Math.random() but you can install external package which make provision for that. i used these package which can be install using these command

npm i random-seed

the example is gotten from the package documentation.

var seed = 'Hello World',
rand1 = require('random-seed').create(seed),
rand2 = require('random-seed').create(seed);
console.log(rand1(100), rand2(100));

follow the link for documentation https://www.npmjs.com/package/random-seed

A simple approach for a fixed seed:

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

In PHP, there is function srand(seed) which generate fixed random value for particular seed. But, in JS, there is no such inbuilt function.

However, we can write simple and short function.

Step 1: Choose some Seed (Fix Number).
var seed = 100;
Number should be Positive Integer and greater than 1, further explanation in Step 2.

Step 2: Perform Math.sin() function on Seed, it will give sin value of that number. Store this value in variable x.

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

sin() method returns a Fractional value between -1 and 1.
And we don't need Negative value, therefore, in first step choose number greater than 1.

Step 3: Returned Value is a Fractional value between -1 and 1.
So mulitply this value with 10 for making it more than 1.

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

Step 4: Multiply the value with 10 for additional digits

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

Multiply as per requirement of digits.

The result will be in decimal.

Step 5: Remove value after Decimal Point by Math's Round (Math.round()) Method.

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

Step 6: Turn Negative Values into Positive (if any) by Math.abs method

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

Explanation End.

Final Code

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

Clean and Optimized Functional Code

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

Then Call this function using
random_seed(any_number, number_of_digits)
any_number is must and should be greater than 1.
number_of_digits is optional parameter and if nothing passed, 1 Digit will return.

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