在 CLISP 中设置随机状态

问题描述

所以我使用函数 (random 3) 在我猜测的 0 和 3 之间生成一个随机数,但是每次我运行程序时,它每次都输出数字 3,而当我尝试 (random 2) 时,它总是输出 1.我查找了我需要做的事情,堆栈溢出的另一篇文章说我需要初始化随机状态,我认为我需要这样做,所以我把这段代码放在我的程序的顶部,那就是它的时候失控了。

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

在我将这段代码放在顶部并运行程序后,我首先从控制台收到了一个类似这样的错误

*** - Program stack overflow. RESET

我再次尝试运行该程序,但出现了这个超级古怪的错误

*** - 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.

然后我从 Windows 收到一条错误消息,该消息以 lisp.exe 结束,我不确定我做了什么,但是当十六进制出现时,我吓坏了。我环顾了论坛,但找不到使我的随机状态代码工作的解决方案,有人可以帮助我。

解决办法: 我没有使用 (current-time),而是使用了 (make-random-state t),它工作得很好,所以我的问题得到了解决

解决方法

...每次我运行程序时,它都会输出数字 3....” 调用 (random 3) 的代码在哪里?重复调用 (random 3) 应该返回 0 到 2 之间的随机数,包括 0 和 2,并且永远不应该返回 3。除非您更改 random 使用的随机状态,否则每次开始新会话时您将获得相同的模式.

random 接受一个可选的随机状态参数;您不需要手动设置全局 *random-state* 变量,但它可以很方便。在学习 Common Lisp(或任何新语言)时不要依赖你从其他语言中知道的东西,当你有问题时read the documentation。在 Common Lisp 中,随机状态不是一个整数;它是一个随机状态对象,您可以通过调用 make-random-state 创建一个新对象。

发布的代码调用 (current-time) 并将 *random-state* 设置为返回值。现在,current-time 不是 Common Lisp 函数;它特定于 Clisp,它打印当前时间作为副作用,返回 nil。将*random-state*设置为调用current-time(nil)的结果后,*random-state*不再绑定随机状态对象,调用random为不再有效。这可能就是您的程序“失控”的原因。

这是一个示例函数,它返回 0 到 n 之间的随机数列表(在 n 下面):

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

从 REPL 运行它以创建 10 个介于 0 和 2(含)之间的随机数列表,并使用默认随机状态。第一行显示 *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)

可以看到,每次调用该函数时,都会生成不同的列表。这是因为随机状态 (*random-state*) 尚未在调用 random 之间重置。重新启动 REPL 并再次运行程序将产生完全相同的结果,因为随机状态被重置为其初始值:

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)

再次重启,我们可以用setf试试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)

这没有按预期工作!我们得到了与使用默认随机状态时完全相同的结果;这是因为调用带有 make-random-state 参数或不带参数的 nil 会返回当前随机状态对象的副本。您可以使用 make-random-state 作为参数调用 t 以返回 new 随机状态对象。注意这里Clisp在调用setf之后打印了随机状态对象;在 SBCL 中做同样的事情会导致一个非常不同的随机状态对象,需要打印更多的行。

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)

现在您可以看到 *random-state* 已设置为 new 随机状态对象,并且生成的列表与之前的列表不同。