# New Ways With Random Numbers, Part I

Today I'd like to introduce a guest blogger, Peter Perkins, who is a statistical software developer here at The MathWorks. In his life prior to The MathWorks, he worked as a statistician doing marine mammal research for the U.S. National Marine Fisheries Service, and one of his interests in MATLAB is random number generation and Monte-Carlo simulation.

### Contents

Almost everyone who's used MATLAB has used the `rand` or `randn` functions at one time or another. The way people use them varies widely, though. Sometimes, you might just want to fill
in a vector or matrix with values other than 0 or 1, in order to try out some new lines of code that you've written. At the
other end of the spectrum, you might be running a Monte-Carlo simulation where the statistical properties of the pseudo-random
values matter.

But regardless of the context, one of the questions I consistently hear is, "How and when should I initialize or reinitialize
or reset MATLAB's random number generator?" The answer I usually give is, annoyingly, another question: "Why do you want to
do that?" That's because messing with the state of a random number generator can affect the statistical properties of the
values it generates, and the right answer to the original question depends on the context - quite often, the answer is "Don't."
There's a lot that could be said about the *when* and the *why*, and I'll touch on that. But mostly, I want to show some of the *how* that's new in MATLAB R2008b.

If you want to run the code that I used to create this blog entry, you'll need R2008b or later, and you'll want to start with a fresh MATLAB session, or else execute these two commands (don't worry, I'll explain what they do).

```
mtstream = RandStream('mt19937ar');
RandStream.setDefaultStream(mtstream);
```

### Random Streams in MATLAB

New in MATLAB R2008b is the addition of random number streams. A random number stream is just what it sounds like: a source
for a sequence of random values. Usually, people talk about streams of values from a standard uniform distribution, and that's
the sense that MATLAB uses too. Actually, MATLAB has always had random number streams in the abstract, underneath `rand` and `randn`. But in R2008b, streams are something you can interact with directly, and you can even create your own streams. There's
lots to talk about there, but this article is about how to initialize or reset a stream, so what follows is just a brief summary.
There's lots more in MATLAB's documentation.

Quite often, you don't need to worry about streams at all; MATLAB creates one for you when it starts up. `rand`, `randn`, and the new `randi` function draw values from that stream, and you can generate random numbers without ever knowing or caring what stream they
come from. And there's nothing wrong with that - if the random number generators in MATLAB are doing their job correctly
(and in R2008b, you have a choice among three generators based on state-of-the-art algorithms), you should be able to just
generate random numbers using `rand`, `randn` or `randi`, and treat everything they return as independent random values.

But how do you go back and reproduce the random values you generate or otherwise control how they're generated? There's a new way to do that: you interact with a new kind of object in MATLAB, one that represents a random stream. Let's get the stream that MATLAB creates at startup.

defaultStream = RandStream.getDefaultStream()

defaultStream = mt19937ar random stream (current default) Seed: 0 RandnAlg: Ziggurat

The "mt19937ar" indicates that this stream generates values using the most common of the Mersenne Twister algorithms, the same algorithm that's been underneath `rand` for several releases. There are other generator choices; more about that in a moment. MATLAB created the stream using a
seed of 0; more about *that* in a moment as well.

