Suppose you want to draw random numbers from a Normal Distribution with a mean of 33 and variance of 10. There is no "normal distribution object" created automatically in the Swarm kernel, you have to create that in your code. In order to explain how this is done, it is important to understand the two-step nature of the process of creating random numbers from a distribution.
Mathematically speaking, numbers are created as draws from a particular distribution through a two-step process. First, one or more numbers on a given interval are drawn. If one is creating a continuous distribution, the interval is usually [0,1). Then using various formulae from the field of statistics, a draw from a particular distribution is created that depends on the draw(s) in the first step. This two-step process is documented in the literature on simulation. (A very readable and complete treatment is found in Averill M. Law and W.David Kelton, Simulation Modeling and Analysis, New York: McGraw Hill.)
The first stage in the process uses an object called a random number generator. A random number generator is a component that can generate unpredictable numbers within some interval that are "equally likely" to occur. There have been many kinds of procedures proposed for creating numbers that appear to be random. Swarm includes a great many of these. The default random generator, the one that Swarm uses to generate its built-in random number objects, is MT19337. The generator has a period close to 219937 (1 x 106001), so there is no danger of running a simulation long enough for the generator to repeat itself. At one microsecond per call, it would take about 3.2 x 105987 years to exhaust this generator. For comparison, the age of the Universe is `only' 2 x 1010 years! This generator can be asked either for a real number (a variable of type double) between [0,1) or for an integer, which will be uniformly distributed on the range [0,4294967295] = [0,232-1].
In the second stage, the output from the random number generator is used to create a random variate that meets the specificiations of a particular distribution. Of course, some are done more easily than others. If one needs a draw from a Uniform distribution, then the output of the random number generator itself can be used. On the other hand, some distributions require complicated transformations in order to create numbers that appear as if they were generated from the distribution. For many common statistical distributions, the code to transform the uniformly distributed random numbers into other distributions are provided in the Swarm library. While there are some distributions that are not currently supported, they can typically be constructed by users with the existing distributions as building blocks.
The Swarm Random library can be divided into two parts, which parallel the two-stage process we have described. There are
Generators
Distributions
The following sections will dig into the details of these libraries, but first we will offer a couple of simple usage examples.
Suppose one wants to draw numbers from a normal distribution. The normal is a well known distribution and it has known statistical properties. The object "randomGenerator" is created when the Swarm kernel is initialized, so it can be used in any distribution as the random number generator. To create a NormalDist distribution object and connect it to the predefined MT19937 generator, this code will suffice:
#import <random.h> //This includes the Swarm random library id <NormalDist> myNormalDist; //This names your object and adopts the NormalDist protocol myNormalDist = [NormalDist create: [self getZone] setRandomGenerator: randomGenerator]; |
If for some reason, one does not want to use MT19937 as the generator, then one of the other Swarm generators can be selected and explicitly created. The next code example uses a generator called RWC8gen. This code will first create an instance of that generator, then it will create an object to draw normally distributed observations.
int mySeed = 123776; id myGenerator; id <NormalDist> myNormalDist; myGenerator = [RWC8gen create: [self getZone] setStateFromSeed: mySeed]; myNormalDist = [NormalDist create: [self getZone] setRandomGenerator: myGenerator]; |
The random library is designed in a highly versatile way. Each generator must have a "seed" value, a starting place from which to spin out the random numbers. As long as one leaves the seed at the same value, then the stream of random numbers will be replicated each time the program is run. If one does not want to specify a seed, then that chore can be left up to Swarm, which will insert a seed on behalf of the user. The way to create a generator that uses the system default value for the seed is shown here:
myGenerator = [RWC8gen createWithDefaults: [self getZone]]; |
Another example of the versatility of the Swarm random library is in the design of the distribution classes themselves. We have already seen examples in which random numbers are drawn according to user specified requirements. In the case of the Normal distribution, one can draw from a distribution with a mean of 0 and variance of 1.3 with this command:
double sample; sample = [myNormalDist getSampleWithMean: 0.0 withVariance: 1.3]; |
If one expects to want many draws from a distribution with that same set of parameters, then the distribution object can be told to set those values as the defaults. After the default values of the mean and variance are set, then values retrieved from that distribution object can be retrieved with the simpler method getDoubleSample. For example:
[myNormalDist setMean: 0.0 setVariance: 1.3]; sample = [myNormalDist getDoubleSample]; |
Of course, each distribution will have its own parameters and particular methods for setting them. These parameters can be reset at any time.