Using Random-Number Functions and CALL Routines
Types of Random-Number Functions
There are two types
of random-number functions in SAS. The newest random-number function
is the RAND function. It uses the Mersenne-Twister pseudo-random number
generator (RNG) that was developed by Matsumoto and Nishimura (1998).
This RNG has a very long period of 219937 –
1, and has very good statistical properties. (A period is
the number of occurrences before the pseudo-random number sequence
repeats.)
The
RAND function is started with a single seed. However, the state of
the process cannot be captured by a single seed, which means that
you cannot stop and restart the generator. Use the STREAMINIT function
to produce a sequence of values that begins at the beginning of a
stream. For
more information, see the Details section of the RAND Function.
The older random-number
functions include the UNIFORM, NORMAL, RANUNI, RANNOR, and other functions
that begin with RAN. These functions have a period of only 231 –
2 or less. The pseudo-random number stream is started with a single
seed, and the state of the process can be captured by a new seed.
You can stop and restart the generator by providing the proper seed
to the corresponding CALL routine. You can use the random-number functions
to produce a sequence of values that begins in the middle of a stream.
Seed Values
Random-number
functions and CALL routines generate streams of pseudo-random numbers
from a starting point called a seed, that
either the user or the computer clock supplies. A seed must be a nonnegative
integer with a value less than 231–1
(or 2,147,483,647). If you use a positive seed, you can replicate
the stream of random numbers by using the same DATA step. If you use
zero as the seed, the computer clock initializes the stream, and the
stream of random numbers cannot be replicated.
Understanding How Functions Generate a Random-Number Stream
Using the DATA Step to Generate a Single Stream of Random Numbers
The
DATA steps in this section illustrate several properties of the random-number
functions. Each of the DATA steps that call a function generates a
single stream of pseudo-random numbers based on a seed value of 7,
because that is the first seed for the first call for every step.
Some of the DATA steps have single function calls and others have
multiple function calls. None of these DATA steps change the seed.
The only seed that is relevant to the function calls is the seed that
was used with the first execution of the first random-number function.
You cannot create separate streams with functions. CALL routines are
used for this purpose. The only way to restart the function random-number
stream is to start a new DATA step.
This example executes
multiple DATA steps:
/* This DATA step produces a single stream of random numbers */
/* based on a seed value of 7. */
data a;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
a = ranuni (7); output;
run;
/* This DATA step uses a DO statement to produce a single */
/* stream of random numbers based on a seed value of 7. */
data b (drop = i);
do i = 7 to 18;
b = ranuni (i);
output;
end;
run;
/* This DATA step uses a DO statement to produce a single */
/* stream of random numbers based on a seed value of 7. */
data c (drop = i);
do i = 1 to 12;
c = ranuni (7);
output;
end;
run;
/* This DATA step calls the RANUNI and the RANNOR functions */
/* and produces a single stream of random numbers based on */
/* a seed value of 7. */
data d;
d = ranuni (7); f = ' '; output;
d = ranuni (8); f = ' '; output;
d = rannor (9); f = 'n'; output;
d = .; f = ' '; output;
d = ranuni (0); f = ' '; output;
d = ranuni (1); f = ' '; output;
d = rannor (2); f = 'n'; output;
d = .; f = ' '; output;
d = ranuni (3); f = ' '; output;
d = ranuni (4); f = ' '; output;
d = rannor (5); f = 'n'; output;
d = .; f = ' '; output;
run;
/* This DATA step calls the RANNOR function and produces a */
/* single stream of random numbers based on a seed value of 7. */
data e (drop = i);
do i = 1 to 6;
e = rannor (7); output;
e = .; output;
end;
run;
/* This DATA step merges the output data sets that were */
/* created from the previous five DATA steps. */
data five;
merge a b c d e;
run;
/* This procedure writes the output from the merged data sets. */
proc print label data=five;
options missing = ' ';
label f = '00'x;
title 'Single Random Number Streams';
run;The following output
shows the program results.
Results from Generating a Single Random-Number Stream

