大 N. Common Lisp 结果不正确的代码

code with incorrect result for big N. Common Lisp

下面的代码给出了错误的答案。它应该给出大约 0.5,这是一个包含 0 和 1 之间的许多随机数的数组的平均值。我认为问题是因为 N 是 "to big",或者可能是生成的随机数的精度?。该代码适用于较小的 N 值(10^7、10^6 等)。一些建议会有所帮助。

提前致谢。

(defun randvec(n)
    (let ((arr (make-array n)))
        (dotimes (i n)
            (setf (aref arr i) (random 1.0))
        )
        arr
    )
)

(defparameter N (expt 10 8))
(setf *random-state* (make-random-state t))
(defparameter vector1 (randvec N))
(format t "~a~%" (/ (reduce #'+ vector1) (length vector1)))

浮点数的精度

您正在使用单精度浮点数进行计算。通过将所有随机数相加,您将得到一个单浮点数。添加的数字越多,浮点数就越大。这最终会导致你的结果没有足够的精度。

1.0d0 这样的双浮点数比像 1.0s0 这样的单浮点数具有更高的精度。默认情况下 1.0 被读取为单浮点数。 (RANDOM 1.0d0) 将计算双浮点数。

(defun randvec (n)
  (let ((v (make-array n)))
    (dotimes (i n v)
      (setf (aref v i) (random 1.0d0)))))   ; create a double float random number

(defun test (&optional (n 10))
  (setf *random-state* (make-random-state t))  
  (let ((v (randvec n)))
    (/ (reduce #'+ v) (length v))))

示例:

CL-USER 58 > (test (expt 10 8))
0.4999874882753848D0

风格

使用 Common Lisp 编程时请使用 common Lisp 编程风格:

  • 如果没有必要,不要使用全局变量。改为使用局部变量编写函数。
  • 如果用defparameter定义全局变量,不要命名为n,而是*n*.
  • 正确格式化和缩进代码。缩进应该在编辑器的帮助下完成。
  • 不要在自己的行上使用括号。

参见上面的示例。