我正在基于1个TSP随机化一个TSP(城市阵列)的算法。
(do ((i 0 (+ i 1)))
((= i n-population))
(setf (aref population i) (shuffle TSP 100))
)
据我所知,我用i
填充了数组population
的(shuffle TSP 100)
位置,这被称为每次迭代,但是算法设置的是所有数组位置,而不仅仅是i位置。
[[Note。此答案的较早版本包含一个错误,它将严重改变混洗的统计信息:请在下面查看更正的版本以及有关问题所在的注释。]
给出您的代码,稍加详细说明即可将其转换为功能:
(defun fill-array-with-something (population n-population TSP)
(do ((i 0 (+ i 1)))
((= i n-population))
(setf (aref population i) (shuffle TSP 100))))
然后population
至0
中(1- n-population)
的每个元素将被设置为(shuffle TSP 100)
的结果。然后有两种可能性:
(shuffle TSP 100)
返回每个调用的新对象;(shuffle TSP 100)
返回每个调用的same对象-可能是TSP
。在第一种情况下,数组的每个元素将具有不同的值。在第二种情况下,n-population
以下的所有元素都将具有same值。
[不知道您的shuffle
函数的功能,这是一个例子,它将给出后者的行为:
(defun shuffle (vec n)
;; shuffle pairs of elts of VEC, N times.
(loop with max = (length vec)
repeat n
do (rotatef (aref vec (random max))
(aref vec (random max)))
finally (return vec)))
我们可以测试一下:
> (let ((pop (make-array 10))
(tsp (vector 0 1 2 3 4 5 6 7 8 9 )))
(fill-array-with-something pop (length pop) tsp)
pop)
#(#(2 8 7 1 3 9 5 4 0 6) #(2 8 7 1 3 9 5 4 0 6) #(2 8 7 1 3 9 5 4 0 6)
#(2 8 7 1 3 9 5 4 0 6) #(2 8 7 1 3 9 5 4 0 6) #(2 8 7 1 3 9 5 4 0 6)
#(2 8 7 1 3 9 5 4 0 6) #(2 8 7 1 3 9 5 4 0 6) #(2 8 7 1 3 9 5 4 0 6)
#(2 8 7 1 3 9 5 4 0 6))
您可以看到所有元素都神秘地是同一件事,这是因为我的shuffle
仅返回了第一个参数,并对其进行了修改。
您可以通过显式检查shuffle
的结果,或者例如使用*print-circle*
查看共享来进行检查。后一种方法非常简洁:
> (let ((*print-circle* t)
(pop (make-array 10))
(tsp (vector 0 1 2 3 4 5 6 7 8 9 )))
(fill-array-with-something pop (length pop) tsp)
(print pop)
(values))
#(#1=#(4 6 7 0 1 2 5 9 3 8) #1# #1# #1# #1# #1# #1# #1# #1# #1#)
现在可以立即看出问题所在。
解决方案是确保shuffle
返回一个新对象,或复制其结果。使用我的shuffle
,可以这样操作:
(defun fill-array-with-something (population n-population tsp)
(do ((i 0 (+ i 1)))
((= i n-population))
(setf (aref population i) (shuffle (copy-seq TSP) 100))))
注意此答案的先前版本为(copy-seq (shuffle TSP 100))
:对于我的shuffle
版本,这是一个严重的错误,因为这意味着population
中的元素相互关联,但越来越多随手洗牌。使用(shuffle (copy-seq TSP) 100)
,每个元素都独立获得相同的混洗量。
和现在
> (let ((*print-circle* t)
(pop (make-array 10))
(tsp (vector 0 1 2 3 4 5 6 7 8 9 )))
(fill-array-with-something pop (length pop) tsp)
(print pop)
(values))
#(#(8 3 4 1 6 9 2 5 0 7) #(8 6 5 1 3 0 4 2 9 7) #(5 0 4 7 1 6 9 3 2 8)
#(3 0 7 6 2 9 4 5 1 8) #(8 2 5 1 7 3 9 0 4 6) #(0 5 6 3 8 7 2 1 4 9)
#(4 1 3 7 8 0 5 2 9 6) #(6 9 1 5 0 7 4 2 3 8) #(2 7 5 8 0 9 6 3 4 1)
#(5 4 8 9 6 7 2 0 1 3))
我怀疑问题出在尚未共享的OP功能SHUFFLE
中;我怀疑SHUFFLE
是将*TSP*
数组本身改组到位,而不是创建该数组的改组后的副本。然后,POPULATION
值都引用相同的改组的*TSP*
数组。
为了解决此问题,SHUFFLE
应该返回经过改组的数组,而不是将数组改组到位。这是一个对向量执行Fisher-Yates随机播放的函数:
(defun shuffle-vector (vect)
"Takes a vector argument VECT and returns a shuffled vector."
(let ((result (make-array (length vect) :fill-pointer 0)))
(labels ((shuffle (v)
(if (zerop (length v))
result
(let* ((i (random (length v)))
(x (elt v i)))
(vector-push x result)
(shuffle (concatenate 'vector
(subseq v 0 i)
(subseq v (1+ i))))))))
(shuffle vect))))
在REPL中测试:
CL-USER> (defvar *TSP* #("Village" "Town" "City" "Metropolis" "Megalopolis"))
*TSP*
CL-USER> (defvar *n-population* 5)
*N-POPULATION*
CL-USER> (defvar *population* (make-array *n-population*))
*POPULATION*
CL-USER> (dotimes (i *n-population*)
(setf (aref *population* i) (shuffle-vector *TSP*)))
NIL
CL-USER> *population*
#(#("Megalopolis" "City" "Metropolis" "Town" "Village")
#("Megalopolis" "Metropolis" "Town" "City" "Village")
#("City" "Megalopolis" "Town" "Village" "Metropolis")
#("City" "Megalopolis" "Village" "Metropolis" "Town")
#("Megalopolis" "Town" "Metropolis" "City" "Village"))