The pseudo-random number
streams in output data sets a, b, and c are identical. The stream
in output data set d mixes calls to the RANUNI and RANNOR functions.
In observations 1, 2, 5, 6, 9, and 10, the values that are returned
by RANUNI match the values in the previous streams. Observations 3,
7, and 11, which are flagged by “n”, contain the values
that are returned by RANNOR. The mix of the function calls does not
affect the generation of the pseudo-random number stream. The results
are based on a single stream of uniformly distributed values. Some
values are transformed and returned from other functions such as RANNOR.
The results of the RANNOR function are produced from two internal
calls to RANUNI. The DATA step that creates output data set d executes
these steps three times to create 12 observations:
-
call to RANUNI
-
call to RANUNI
-
call to RANNOR (which internally calls RANUNI twice)
-
skipped line to compensate for the second internal call to RANUNI
In the DATA step that
creates data set e, RANNOR is called six times. Each time, RANNOR
skips a line to compensate for the two internal calls to RANUNI. The
three values that are returned from RANNOR in the DATA step that creates
data set d match the corresponding values in data set e.
Using the %SYSFUNC Macro to Generate a Single Stream of Random Numbers
When
the RANUNI function is called through the macro language by using
%SYSFUNC, one pseudo-random number stream is created. You cannot change
the seed value unless you close SAS and start a new SAS session. The
%SYSFUNC macro produces the same pseudo-random number stream as the
DATA steps that generated the data sets a, b, and c for the first
macro invocation. Any subsequent macro calls produce a continuation
of the single stream.
%macro ran;
%do i = 1 %to 12;
%let x = %sysfunc (ranuni (7));
%put &x;
%end;
%mend;
%ran;SAS writes the following
output to the log:
Results of Execution with the %SYSFUNC Macro
10 %macro ran; 11 %do i = 1 %to 12; 12 %let x = %sysfunc (ranuni (7)); 13 %put &x; 14 %end; 15 %mend; 16 %ran; 0.29473798875451 0.79062100955779 0.79877014262544 0.81579051763554 0.45121804506109 0.78494144826426 0.80085421204606 0.72184205973606 0.34855818345609 0.46596586120592 0.73522999404707 0.66709365028287
Comparison of Seed Values in Random-Number Functions and CALL Routines
Each random-number function
and CALL routine generates pseudo-random numbers from a specific statistical
distribution. Each random-number function requires a seed value expressed
as an integer constant or a variable that contains the integer constant.
Each CALL routine calls a variable that contains the seed value. Also,
every CALL routine requires a variable that contains the generated
pseudo-random numbers.
The seed variable must
be initialized before the first execution of the function or CALL
routine. After each execution of a function, the current seed is updated
internally, but the value of the seed argument remains unchanged.
However, after each execution of the CALL routine, the seed variable
contains the current seed in the stream that generates the next pseudo-random
number. For a function, it is not possible to control the seed values.
The pseudo-random numbers are generated after the initialization.
SAS provides a CALL
routine that has the same name as each random-number function. The
exceptions are the NORMAL and UNIFORM functions, which are equivalent
to the RANNOR and RANUNI functions. Using CALL routines gives you
greater control over the seed values.
Generating Multiple Streams from Multiple Seeds in Random-Number CALL Routines
Overview of Random-Number CALL Routines and Streams
You can use random-number
CALL routines to generate multiple streams of pseudo-random numbers
within a single DATA step. If you supply a different seed value to
initialize each seed variable, the streams of generated pseudo-random
numbers are computationally independent, but the generated numbers
might not be statistically independent unless you select the seed
values carefully.
Note: As a best practice, create
a single stream. As multiple streams, become longer, the chances of
the stream overlapping increase.
These two examples show
how to produce multiple streams by using multiple seeds. Although
this practice is not recommended, you can use the random-number CALL
routines with multiple seeds.
Example 1: Using Multiple Seeds to Generate Multiple Streams
The first DATA step
creates a data set with three variables that are normally distributed.
The second DATA step creates variables that are uniformly distributed.
The SGSCATTER procedure shows the relationship between each pair of
variables for each distribution.
data normal;
seed1 = 11111;
seed2 = 22222;
seed3 = 33333;
do i = 1 to 10000;
call rannor(seed1, x1);
call rannor(seed2, x2);
call rannor(seed3, x3);
output;
end;
run;
data uniform;
seed1 = 11111;
seed2 = 22222;
seed3 = 33333;
do i = 1 to 10000;
call ranuni(seed1, x1);
call ranuni(seed2, x2);
call ranuni(seed3, x3);
output;
end;
run;
proc sgscatter data = normal;
title 'Nonindependent Random Normal Variables';
plot x1*x2 x1*x3 x3*x2 / markerattrs = (size = 1);
run;
proc sgscatter data = uniform;
title 'Nonindependent Random Uniform Variables';
plot x1*x2 x1*x3 x3*x2 / markerattrs = (size = 1);
run;
Multiple Streams from Multiple Seeds: Nonindependent Random
Normal Variables

