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
such as Simulation
Modeling and Analysis, Third Edition
by Law and Kelton (McGraw-Hill, 2000).
Random numbers generated by computers
are actually pseudo-random.
A sequence of values is generated using
a recurrence relation that calculates
the next value in the sequence from
the previous value. The sequence is
begun by specifying a starting value
called a seed.
A good random number generator has the
property that the numbers it produces
have no discernible patterns that distinguish
them from truly random numbers.
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 18
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.
Prototype:
double uniform(double
min, double max)
Prototype:
double triangular(double
min, double max,
double mode)
Prototype:
double beta(double
min, double max, double
shape1, double shape2)
Prototype:
double exponential(double
mean)
Prototype:
double gamma(double
mean, double stddev)
Prototype:
double erlang(double
mean, double var)
Prototype:
double hyperx(double
mean, double var)
Prototype:
double weibull(double
shape, double scale)
Prototype:
double normal(double
mean, double stddev)
Prototype:
double lognormal(double
mean, double stddev)
Prototype:
double cauchy(double
alpha, double beta)
Prototype:
double hypoexponential(double
mn, double var)
Prototype:
double pareto(long
a)
Prototype:
double zipf(long
n)
Prototype:
double zipf_sum(long
n, double *sum)
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 random_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 (STREAM s)
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 built-in type STREAM.
Example:
STREAM s;
Before a stream can be used, it must
be initialized by calling the create_stream
function.
Prototype:
STREAM create_stream(void)
Example:
s = create_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 reseed(STREAM
s, long n)
Example:
reseed(s, 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(STREAM
s)
Example:
i = stream_state(s);
If stream_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 stream is no longer needed, its
storage can be reclaimed by calling
the delete_stream
function.
Prototype:
void delete_stream(STREAM
s)
Example:
delete_stream(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.
For multiple streams, the function names
begin with "stream_"
and the functions have an additional
first parameter that specifies the stream.
The following are two examples.
Single Stream
Prototype: double
uniform(double min,
double max)
Multiple
Stream Prototype: double
stream_uniform(STREAM s,
double min, double max)
Single Stream
Prototype: double
triangular(double min,
double max, double mode)
Multiple
Stream Prototype: double
stream_triangular(STREAM
s, 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.