Skip to content

Commit

Permalink
More careful testing for new prng algorithms.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbau committed May 1, 2015
1 parent f1a4973 commit 881aff1
Show file tree
Hide file tree
Showing 25 changed files with 237 additions and 229 deletions.
11 changes: 5 additions & 6 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ module.exports = function(grunt) {
all: {
files: {
"<%= pkg.name %>.min.js": [ "<%= pkg.name %>.js" ],
"xor/tychei.min.js": [ "xor/tychei.js" ],
"xor/xor4096.min.js": [ "xor/xor4096.js" ],
"xor/xorshift7.min.js": [ "xor/xorshift7.js" ],
"xor/xorwow.min.js": [ "xor/xorwow.js" ],
"xor/xsadd.min.js": [ "xor/xsadd.js" ],
"xor/xor128.min.js": [ "xor/xor128.js" ]
"prng/tychei.min.js": [ "prng/tychei.js" ],
"prng/xor4096.min.js": [ "prng/xor4096.js" ],
"prng/xorshift7.min.js": [ "prng/xorshift7.js" ],
"prng/xorwow.min.js": [ "prng/xorwow.js" ],
"prng/xor128.min.js": [ "prng/xor128.js" ]
},
options: {
preserveComments: false,
Expand Down
63 changes: 58 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ seedrandom.js

Seeded random number generator for JavaScript.

Version 2.3.12
Version 2.4.0

Author: David Bau

Date: 2015-02-19
Date: 2015-05-02

Can be used as a plain script, a Node.js module or an AMD module.

Expand All @@ -19,7 +19,7 @@ Script tag usage
----------------

```html
<script src="//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.3.12/seedrandom.min.js">
<script src="//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.0/seedrandom.min.js">
</script>
```

Expand All @@ -42,8 +42,57 @@ console.log(Math.random()); // As unpredictable as added entropy.
// Use "new" to create a local prng without altering Math.random.
var myrng = new Math.seedrandom('hello.');
console.log(myrng()); // Always 0.9282578795792454

// Use "quick" to get only 32 bits of randomness in a float.
console.log(myrng.quick()); // Always 0.3752569768112153

// Use "int32" to get a 32 bit (signed) integer
console.log(myrng.int32()); // Always 986220731

```

Other Fast PRNG Algorithms
--------------------------

The package includes some other fast PRNGs. To use Richard Brent's
xorgens-4096 PRNG:


```html
<script src="//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.0/prng/xor4096.min.js">
</script>
```

```js
// Use xor4096 for Richard Brent's xorgens-4096 algorithm.
var xorgen = new xor4096('hello.');

// By default provides 32 bits of randomness in a float.
console.log(xorgen()); //

// Use "double" to get 56 bits of randomness.
console.log(xorgen.double()); //

// Use "int32" to get a 32 bit (signed) integer.
console.log(xorgen.int32()); //

````

Besides xor4096, there are several other faster PRNGs available.

|PRNG name | Time vs native | Period, Author |
|-----------|----------------|------------------------------|
|`xor128` | 5.30 ns, 1.3x | 2^128-1, Marsaglia |
|`xorwow` | 5.65 ns, 1.4x | 2^192-2^32, Marsaglia |
|`xorshift7`| 6.70 ns, 1.6x | 2^256-1, Panneton/L'ecuyer |
|`tychei` | 11.35 ns, 2.8x | 2^127, Neves/Araujo (ChaCha) |
|`quick` | 12.20 ns, 3.0x | ~2^1600, Bau (ARC4) |
|`xor4096` | 20.70 ns, 5.0x | 2^4096-2^32, Brent |
|-----------|----------------|------------------------------|
(`quick` is just the 32-bit version of the RC4-based PRNG
originally packaged with seedrandom.)
Node.js usage
-------------
Expand All @@ -69,6 +118,10 @@ console.log(rng()); // Reasonably unpredictable.
// Mixing accumulated entropy.
rng = seedrandom('added entropy.', { entropy: true });
console.log(rng()); // As unpredictable as added entropy.
// Using alternate algorithms, as listed above.
var rng2 = seedrandom.xor4096('hello.')
console.log(rng2());
```
Expand All @@ -93,7 +146,7 @@ Network seeding
---------------
```html
<script src=//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.3.12/seedrandom.min.js>
<script src=//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.0/seedrandom.min.js>
</script>
<!-- Seeds using urandom bits from a server. -->
<script src=//jsonlib.appspot.com/urandom?callback=Math.seedrandom>
Expand Down Expand Up @@ -182,7 +235,7 @@ The random number sequence is the same as version 1.0 for string seeds.
* Version 2.3.6 adds a readable options object argument.
* Version 2.3.10 adds support for node.js crypto (contributed by ctd1500).
* Version 2.3.11 adds an option to load and save internal state.
* Version 2.3.12 adds implementations of fast xor-shift prngs.
* Version 2.4.0 adds implementations of fast xor-shift prngs.

The standard ARC4 key scheduler cycles short keys, which means that
seedrandom('ab') is equivalent to seedrandom('abab') and 'ababab'.
Expand Down
2 changes: 1 addition & 1 deletion component.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "seedrandom",
"version": "2.3.12",
"version": "2.4.0",
"description": "Seeded random number generator for Javascript",
"repository": "davidbau/seedrandom",
"main": "seedrandom.js",
Expand Down
30 changes: 6 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
//
// speed period BigCrush failures
// xor128 1.7x 2^128-1 MatrixRank, LinearComp
// xsadd 1.9x 2^128-1 several when bits are reversed
// xorwow 1.9x 2^192-2^32 CollisionOver, SimpPoker, LinearComp
// xorshift7 2.1x 2^256-1 none
// xor4096 2.1x 2^4128-2^32 none
Expand All @@ -43,7 +42,7 @@
// var i = rng.int32();
//
// If using one of these algorithms in the browser, you can
// include the code directly under the /xor/ directory to avoid
// include the code directly under the /prng/ directory to avoid
// pulling in code for the other algorithms.
//
// Background on these fast xor-based algorithms.
Expand Down Expand Up @@ -71,29 +70,13 @@
// A pure xor-shift generator by George Marsaglia.
// Period: 2^128-1.
// Reported to fail: MatrixRank and LinearComp.
var xor128 = require('./xor/xor128');
var xor128 = require('./prng/xor128');

// xorwow: 29.6 nanoseconds per call, 1.9x native random.
// George Marsaglia's 160-bit xor-shift combined plus weyl.
// Period: 2^192-2^32
// Reported to fail: CollisionOver, SimpPoker, and LinearComp.
var xorwow = require('./xor/xorwow');

// xsadd adds robustness through an addition step; it was
// proposed by Mutsuo Saito and Makoto Matsumoto (author of MT19937)
// (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/XSADD/index.html),
// and it passes all BigCrush tests; although it fails some if
// bits are reversed. Sebastiano Vigna recently proposed another
// algorithm based on this idea, "xorshift128+", which fixes
// weaknesses in the lower bits; unfortunatly, that improved
// algorithm requires 64-bit arithmatic and is not fast in Javascript.

// xsadd: 28.9 nanoseconds per call, 1.9x native random.
// Mutsuo Saito and Makoto Matsumoto's xorshift with an addition.
// Period: 2^128-1.
// Fails no tests in normal mode.
// Fails when bits reversed: LinearComp, MatrixRank, MaxOft, Permutation.
var xsadd = require('./xor/xsadd');
var xorwow = require('./prng/xorwow');

// xorshift7, by François Panneton and Pierre L'ecuyer, takes
// a different approach: it adds robustness by allowing more shifts
Expand All @@ -104,7 +87,7 @@ var xsadd = require('./xor/xsadd');
// François Panneton & Pierre L'ecuyer's 7-shift generator with 256 bits.
// Period 2^256-1.
// No systematic BigCrush failures reported.
var xorshift7 = require('./xor/xorshift7');
var xorshift7 = require('./prng/xorshift7');

// xor4096, by Richard Brent, is a 4096-bit xor-shift with a
// very long period that also adds a Weyl generator. It also passes
Expand All @@ -116,7 +99,7 @@ var xorshift7 = require('./xor/xorshift7');
// Richard Brent's 4096-bit "xorgens" xor shift plus weyl.
// Period: 2^4128-2^32.
// No systematic BigCrush failures reported.
var xor4096 = require('./xor/xor4096');
var xor4096 = require('./prng/xor4096');

// Tyche-i, by Samuel Neves and Filipe Araujo, is a bit-shifting random
// number generator derived from ChaCha, a modern stream cipher.
Expand All @@ -126,11 +109,10 @@ var xor4096 = require('./xor/xor4096');
// Richard Brent's 4096-bit "xorgens" xor shift plus weyl.
// Period: 2^4128-2^32.
// No systematic BigCrush failures reported.
var tychei = require('./xor/tychei');
var tychei = require('./prng/tychei');

var sr = require('./seedrandom');
sr.xor128 = xor128;
sr.xsadd = xsadd;
sr.xorwow = xorwow;
sr.xorshift7 = xorshift7;
sr.xor4096 = xor4096;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "seedrandom",
"version": "2.3.12",
"version": "2.4.0",
"description": "Seeded random number generator for Javascript.",
"main": "index.js",
"keywords": [
Expand Down
43 changes: 23 additions & 20 deletions xor/tychei.js → prng/tychei.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
(function(global, module, define) {

function XorGen(seed) {
var me = this;
var me = this, strseed = '';

// Set up generator function.
me.next = function() {
Expand All @@ -16,36 +16,39 @@ function XorGen(seed) {
a = (a - b) | 0;
me.b = b = (b << 20) ^ (b >>> 12) ^ c;
me.c = (c - d) | 0;
me.d = d = (d << 16) ^ (c >>> 16) ^ a;
me.d = (d << 16) ^ (c >>> 16) ^ a;
return me.a = (a - b) | 0;
};

function init(me, seed) {
me.a = seed | 0;
me.b = (seed / ((1 << 30) * 4)) | 0;
me.c = 2654435769 | 0;
me.d = 1367130551;
// Discard an initial batch of 20 values.
for (var k = 20; k > 0; --k) {
me.next();
}
me.a = 0;
me.b = 0;
me.c = 2654435769 | 0;
me.d = 1367130551;

if (seed === (seed | 0)) {
// Integer seed.
me.a = seed;
} else {
// String seed.
strseed += seed;
}

init(me, seed);
// Mix in string seed, then discard an initial batch of 64 values.
for (var k = 0; k < strseed.length + 20; k++) {
me.a ^= strseed.charCodeAt(k) | 0;
me.next();
}
}

function copy(f, t) {
t.i = f.i;
t.w = f.w;
t.X = f.X.slice();
t.a = f.a;
t.b = f.b;
t.c = f.c;
t.d = f.d;
return t;
};

function impl(seed, opts) {
if (!(seed === seed | 0)) {
// TODO: implement array seeding.
throw new Error('string seeding unimplemented');
}
var xg = new XorGen(seed),
state = opts && opts.state,
prng = function() { return (xg.next() >>> 0) / ((1 << 30) * 4); };
Expand All @@ -60,7 +63,7 @@ function impl(seed, opts) {
prng.int32 = xg.next;
prng.quick = prng;
if (state) {
if (state.X) copy(state, xg);
if (typeof(state) == 'object') copy(state, xg);
prng.state = function() { return copy(xg, {}); }
}
return prng;
Expand Down
1 change: 1 addition & 0 deletions prng/tychei.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 25 additions & 15 deletions xor/xor128.js → prng/xor128.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
(function(global, module, define) {

function XorGen(seed) {
var me = this;
var me = this, strseed = '';

me.x = 0;
me.y = 0;
me.z = 0;
me.w = 0;

// Set up generator function.
me.next = function() {
Expand All @@ -15,25 +20,30 @@ function XorGen(seed) {
return me.w ^= (me.w >>> 19) ^ t ^ (t >>> 8);
};

function init(me, seed) {
if (seed === (seed | 0)) {
// Integer seed.
me.x = seed;
me.y = 0;
me.z = 0;
me.w = 0;
// Discard an initial batch of 64 values.
for (var k = 64; k > 0; --k) {
me.next();
}
} else {
// String seed.
strseed += seed;
}

init(me, seed);
// Mix in string seed, then discard an initial batch of 64 values.
for (var k = 0; k < strseed.length + 64; k++) {
me.x ^= strseed.charCodeAt(k) | 0;
me.next();
}
}

function copy(f, t) {
t.x = f.x;
t.y = f.y;
t.z = f.z;
t.w = f.w;
return t;
}

function impl(seed, opts) {
if (!(seed === seed | 0)) {
// TODO: implement array seeding.
throw new Error('string seeding unimplemented');
}
var xg = new XorGen(seed),
state = opts && opts.state,
prng = function() { return (xg.next() >>> 0) / ((1 << 30) * 4); };
Expand All @@ -48,7 +58,7 @@ function impl(seed, opts) {
prng.int32 = xg.next;
prng.quick = prng;
if (state) {
if (state.X) copy(state, xg);
if (typeof(state) == 'object') copy(state, xg);
prng.state = function() { return copy(xg, {}); }
}
return prng;
Expand Down
1 change: 1 addition & 0 deletions prng/xor128.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion xor/xor4096.js → prng/xor4096.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// implementation also provides for initalizing the generator with
// string seeds, or for saving and restoring the state of the generator.
//
// On Chrome, this prng benchmarks about 4.5 times slower than
// On Chrome, this prng benchmarks about 2.1 times slower than
// Javascript's built-in Math.random().

(function(global, module, define) {
Expand Down
2 changes: 1 addition & 1 deletion xor/xor4096.min.js → prng/xor4096.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 881aff1

Please sign in to comment.