Multiple Streams from Multiple Seeds: Nonindependent Random
Uniform Variables

Multiple Streams from Multiple Seeds: Nonindependent Random Normal Variables
shows that normal variables appear to be linearly uncorrelated, but
they are not independent. Multiple Streams from Multiple Seeds: Nonindependent Random Uniform Variables shows that uniform variables are clearly
related. With this class of random-number generators, there is never
any guarantee that the streams are independent.
Example 2: Using Different Seeds with the CALL RANUNI Routine
The following example
uses three different seeds and the CALL RANUNI routine to produce
multiple streams.
data uniform(drop=i);
seed1 = 255793849;
seed2 =1408147117;
seed3 = 961782675;
do i=1 to 10000;
call ranuni(seed1, x1);
call ranuni(seed2, x2);
call ranuni(seed3, x3);
i2 = lag(x2);
i3 = lag2(x3);
output;
end;
label i2='Lag(x2)' i3='Lag2(x3)';
run;
title 'Random Uniform Variables with Overlapping Streams';
proc sgscatter data=uniform;
plot x1*x2 x1*x3 x3*x2 / markerattrs = (size = 1);
run;
proc sgscatter data=uniform;
plot i2*x1 i3*x1 / markerattrs = (size = 1);
run;
proc print noobs data=uniform(obs=10);
run;Using Different Seeds with CALL RANUNI: Random Uniform Variables
with Overlapping Streams, Plot 1

Using Different Seeds with CALL RANUNI: Random Uniform Variables
with Overlapping Streams, Plot 2

Random Uniform Variables with Overlapping Streams

Using Different Seeds with CALL RANUNI: Random Uniform Variables with Overlapping
Streams, Plot 1
shows expected results: the variables appear to be statistically independent.
However, Using Different Seeds with CALL RANUNI: Random Uniform Variables with Overlapping
Streams, Plot 2 and the first 10 observations show that there is almost
complete overlap between the two streams. The last 9999 values in
x1 match the first 9999 values in x2, and the last 9998 values in
x1 match the first 9998 values in x3. In other words, there is perfect
agreement between the nonmissing parts of x1 and lag(x2) and between
x1 and lag2(x3). Even if the streams appear to be independent at first
glance as in the first plot, there might be overlap, which might be
undesirable depending on how the streams are used.
If
you make multiple small streams with separate and randomly selected
seeds, you probably do not encounter the problems that are shown in
the first two examples. Using Different Seeds with CALL RANUNI: Random Uniform Variables with Overlapping
Streams, Plot 2 deliberately selects seeds to illustrate worst-case scenarios.
It is always safer to
create a single stream. With multiple streams, as the streams get
longer, the chances of the streams overlapping increase.
Generating Multiple Variables from One Seed in Random-Number Functions
Overview of Functions and Streams
If you use functions
in your program, you cannot generate more than one stream of pseudo-random
numbers by supplying multiple seeds within a DATA step.
The following example
uses the RANUNI function to show the safest way to create multiple
variables from the same stream with a single seed.
Example: Generating Random Uniform Variables with Overlapping Streams
In the following example,
the RANUNI function is used to create random uniform variables with
overlapping streams. The example shows the safest way to create multiple
variables by using the RANUNI function. All variables are created
from the same stream with a single seed.
data uniform(drop=i);
do i = 1 to 10000;
x1 = ranuni(11111);
x2 = ranuni(11111);
x3 = ranuni(11111);
i2 = lag(x2);
i3 = lag2(x3);
output;
end;
label i2 = 'Lag(x2)' i3 = 'Lag2(x3)';
run;
title 'Random Uniform Variables with Overlapping Streams';
proc sgscatter data = uniform;
plot x1*x2 x1*x3 x3*x2 / markerattrs = (size = 1);
run;
proc sgscatter data = uniform;
plot i2*x1 i3*x1 / markerattrs = (size = 1);
run;Random Uniform Variables with Overlapping Streams: Plot 1

