We’ve been discussing features of Lisp that the newcomer to the language might not have encountered before, and now I’m going to talk a bit about random. While the newcomer arriving from C++ is probably aware of this function, at least in the abstract, there are some dangers that have to be pointed out for the serious users of pseudo-random numbers.
I am going to assert that there are two categories of users of pseudo-random numbers. First, there are those I’d call the “casual” users. These users are interested in producing one or more pseudo-random numbers with no eye to recovering or replaying those specific numbers later. People generating cryptographic keys, or shuffling a deck of cards for a game, or making a salt or other sequence number, would all fall under the category of casual users. The second group are the “serious” users. These are the Monte Carlo simulator people (among whose ranks I count myself). These people would like to be able to run the same program with 1000 different random number sequences, and would like to be able to re-run specific instances for debugging purposes.
The Common Lisp standard defines its pseudo-random number behaviour in a way that accommodates the casual users, but not the serious users. We’ll start by explaining what the standard does require.
First, the Common Lisp random function takes, as an optional argument, a random state object. This is quite useful, as it allows the programmer, in effect, to produce multiple independent pseudo-random number streams. This is unlike the BSD srandom() and random() functions, which modify a global state and don’t allow for easy manipulation of it. It is, though, somewhat like the GNU libc extensions, srandom_r() and random_r(), which take a state vector as an additional argument.
That random state object used in the Lisp random function can be constructed using make-random-state, operating in one of three modes. The make-random-state function can be used to create a copy of an existing random state, to create a copy of the default random state, or to build a new random state using an implementation-defined technique that is hoped to give a different result every time it is used. None of these use cases allows what the programmer would think of as a “random number seed”, which the Monte Carlo modelers absolutely require for their work. The Common Lisp standard also does not appear to require that the implementation supply a way to produce a readable representation of the current random state, so that it can be recorded and re-loaded from disc.
EDIT #1: 2014-05-18
As pointed out by mch in the comments, the standard does, in fact, require that *random-state* have a *print-readably* representation, so a compliant implementation must be able to store the current random state in a form that can be re-loaded within the same implementation. There is no guarantee, however, that this representation is meaningful to any other Lisp implementation.
If you need to produce multiple reproducible pseudo-random number sequences, you really have two choices. You can either write your own pseudo-random number generator, and use that for your work, or you can check the particular implementation of Lisp that you are using for any non-standard pseudo-random number extensions that may be supplied. For instance, in SBCL there is a non-standard extension, sb-ext:seed-random-state, that allows the programmer to construct a random state variable based on a supplied seed, exactly what the Monte Carlo programmers require.
Actually, the ANSI standard requires random-state objects to have printable/readable representations, but the syntax is implementation-dependent.
Check section 22.1.3.10 “Printing Random States” in the HyperSpec.
mch,
Thank you for the correction, I will update the text.