Most simulations are
random number driven. In such simulations,
random numbers are used for interarrival
times, service times, allocation amounts,
and routing probabilities. For each application
of random numbers in a simulation, a distribution
must be chosen. The distribution determines
the likelihood of different values occurring.
A distribution is uniquely specified by
the name of its family (such as uniform,
exponential, or normal) and its parameter
values (such as the mean and standard
deviation). Discussions of distributions
and their uses in models can be found
in texts [Lake 96].
Most CSIM users need
only read the following two sections,
which describe single stream random number
generation. Those interested in building
multiple-stream simulations should read
the remaining sections as well.
CSIM includes a library
of functions for generating random numbers
from more than 20 different distributions.
Continuous distributions have values that
are floating-point numbers; values from
these distributions are most often used
for amounts of time. Discrete distributions
have values that are integers; values
from these distributions are often used
for quantities of resources.
The following prototypes
are for the functions that generate values
from continuous distributions. The parameters
min
and max
specify the minimum and maximum values
that will be generated. The parameters
mean,
var,
stddev,
and mode specify respectively the mean,
variance, standard deviation, and mode
of the distribution. The parameters shape1,
shape2,
shape,
alpha,
and beta
are all shape parameters whose meaning
can be found in any text that describes
these distributions.
The following prototypes are for the
functions that generate values from
discrete distributions. The parameters
min
and max
specify the minimum and maximum values
that will be generated. The parameter
mean
specifies the mean of the distribution.
The parameters prob_success,
num_trials,
and success_num
are respectively the probability of
success, the number of trials, and the
success number. A text that describes
theses distributions should be consulted
for the detailed meaning of these parameters.
Prototype:
long uniform_int(long min, long max)
Prototype:
long bernoulli(double prob_success)
Prototype:
long binomial(double prob_success,
long
num_trials)
Prototype:
long geometric(double prob_success)
Prototype:
long negative_binomial(long success_num,
double prob_success)
Prototype:
long poisson(double mean)
Two functions must be used to efficiently
generate values from an empirical distribution.
Their prototypes are shown below.
Prototype:
void setup_empirical(long
n, double prob[],
double cutoff[], long alias[])
Prototype:
double
empirical(long n, double cutoff[],
long alias[], double value[])
The setup_empirical
function must be called once, prior
to any calls to function empirical.
It takes as input the number of values,
n,
in the distribution and an array, prob,
that specifies the probability of generating
each value. It calculates two sets of
values and stores them in the arrays
cutoff
and alias.
The contents of these arrays need not
be understood to use this distribution.
All arrays must be of size at least
n+1. Function empirical
is called to generate a value from an
empirical distribution that has already
been set-up. The function takes as input
the same parameters n,
cut-off,
and alias
as the setup_empirical
function. It also takes an array, value,
that contains the values to be generated
with the probabilities that were specified
in array prob.
Each call returns one of the values
in the array value.
By default, the single stream from
which all random numbers are generated
is seeded with the value of 1. Unless
the seed is changed, every execution
of every CSIM program will use the same
sequence of random numbers. The seed
can be changed by calling the reseed
function.
Prototype:
void reseed(stream *s, long n)
Example:
reseed(NIL, 13579);
In simulations that use a single random
number stream, the value of the first
parameter in the function call should
always be NIL. The second parameter
is the positive integer that is to be
used as the seed. The choice of the
seed value will not affect the randomness
of the numbers that are produced. Although
it is most common to call reseed once
at the beginning of a CSIM program,
the reseed
function can be called any number of
times and from any place within a program.
The current state of the stream can
be retrieved by calling the stream_state
function.
Prototype:
long stream::state()
Example:
i = stream_state(NIL);
If stream_state
is called immediately after reseeding
the stream, the seed value will be returned.
Otherwise, the positive integer used
to produce the most recently generated
random number will be returned.
In a single stream simulation, all
random numbers are produced from a single
stream of pseudo-random integers. The
random numbers used for a particular
purpose (for example, interarrival times)
are generated from a subsequence of
these random integers. It is of concern
to some people that the subsequence
of integers may not be "as random"
as the stream from which they were extracted.
This concern can be alleviated by using
a separate stream of pseudo-random integers
for each application of random numbers
in the model. So, separate streams would
be used for the service times at each
facility, for the allocation amounts
of each storage, and so forth.
Multiple streams are also used to guarantee
that exactly the same sequence of random
numbers is used for the interarrival
times (for example) in two different
models. This technique is called common
random numbers and is described in simulation
texts.
There is virtually no difference in
the time required to generate random
number from a single stream or from
multiple streams. Multiple stream simulations
require slightly more programming: the
multiple streams must be declared, initialized,
and (perhaps) seeded, and each call
to a function that generates random
numbers must specify the stream to be
used.
A stream is declared in a CSIM program
using the class stream.
Dynamic
Example: stream
*s;
Before a stream can be used, it must
be initialized by calling the create_stream
constructor.
Prototype:
stream::stream(void)
Example:
s = new stream();
By default, streams are created with
seeds that are spaced 100,000 values
apart. CSIM contains a table of 100
such seed values; if more than 100 streams
are created, the seed values are reused.
The seed value for any stream can be
changed by calling the reseed
function.
Prototype:
void stream::reseed(long
n)
Dynamic Example:
s->reseed(24680);
The second parameter is a positive
integer that is to be used as the new
seed. Although it is most common to
call reseed once for each stream at
the beginning of a CSIM program, streams
can be reseeded any number of times
and at any place in the program.
The current state of a stream can be
retrieved by calling the stream_state
function.
Prototype:
long stream::state()
Dynamic Example:
i = s->state();
If state is called immediately after
reseeding a stream, the seed value will
be returned. Otherwise, the positive
integer used to produce the random number
most recently generated from the stream
will be returned.
If a dynamic stream is no longer needed,
its storage can be reclaimed as follows:
Dynamic
Example: delete
s;
Once a stream has been deleted, it
must not be further referenced.
The same 18 distributions are available
for generating random numbers from multiple
streams as are available for generating
random numbers from a single stream.
The following are two examples:
Multiple
Stream Prototype: double
stream::uniform
(double min, double max)
Multiple
Stream Prototype: double
stream::triangular
(double min, double max,
double mode)
In all other ways, the functions and
their parameters are exactly the same.
It is the programmer's responsibility
to ensure that a stream is used for
only one purpose and that a separate
stream is used for each application
of random numbers in the model.