Random Uniform Variables with Overlapping Streams: Plot 2

In Example: Generating Random Uniform Variables with Overlapping Streams, it appears that the variables are independent. The random-number
functions and CALL routines have a period of only 231 -
2 or less (approximately 2.1 billion). When this limit is reached,
the stream repeats. Computers that perform complicated simulations
can easily exhaust the entire stream in minutes.
Using the RAND Function as an Alternative
A better approach to
generating random uniform variables is to use the RAND function, where
multiple streams are not permitted. The RAND function has a period
of 219937 - 1. This limit is not reached,
with computers of the early 21st century. The number 219937 -
1 is approximately 106000 (1 followed by
6,000 zeros). In comparison, the largest value that can be represented
in eight bytes on most computers that run SAS is approximately 10307.
The RAND function does
not allow multiple streams. The RAND function uses a different algorithm
from the random-number CALL routines. These CALL routines can create
multiple streams with multiple seeds. Because the state of the RAND
process cannot be captured by a single seed, you cannot stop and restart
the generator. Therefore, the RAND function allows only a single stream
of numbers, but it can be used to make multiple streams, just as the
RANUNI function can.
Effectively Using the Random-Number CALL Routines
Starting, Stopping, and Restarting a Stream
An effective use of
the random-number CALL routines is starting and stopping a single
stream, provided the stream never exhausts the RANUNI stream. For
example, you might want SAS to perform iterations, stop, evaluate
the results, and then restart the stream at the point that it stopped.
In this example, the
random-number CALL routine generates a stream of five numbers, stops
processing, restarts, and then generates five more numbers from the
same stream. It then combines the result, and generates the full stream
for comparison. In the first DATA step, the state of the random-number
seed is stored in a macro variable seed for use as the starting seed
in the next step. The separate streams in the example output match
the full stream.
data u1(keep=x);
seed = 104;
do i = 1 to 5;
call ranuni(seed, x);
output;
end;
call symputx('seed', seed);
run;
data u2(keep=x);
seed = &seed;
do i = 1 to 5;
call ranuni(seed, x);
output;
end;
run;
data all;
set u1 u2;
z = ranuni(104);
run;
proc print label;
title 'Random Uniform Variables with Overlapping Streams';
label x = 'Separate Streams' z = 'Single Stream';
run;Starting, Stopping, and Restarting a Stream

Comparison of Changing the Seed in a CALL Routine and in a Function
If you use a CALL routine
to change the seed, the results are different from using a function
to change the seed:
data seeds;
retain Seed1 Seed2 Seed3 104;
do i = 1 to 10;
call ranuni(Seed1,X1);
call ranuni(Seed2,X2);
X3 = ranuni(Seed3);
if i = 5 then do;
Seed2 = 17;
Seed3 = 17;
end;
output;
end;
run;
proc print data = seeds;
title 'Random Uniform Variables with Overlapping Streams';
id i;
run;Changing Seeds in a CALL Routine and in a Function

Changing Seed2 in the
CALL RANUNI statement when i=5 forces the stream for X2 to deviate
from the stream for X1. However, changing Seed3 in the RANUNI function
has no effect. The X3 stream continues as if nothing has changed,
and the X1 and X3 streams are the same.
Copyright © SAS Institute Inc. All Rights Reserved.
Last updated: March 16, 2017