lispcommon-lispclisp

Setting Random State in CLISP


So I was using the function (random 3) to make a random number between I guess 0 and 3 but every time I ran the program it outputted the number 3 every time and when I tried (random 2) it always outputs 1. I looked up what I needed to do and a different post on stack overflow said I needed to initialize the random-state which I assumed I would need to do so I put this code at the top of my program and that's when it went haywire.

(setf *random-state* (current-time))

After I put this code at the top and ran the program I first got an error that looked like this from the console.

*** - Program stack overflow. RESET

I tried running the program again and I got this super wacky error.

*** - handle_fault error2 ! address = 0x137c9500 not in [0x1a920000,0x1aaa7acc) !
SIGSEGV cannot be cured. Fault address = 0x137c9500.
GC count: 0
Space collected by GC: 0 0
Run time: 0 625000
Real time: 0 543820
GC time: 0 0
Permanently allocated: 92512 bytes.
Currently in use: 2734320 bytes.
Free space: 5 bytes.

I then got an error message from Windows that ended lisp.exe and I'm not sure what I did but when the hexadecimal came up I got freaked out. I've looked around the forums and I cannot find a solution to make my random-state code work can someone please help me.

Solution: Instead of using (current-time) I used (make-random-state t) which works perfectly fine so my problem is fixed.


Solution

  • "... every time I ran the program it outputted the number 3...." Where is the code that calls (random 3)? Calling (random 3) repeatedly should return random numbers between 0 and 2, inclusive, and should never return 3. You will get the same pattern each time you start a new session unless you change the random state used by random.

    random accepts an optional random state argument; you don't need to manually set the global *random-state* variable, but it can be convenient. Don't rely on things you know from other languages when learning Common Lisp (or any new language), read the documentation when you have a question. In Common Lisp a random state is not an integer; it is a random state object, and you can create a new one by calling make-random-state.

    The posted code calls (current-time) and sets *random-state* to the returned value. Now, current-time is not a Common Lisp function; it is specific to Clisp, where it prints the current time as a side-effect, returning nil. After setting *random-state* to the result of calling current-time (nil), *random-state* is no longer bound to a random state object, and the call to random is no longer valid. This is probably why your program "went haywire."

    Here is an example function which returns a list of random numbers between 0 and n (below n):

    (defun random-list (n max)
      (loop for i from 1 to n
            collecting (random max)))
    

    Running this from the REPL to create a list of 10 random numbers between 0 and 2, inclusive, and using the default random state. The first line shows the value of *random-state*:

    CL-USER> *random-state*
    #S(RANDOM-STATE #*0101001111100011110111001111101110101101110011101101001010001101)
    CL-USER> (random-list 10 3)
    (2 1 1 0 2 2 0 1 1 0)
    CL-USER> (random-list 10 3)
    (0 2 2 2 2 1 1 2 0 2)
    CL-USER> (random-list 10 3)
    (2 0 2 1 0 0 2 2 1 1)
    

    You can see that each time the function is called, a different list is generated. This is because the random state (*random-state*) has not been reset between calls to random. Restarting the REPL and running the program again will produce exactly the same results because the random state is reset to its initial value:

    CL-USER> *random-state*
    #S(RANDOM-STATE #*0101001111100011110111001111101110101101110011101101001010001101)
    CL-USER> (random-list 10 3)
    (2 1 1 0 2 2 0 1 1 0)
    CL-USER> (random-list 10 3)
    (0 2 2 2 2 1 1 2 0 2)
    CL-USER> (random-list 10 3)
    (2 0 2 1 0 0 2 2 1 1)
    

    Restarting again, we can try setf with make-random-state:

    CL-USER> (setf *random-state* (make-random-state))
    #S(RANDOM-STATE #*0101001111100011110111001111101110101101110011101101001010001101)
    CL-USER> (random-list 10 3)
    (2 1 1 0 2 2 0 1 1 0)
    CL-USER> (random-list 10 3)
    (0 2 2 2 2 1 1 2 0 2)
    CL-USER> (random-list 10 3)
    (2 0 2 1 0 0 2 2 1 1)
    

    This did not work as expected! We got exactly the same results as when we used the default random state; this is because calling make-random-state with a nil argument or no argument returns a copy of the current random state object. You can call make-random-state with t as its argument to return a new random state object. Note that here Clisp has printed the random state object after the call to setf; doing the same thing in SBCL will result in a very different random state object that takes significantly more lines to print.

    CL-USER> (setf *random-state* (make-random-state t))
    #S(RANDOM-STATE #*0101101000011011111101010101100110010101010100001011001111101100)
    CL-USER> (random-list 10 3)
    (0 1 0 0 1 1 1 2 2 1)
    CL-USER> (random-list 10 3)
    (0 0 2 2 0 2 1 0 2 1)
    CL-USER> (random-list 10 3)
    (1 2 1 2 0 0 1 0 2 0)
    

    Now you can see that *random-state* has been set to a new random state object, and the resulting lists are different from the previous lists.