仅更改数组Clisp上的一个位置

问题描述 投票:0回答:2

我正在基于1个TSP随机化一个TSP(城市阵列)的算法。

(do ((i 0 (+ i 1)))
    ((= i n-population))
        (setf (aref population i) (shuffle TSP 100))
    )

据我所知,我用i填充了数组population(shuffle TSP 100)位置,这被称为每次迭代,但是算法设置的是所有数组位置,而不仅仅是i位置。

lisp common-lisp
2个回答
2
投票

[[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))))

然后population0(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))

2
投票

我怀疑问题出在尚未共享的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"))
© www.soinside.com 2019 - 2024. All rights reserved.