Skip to content

Instantly share code, notes, and snippets.

@satori99
Last active August 29, 2015 14:24
Show Gist options
  • Save satori99/7c1200f2c0eee8b9e36a to your computer and use it in GitHub Desktop.
Save satori99/7c1200f2c0eee8b9e36a to your computer and use it in GitHub Desktop.
A seedable psuedo-random number generator for browser use
(function(){
/**
* rc4.js - A fast seedable psuedo-random number generator
* @public
* @class
* @param {string} [seed] - seed. IF specified, the first 256 chars will
* be used to seed the RC4 PRNG, otherwise a random character
* string will used.
* @example
* // Get a psuedo-random floating point value
* RC4.random(); // same as Math.random()
* @example
* // Get deterministic psuedo-random floating point values
* var rc4 = new RC4( 'test' );
* rc4.random(); // 0.6818718355790825
* rc4.random(); // 0.14780130176502
* rc4.random(); // 0.9055136215611361
* rc4.reset(); // reset state using existing seed
* rc4.random(); // 0.6818718355790825 (again)
* rc4.random(); // 0.14780130176502 (again)
*/
function RC4 ( seed ) {
this.state = new Uint8Array( 256 );
this.reset( seed );
}
RC4.prototype = {
/** @constructs */
constructor: RC4,
/**
* rc4 seed
* @public
* @type {!string}
*/
seed: undefined,
/**
* rc4 state array
* @public
* @type {Uint8Array}
*/
state: undefined,
/**
* rc4 state ptr
* @public
* @type {number}
*/
i: 0,
/**
* rc4 state ptr
* @public
* @type {number}
*/
j: 0,
/**
* swaps the byte values at the specifed positions in the state buffer
* @private
* @param {!number} i - position one
* @param {!number} j - position two
*/
swap: function( i, j ) {
tmp = this.state[ i ];
this.state[ i ] = this.state[ j ];
this.state[ j ] = tmp;
},
/**
* resets the PRNG state
* @public
* @param {string} [seed] - new seed value. If specified, the first 256 bytes
* will be used to initialize the state buffer, otherwise the
* existing seed will be re-used.
*/
reset: function ( seed ) {
this.seed = ( seed !== undefined ?
String( seed ).trim().slice( 0, 256 ) : false )
|| this.seed
|| Math.random().toString( 36 ).slice( 2 );
this.state.set( Array.apply( 0, Array( 256 ) )
.map( function ( x, y ) { return y; } ) );
for ( var i = 0, j = 0, t = 0; i < 256; i ++ ) {
this.swap( i, j = ( j + this.state[ i ] +
this.seed[ i % this.seed.length ].charCodeAt( 0 ) ) % 256 );
}
this.i = 0;
this.j = 0;
},
/**
* Gets the next byte value from the psuedo-random byte stream
* @public
* @returns {number} random integer between 0 (inc) and 255 (inc)
*/
next: function () {
// consume next byte
this.i = ( this.i + 1 ) % 256;
this.j = ( this.j + this.state[ this.i ] ) % 256;
this.swap( this.i, this.j );
return this.state[ ( this.state[ this.i ] + this.state[ this.j ] ) % 256 ];
},
/**
* Gets a random sign
* @public
* @returns {number} -1 or + 1
*/
sign: function () {
// consume 1 bytes to create a random sign
return ( ( this.next() >> 7 ) << 1 ) - 1;
},
/**
* Gets the next float value from the psuedo-random byte stream
* @public
* @returns {number} a random number between zero (inc) and one (exc).
*/
random: function () {
// consume 7 bytes to create a 53 bit double
var out = this.next();
out = out * 256 + this.next();
out = out * 256 + this.next();
out = out * 256 + this.next();
out = out * 256 + this.next();
out = out * 256 + this.next();
out = out * 256 + this.next();
return out / 0x100000000000004;
},
/**
* Gets a random number in the range -1.0 to 1.0 (exclusive)
* @public
* @returns {number} A random number between -1 and 1
*/
signedRandom: function () {
// consumes 8 bytes from the psuedo-random stream
return this.sign() * this.random();
},
/** @inheritdoc */
toString: function () { return '[object RC4]'; }
}
// temp var for swap operations
var tmp = 0;
// internal instance with random seed for binding static methods
var rc4 = new RC4();
/**
* Gets a random byte
* @public
* @static
* @returns {number} random integer between zero (inc) and 255 (inc)
*/
RC4.next = function () { return rc4.next(); };
/**
* Gets a random sign
* @public
* @static
* @returns {number} positive or negative one
*/
RC4.sign = function () { return rc4.sign(); };
/**
* Gets a random floating point number ( functionally equivalent to `Math.random()`)
* @public
* @static
* @returns {number} a random number between zero (inc) and one (exc).
*/
//
RC4.random = function () { return rc4.random(); };
/**
* Gets a random number in the range -1.0 to 1.0 (exclusive)
* @public
* @static
* @returns {number} a random number between zero (inc) and one (exc).
*/
RC4.signedRandom = function () { return rc4.signedRandom(); };
// export to global scope
self.RC4 = RC4;
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment