Swarm Random Number Library

This document describes the Swarm random number library, an object oriented library of random number generators and accompanying distributions.


Thoughts

First, a few thoughts on pseudorandom number generation. It's hard to do right. There are many problems: the root cause, of course, is that computer algorithms themselves are not random. But there are also problems with defining "random", coming up with good tests for generators, and implementing algorithms correctly and efficiently. The history of pseudorandom number generation in simulation work is mostly embarassing. This library attempts to do a decent job of generating random numbers, as well as documenting how things work and what shortcomings there are. If you want to learn more about random number generation, the bibliography has useful notes. Knuth is the main reference in this realm, but too old to describe most of the particular generators used here.

The code here represents an effort to implement several efficient, reasonably safe generators. The algorithms come from reading the literature (see the bibliography): these algorithms have been implemented as accurately as possible and run through some simple tests. There is always a chance that some algorithm here is no good - there's also the chance that it is implemented incorrectly.

For best results, library users should test these generators yourself in some domain-specific way. One easy way to do this is to run an experiment twice: once with one class of generator (say, PMMLCG), and once with another (say, SWB). If the results differ radically, then you can suspect the generator. If they don't, well, the generator still might be faulty.


Library Design

This is an object oriented random number library. There are two basic types of objects (described in protocols)

RandomBitGenerator
a generator that puts out random numbers between [0,maxValue]
RandomDistribution
a probability distrubtion generated from a particular generator.

In the simplest case, you can instantiate one RandomBitGenerator and feed it to one RandomDistribution - this will duplicate your conventional libc random() function. (Note: the simtools library does exactly this, providing a uniformRandom object.)

But you can also do more powerful things: you can create multiple RandomBitGenerator objects to have independent streams of random numbers. You can feed several different RandomDistributions off of pne RandomBitGenerator. You have a lot of flexibility.

The Random library uses standard Objective C alloc/init style creation (not defobj createBegin/createEnd).


Random Bit Generators

There are four base classes of random bit generators: PMMLCG, SWB, LCG, ACG. Of these, only SWB and PMMLCG are recommended for use: LCG and ACG are implemented for historical reasons. For each base class of generator, there are several particular classes with specific parameter settings for the generator. You should use these particular classes, not the base class.

Each random bit generator can be initialized in one of two ways
-initSeed: (unsigned) seed;
Seed with a particular number
-init;
Use a default seed defined in RandomBitGenerator.h (getpid() * time(0) at the moment.)

Recommended generators

SWB
Subtract with Borrow lagged fibonacci
X_n = X_(n-s) - X_(n-r) - carry (mod 2^32)
Very efficient, very high period (roughly 2^(r*32)). All bits should be good. see marsaglia-swb in the bibliography.
PMMLCG
Prime Modulus Multiplicative Linear Congruential
X_n+1 = a * X_n (mod 2^31-1)
Reasonably efficient, with reasonably high period (2^31-1). All the bits should be pretty good. See Numerical Recipies for a comment about some occasional serial correlations (the multiplier a is small), and Park & Miller for the particular parameters used here.

Non-recommended generators

LCG
Linear Congruential Generator
X_n+1 = a * X_n + c (mod 2^32)
Nice and efficient, with reasonably high period (2^32, I think) The low bits are terrible, though.
ACG
Additive Congruential Generator
X_n+1 = X_(n-s) + X_(n-r) (mod 2^32)
Efficient, but not very random. Low bits are awful. Period roughly 2^r.
SCG
Subtractive Congruential Generator
X_n+1 = X_(n-r) - X_(n-s) (mod 10^9)
Similar to ACG in performance.

Future work

There are lots of other generators out there: Tauseworth generators, Wolfram's recommendation to use CA rule 30 to generate random bits, and a variety of functions from the cryptographic literature. In addition, any of these generators can be combined with a shuffler that somewhat breaks up the order of return values (this looks to be a good idea with PMMLCG1). Contributions to this library are welcome.


Random Distributions

