| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
This is designed to be a mathematically correct random number generator library for JavaScript. You can use it several times to get familiar with the parameters.
Inspiration was primarily taken from C++11's <random>.
Upgrading from 1.0 to 2.0 is a major, breaking change. For the most part, the way exports are defined is different. Instead of everything being available as static properties on a class-like function, random-js 2.0 exports each binding in accordance with current ECMAScript standards.
Despite Math.random() being capable of producing numbers within [0, 1], there are a few downsides to doing so:
Also, and most crucially, most developers tend to use improper and biased logic as to generating integers within a uniform distribution.
Random.js provides a set of "engines" for producing random integers, which consistently provide values within [0, 4294967295], i.e. 32 bits of randomness.
One is also free to implement their own engine as long as it returns 32-bit integers, either signed or unsigned.
Some common, biased, incorrect tool for generating random integers is as follows:
// DO NOT USE, BIASED LOGIC
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// DO NOT USE, BIASED LOGIC (typical C-like implementation)
function randomIntByModulo(min, max) {
var i = (Math.random() * 32768) >>> 0;
return (i % (min - max)) + min;
}The problem with both of these approaches is that the distribution of integers that it returns is not uniform. That is, it might be more biased to return 0 rather than 1, making it inherently broken.
randomInt may more evenly distribute its bias, but it is still wrong. randomIntByModulo, at least in the example given, is heavily biased to return [0, 67] over [68, 99].
It is vastly more apparent in integers close to 2^32, or really any integer that approaches but not surpass 2^n. Given a perfectly uniform integer distribution, one can play a monte carlo game and run through each possibility of integers. In the following example, assume that we're only dealing with a floating point that holds 4 bits instead of 53 or 32 (which is Math.random()'s bit randomness, based on the engine):
Math.floor(0 * 5) = 0
Math.floor(0.0625 * 5) = 0
Math.floor(0.125 * 5) = 0
Math.floor(0.1875 * 5) = 0
Math.floor(0.25 * 5) = 1
Math.floor(0.3125 * 5) = 1
Math.floor(0.375 * 5) = 1
Math.floor(0.4375 * 5) = 2
Math.floor(0.5 * 5) = 2
Math.floor(0.5625 * 5) = 2
Math.floor(0.625 * 5) = 3
Math.floor(0.6875 * 5) = 3
Math.floor(0.75 * 5) = 3
Math.floor(0.8125 * 5) = 4
Math.floor(0.875 * 5) = 4
Math.floor(0.9375 * 5) = 4As you can see, in this contrived example, we are slightly biased toward rolling a 0 compared to rolling 1-4. Of course, as you add more bits of entropy, the bias shows itself less and less, based on how close your integer distribution is to 2^n.
In order to eliminate bias, sometimes the engine which random data is pulled from may need to be used more than once.
Random.js provides a series of distributions to alleviate this.
Or you can make your own!
interface Engine {
next(): number; // an int32
}Any object that fulfills that interface is an Engine.
One can seed a Mersenne Twister with the same value (MersenneTwister19937.seed(value)) or values (MersenneTwister19937.seedWithArray(array)) and discard the number of uses (mt.discard(count)) to achieve the exact same state.
If you wish to know the initial seed of MersenneTwister19937.autoSeed(), it is recommended to use the createEntropy() function to create the seed manually (this is what autoSeed does under-the-hood).
const seed = createEntropy();
const mt = MersenneTwister19937.seedWithArray(seed);
useTwisterALot(mt); // you'll have to implement this yourself
const clone = MersenneTwister19937.seedWithArray(seed).discard(
mt.getUseCount()
);
// at this point, `mt` and `clone` will produce equivalent valuesRandom.js also provides a set of methods for producing useful data from an engine.
An example of using integer would be as such:
// create a Mersenne Twister-19937 that is auto-seeded based on time and other random values
const engine = MersenneTwister19937.autoSeed();
// create a distribution that will consistently produce integers within inclusive range [0, 99].
const distribution = integer(0, 99);
// generate a number that is guaranteed to be within [0, 99] without any particular bias.
function generateNaturalLessThan100() {
return distribution(engine);
}Producing a distribution should be considered a cheap operation, but producing a new Mersenne Twister can be expensive.
An example of producing a random SHA1 hash:
// using essentially Math.random()
var engine = nativeMath;
// lower-case Hex string distribution
var distribution = hex(false);
// generate a 40-character hex string
function generateSHA1() {
return distribution(engine, 40);
}There is an alternate API which may be easier to use, but may be less performant. In scenarios where performance is paramount, it is recommended to use the aforementioned API.
const random = new Random(
MersenneTwister19937.seedWithArray([0x12345678, 0x90abcdef])
);
const value = r.integer(0, 99);
const otherRandom = new Random(); // same as new Random(nativeMath)This abstracts the concepts of engines and distributions.
To get the exact same behavior as JavaScript's native Math.random(), you can use the nativeMath engine in random-js. This engine internally uses Math.random(), so the results will match exactly.
import { Random, nativeMath } from 'random-js';
// Create a Random instance that uses Math.random() under the hood
const random = new Random(nativeMath);
// Generate a floating-point number in [0, 1], just like Math.random()
const value = random.real(0, 1, false);
console.log(value);In your project, run the following command:
npm install random-jsor
yarn add random-jsIf you wish to get a version without nodeCrypto (which is known to cause issues with some use cases, like some web applications and React Native), run this command:
npm install random-js-no-nodeor
yarn add random-js-no-nodeOr add Webpack 5+ polyfills for crypto to use crypto-browserify in your application's webpack config file (in the case of a web application):
resolve: {
fallback: {
"crypto": require.resolve("crypto-browserify")
}
}Or add Hermes JavaScript polyfill libraries for crypto (in the case of React Native):
npm install crypto-browserify react-native-get-random-valuesThese polyfills may depend on your application's needs, visit the Webpack 5+ polyfills documentation and this Webpack 5+ polyfills cheatsheet to learn more.
For documentation on random-js-no-node, visit the random-js-no-node repository, and for documentation on that package, go to random-js-no-node on NPM.
To import random-js or random-js-no-node, in your code:
// ES6 Modules
import { Random } from "random-js";
const random = new Random(); // uses the nativeMath engine
const value = random.integer(1, 100);// CommonJS Modules
const { Random } = require("random-js");
const random = new Random(); // uses the nativeMath engine
const value = random.integer(1, 100);Or to have more control:
const Random = require("random-js").Random;
const random = new Random(MersenneTwister19937.autoSeed());
const value = random.integer(1, 100);where random-js may be random-js-no-node in these examples, depending on which variant you're using.
It is recommended to create one shared engine and/or Random instance per-process rather than one per file.
Download random.min.js and place it in your project, then use one of the following patterns:
define(function (require) {
var Random = require("random");
return new Random.Random(Random.MersenneTwister19937.autoSeed());
});
define(function (require) {
var Random = require("random");
return new Random.Random();
});
define(["random"], function (Random) {
return new Random.Random(Random.MersenneTwister19937.autoSeed());
});Download random-js.min.js and place it in your project, then add it as a <script> tag as such:
<script src="lib/random-js.min.js"></script>
<script>
// Random is now available as a global (on the window object)
var random = new Random.Random();
alert("Random value from 1 to 100: " + random.integer(1, 100));
</script>You can add your own methods to Random instances, as such:
var random = new Random();
random.bark = function () {
if (this.bool()) {
return "arf!";
} else {
return "woof!";
}
};
random.bark(); //=> "arf!" or "woof!"This is the recommended approach, especially if you only use one instance of Random.
Or you could even make your own subclass of Random:
function MyRandom(engine) {
return Random.call(this, engine);
}
MyRandom.prototype = Object.create(Random.prototype);
MyRandom.prototype.constructor = MyRandom;
MyRandom.prototype.mood = function () {
switch (this.integer(0, 2)) {
case 0:
return "Happy";
case 1:
return "Content";
case 2:
return "Sad";
}
};
var random = new MyRandom();
random.mood(); //=> "Happy", "Content", or "Sad"Or, if you have a build tool are are in an ES6+ environment:
class MyRandom extends Random {
mood() {
switch (this.integer(0, 2)) {
case 0:
return "Happy";
case 1:
return "Content";
case 2:
return "Sad";
}
}
}
const random = new MyRandom();
random.mood(); //=> "Happy", "Content", or "Sad"All the code in Random.js is fully tested and covered using jest.
To run tests in node.js:
npm install
npm testor
yarn install
yarn testThe MIT License (MIT).
See the LICENSE file in this project for more details.
| Back | FazBrowse Home | New Git URL |