What's `RandnAlg`? One of the things that's new in R2008b is that `randn` draws values from the same stream as `rand`, although of course the uniform values have to be transformed to a standard normal distribution. The stream shown here makes
that transformation using the Ziggurat algorithm, although you can change it (sorry, no more about that; you'll have to read the doc).

Finally, the display for this stream shows that it is the *default stream*. That just means that it's the stream that `rand`, `randn`, and `randi` draw their values from. Below, I'll show how you can create your own stream, and make *it* the default. There are a couple of reasons why you might want to do that. But first, let's see how to use the default stream
to reproduce results.

### Resetting a Random Stream

Sometimes it's useful to be able to reproduce the output from MATLAB's random number generators. For example, you might want
to rerun a Monte-Carlo simulation repeatedly, using exactly the same random values because you are debugging the code. In
fact, MATLAB initializes the default random stream to the same internal state each time it starts up, so the same calls to
`rand`, `randn`, and `randi` will return the same "random" values every you start MATLAB (unless you take steps - more about that in a minute). But it
would be tedious to have to restart MATLAB each time you wanted to rerun a simulation.

The simplest way to reproduce random number generator output is to reset the default stream, which puts that stream back to the state it had when initially created.

reset(defaultStream) z1 = randn(1,5)

z1 = 0.53767 1.8339 -2.2588 0.86217 0.31877

If I do it again, I'll get the same values again.

reset(defaultStream) z2 = randn(1,5)

z2 = 0.53767 1.8339 -2.2588 0.86217 0.31877

While simple, `reset` is kind of a blunt instrument, and provides reproducibility only on a very gross scale - it's as if you restarted MATLAB.
It's not something you should do unless you really want to reuse the same random sequence over again.

### Seeding a Stream

All pseudo-random number generators have the notion of a seed. Think of a stream as a very long sequence of values, wrapped into a circle, because it repeats when exhausted. Think of a seed as a starting position on the circle that you specify when you create a stream. There are (usually) many more random values than there are seeds, so it's not possible to start just anywhere by specifying a seed. And there's usually no simple description of where, say, the seed 0 lies in relation to, say, the seeds 1 or 2 other than knowing that they represent different starting positions. But if you create a stream with the same seed, you'll get the same sequence of values every time.

When MATLAB starts, it creates the default stream with commands equivalent to

stream0 = RandStream('mt19937ar','Seed',0) RandStream.setDefaultStream(stream0);

stream0 = mt19937ar random stream Seed: 0 RandnAlg: Ziggurat

We saw that earlier. If I create a new stream with a different seed, such as

stream1 = RandStream('mt19937ar','Seed',1)

stream1 = mt19937ar random stream Seed: 1 RandnAlg: Ziggurat

and make *it* the default stream,

RandStream.setDefaultStream(stream1);

I'll get a *different* sequence of random values.

randn(1,5)

ans = -0.64901 1.1812 -0.75845 -1.1096 -0.84555

But if I always use the same seed, I'll always get the same random number stream. This is a lot like resetting the current
default stream - in fact, `reset` is nothing more than reseeding. The main difference between how I used `reset` above and here is that here, I've created my own stream, so I know *exactly* what I'm getting. Before, I'd get repeatability, but the specific random values depended on what generator algorithm was
underneath the current default stream.

Reseeding is kind of a big hammer - you would not want to create new streams every time you needed a set of random values. It's most useful if you use it as an initialization step, perhaps at MATLAB startup, perhaps before running a simulation.

### Using Substreams for Reproducibility

We've seen that a drawback of using `reset` or `'Seed'` to reproduce results is that you essentially have to start over from the beginning. A new feature in R2008b, substreams,
provides a new way around that drawback.

Remember the circle? Substreams are nothing more than fixed "checkpoints" in a random number stream, defined by their position
around the circle, spaced evenly at very large intervals. The substreams themselves are indexed beginning at 1, and you can
jump to the beginning of a substream by setting a stream's `Substream` property. Values generated from different substreams are statistically independent in the same sense as consecutive values
in a stream are.

Two new generator algorithms in R2008b support substreams: the Combined Multiple Recursive (`'mrg32k3a'`) and the Multiplicative Lagged Fibonacci (`'mlfg6331_64'`) generators (the doc has references). I'll create one of the former, and make it the default stream.

```
stream = RandStream('mrg32k3a');
RandStream.setDefaultStream(stream);
```

To reposition a stream to the beginning of a particular substream, I just set its `Substream` property.

stream.Substream = 2;

Much like seeds, substreams are nothing more than aliases for particular points along a sequence of random values. So what's different about them? There are some differences that I'll get into next time, but for now, the most obvious difference is that you can jump between substreams without having to create a new stream. It's a fairly lightweight operation.

Let's see what happens if I position a random number stream to the beginning of a substream before each iteration of a loop.

for i = 1:5 stream.Substream = i; z = rand(1,i) end

z = 0.72701 z = 0.5582 0.85273 z = 0.16662 0.29236 0.77278 z = 0.34773 0.38864 0.80161 0.95025 z = 0.56709 0.68377 0.29879 0.59318 0.69247

Now I can reproduce results from the 3rd iteration simply by repositioning the stream to the corresponding substream. I didn't have to rerun the whole loop, and I didn't even have to know beforehand that I wanted to look at this iteration.

i = 3; stream.Substream = i; z = rand(1,i)

z = 0.16662 0.29236 0.77278

If that loop were a time-consuming Monte-Carlo simulation, it would be a real boon to be able to go recreate and investigate any specific iteration without rerunning the whole thing.

Keep in mind that you don't need to overuse substreams. They're there to use, you don't have to worry about "using up" all the values in each substream before moving to the next one, but it would be pointless to take the other extreme and jump to a different substream every time you generate a new value. Substreams don't add randomness, they just make it easier to reproduce values.

### Saving and Restoring State

`rand` and `randn` are pseudorandom number generators, and all pseudorandom generator algorithms have an internal state that determines what the next value will be. It's a deterministic
algorithm. Remember the circle? Each point along the circle corresponds to a different state. But for almost all modern
algorithms, the state is more complicated than just the last number generated or a simple index of the current position in
the stream.

Let's get the state of the default generator. To do that, I'll read the `State` property.

```
defaultStream = RandStream.getDefaultStream()
savedState = defaultStream.State;
whos savedState
```

defaultStream = mrg32k3a random stream (current default) Seed: 0 RandnAlg: Ziggurat Name Size Bytes Class Attributes savedState 12x1 48 uint32

If I call `rand` to generate a few random values,

u1 = rand(1,5)

u1 = 0.83911 0.51069 0.84469 0.68938 0.23777

and then I restore the state that I saved,

defaultStream.State = savedState;

`rand` will generate those exact same values the next time I call it.

u2 = rand(1,5)

u2 = 0.83911 0.51069 0.84469 0.68938 0.23777

The same is also true for `randn` and for `randi`. And because all three functions draw values from the same default stream, there's only one state to worry about - the default
stream's state. I can use the state I saved earlier to reproduce results from `randn`, for example.

defaultStream.State = savedState; z1 = randn(1,5)

z1 = 0.048564 0.87031 0.52365 0.095609 -0.79233

defaultStream.State = savedState; z2 = randn(1,5)

z2 = 0.048564 0.87031 0.52365 0.095609 -0.79233

It's important to remember that because there's a single default stream, calling one function affects what specific values the others return.

savedState = defaultStream.State; u1 = rand(1,5) u2 = rand(1,5)

u1 = 0.045212 0.48758 0.75138 0.36639 0.5676 u2 = 0.10969 0.27046 0.10359 0.87516 0.28295

defaultStream.State = savedState; % This returns the same vector as before u1 = rand(1,5) % Call randn in between the two calls to rand z = randn(1,5); % This returns different values than before u2 = rand(1,5)

u1 = 0.045212 0.48758 0.75138 0.36639 0.5676 u2 = 0.20425 0.043083 0.54273 0.10648 0.20139

The functions do not act independently with respect to the values they return, however, the values generated by the three
functions can still be treated as *statistically* independent regardless of what order you generate them in.

One practical application of saving and restoring the state might be to reproduce an entire Monte-Carlo simulation run, by
saving the state before running it. Another might be to recreate one particularly interesting iteration in a Monte-Carlo simulation,
having saved the state before that iteration. Another might be to recreate just one call to `rand` that happened deep in your code, so that you can recreate that call elsewhere, perhaps for debugging. As long as you have
saved the state at the appropriate point, you can "jump to" *anywhere* in the sequence of random values. That's the advantage over resetting or seeding a stream, but it's the catch too: you have
to have saved the right state beforehand.

As an aside, the internal states of the various generators available in MATLAB have different sizes and formats, and you can
see what they are by looking at the `State` property of different streams. But I recommend not getting too familiar with, or modifying, the inner workings of these
state vectors. Read 'n' write 'em, yes, perhaps even rope 'n' brand 'em, but don't try and understand 'em.

### Where Not To Do Any Of This

Sometimes, discretion is the better part of valor. Be careful to understand why and when you do any of this. Remember, resetting
or replacing the default stream, or writing to its state, or jumping to a substream affects all subsequent calls to `rand`, `randn`, and `randi`, and is probably not something you want to do deep in your code - too easy to forget that it is in there. Replacing the
default stream, especially, is probably not something you need to do a lot of.

### Next Time

We've seen four ways to reproduce results from MATLAB's random number generators.

- Resetting a stream
- Creating a new stream with a fixed seed
- Substreams
- Explicitly saving and restoring the generator state

But sometimes what's needed is the "opposite of reproducibility" - you want to make sure that different runs of a simulation
use *different* random inputs, because you want to be able to assume that the different runs are statistically independent. Until next time ...

### Other Situations?

Can you think of any cases where you've needed to reproduce random numbers that can't be handled by the examples I've shown? Let me know here.

**カテゴリ:**- Best Practice,
- New Feature,
- Random

## コメント

コメントを残すには、ここ をクリックして MathWorks アカウントにサインインするか新しい MathWorks アカウントを作成します。