Random Bit Generators put out some random number in some large range (typically [0,2^32) or [0,2^32-3]). Random Distribution classes take the output of a bit generator (set via setGenerator:) and coerce it into the distribution you want. Currently, only two distributions are implemented.

Uniform

Uniformly distributed random numbers, with some checking to make sure the request can be filled by the given generator. Four access methods are defined:
-(unsigned) rMax: (unsigned) max;
returns a random integer in the range [0, max). Generated by taking the least significant bits from the generator: this is known to be problematic for some generators, but should be ok for SWB and PMMLCG.
-(int) rMin: (int) min Max: (int) max;
returns a random integer in the range [min, max). Generated by taking the least significant bits from the generator: this is known to be problematic for some generators, but should be ok for SWB and PMMLCG.
-(float) rFloat;
Returns a random floating point number in the range [0, 1.0) Note that the number will have 24 bits of random mantissa on typical floating point hardware.
-(double) rDouble;
currently unimplemented (requires more than 32 bits of randomness to generate properly).

Random Bit

A purely random bit, 0 with .5 probability, 1 with .5 probability. In theory this could just be [aUniform rMax: 2], but certain generators are known to have trouble with that. Also, generating random bits could be more efficient as you don't necessarily need to call the generator every time a bit is needed. The current implementation is not that clever.
-(unsigned char) bit;
return a 0 or 1 with .5 probability of each.

Tests

In general, no test can say "yes this sequence is random". Several tests can say "no, it isn't", though. All of the algorithms implemented here are in published literature that claims that the algorithm passed some battery of tests: the tests in the support/random/tests directory are mostly there to debug the implementation. It would be a good idea for users of this library to run these tests and inspect the output.

Each program takes several test-specific arguments, as well as a particular argument which represents which generator to test. Run the test without arguments for help.

chiSquare
A simple chi-squared test of some particular generator. This test only checks to see if the distribution coming out of your generator is roughly uniform. It does not, by any means, prove randomness, but it can weed out obviously bad generators. Chi-square should be within 2*sqrt(numbins) 90% of the time.
printBits
Prints out random numbers from a generator along with their bit patterns. Certain generators (LCG, ACG) obviously look bad just from doing this.
coinFlip
Print out 0s or 1s from a RandomBit distribution for inspection by eye. Also prints out distribution of run lengths. Note that RandomBit is smart enough to detect when an LCG or ACG is given to it: it will compensate (the results without are terrible!).
printPairs
Print pairs of numbers (x_n,x_n+1) (x_n+1,x_n+2) (x_n+2,x_n+3) etc. Feed this output to your favourite plotting package.
checkPMMLCG1, checkLCG1
This test just runs PMMLCG1 or LCG1 from a known seed and checks to make sure the nth result is equal to a published correct value. Use this to test whether arithmetic is right on your machine.

Portability and Technical Details

Implementing random number generators is a fairly difficult exercise in C hacking. Doing math correctly on all 2^32 integers is hard. In most cases, when choosing a tradeoff between speed and safety I've chosen safety. But in some cases, I haven't. In particular, in several generators (all but PMMLCG) I rely on math on unsigneds being exactly math modulo 2^32, including subtraction. In particular, I require things like

  if (in real numbers)             (a - b) < 0
  then (in computer math)          (a - b)
  is equivalent to (real numbers)  (a - b + 2^32)

I think this is safe on all 32 bit unsigned ANSI C implementations. (ANSI specifies that unsigned arithmetic is done mod 2^n). If your machine doesn't have 32 bit unsigneds, this code definitely won't work. If you don't have two's complement arithmetic, it might not: best check it out.

As for performance, all of these generators are roughly the same speed (within 20% of each other), when compiled on a Sparc with 'gcc -O -mv8'. A quick test showed that these generators (called through Uniform: two message sends per random number) were a bit less than twice as slow as calling SunOS4.1.3 random() directly.


Nelson Minar <nelson@santafe.edu>
Last modified: Sun May 12 17:41:57 MDT 1996