Search 如何减少8字谜A*搜索中的长执行时间
我正在尝试对Lisp中的“8字谜”实施启发式搜索策略A* 要运行搜索,请使用以下命令: (01 2 3 4 5 6 B 7)(01 2 3 4 5 6 7 B) 其中第一个状态是开始目标,第二个状态是结束目标 然而,我的程序运行了很长一段时间。最后,我假设它将堆栈溢出*编辑:它不会耗尽内存,但是它花了30分钟,比我的广度优先搜索要长得多 搜索算法代码:Search 如何减少8字谜A*搜索中的长执行时间,search,lisp,common-lisp,heuristics,sliding-tile-puzzle,Search,Lisp,Common Lisp,Heuristics,Sliding Tile Puzzle,我正在尝试对Lisp中的“8字谜”实施启发式搜索策略A* 要运行搜索,请使用以下命令: (01 2 3 4 5 6 B 7)(01 2 3 4 5 6 7 B) 其中第一个状态是开始目标,第二个状态是结束目标 然而,我的程序运行了很长一段时间。最后,我假设它将堆栈溢出*编辑:它不会耗尽内存,但是它花了30分钟,比我的广度优先搜索要长得多 搜索算法代码: ;;; This is one of the example programs from the textbook: ;;; ;;; Artif
;;; This is one of the example programs from the textbook:
;;;
;;; Artificial Intelligence:
;;; Structures and strategies for complex problem solving
;;;
;;; by George F. Luger and William A. Stubblefield
;;;
;;; Corrections by Christopher E. Davis (chris2d@cs.unm.edu)
;;; insert-by-weight will add new child states to an ordered list of
;;; states-to-try.
(defun insert-by-weight (children sorted-list)
(cond ((null children) sorted-list)
(t (insert (car children)
(insert-by-weight (cdr children) sorted-list)))))
(defun insert (item sorted-list)
(cond ((null sorted-list) (list item))
((< (get-weight item) (get-weight (car sorted-list)))
(cons item sorted-list))
(t (cons (car sorted-list) (insert item (cdr sorted-list))))))
;;; run-best is a simple top-level "calling" function to run best-first-search
(defun run-best (start goal)
(declare (special *goal*)
(special *open*)
(special *closed*))
(setq *goal* goal)
(setq *open* (list (build-record start nil 0 (heuristic start))))
(setq *closed* nil)
(best-first))
;;; These functions handle the creation and access of (state parent)
;;; pairs.
(defun build-record (state parent depth weight)
(list state parent depth weight))
(defun get-state (state-tuple) (nth 0 state-tuple))
(defun get-parent (state-tuple) (nth 1 state-tuple))
(defun get-depth (state-tuple) (nth 2 state-tuple))
(defun get-weight (state-tuple) (nth 3 state-tuple))
(defun retrieve-by-state (state list)
(cond ((null list) nil)
((equal state (get-state (car list))) (car list))
(t (retrieve-by-state state (cdr list)))))
;; best-first defines the actual best-first search algorithm
;;; it uses "global" open and closed lists.
(defun best-first ()
(declare (special *goal*)
(special *open*)
(special *closed*)
(special *moves*))
(print "open =") (print *open*)
(print "closed =") (print *closed*)
(cond ((null *open*) nil)
(t (let ((state (car *open*)))
(setq *closed* (cons state *closed*))
(cond ((equal (get-state state) *goal*) (reverse (build-solution *goal*)))
(t (setq *open*
(insert-by-weight
(generate-descendants (get-state state)
(1+ (get-depth state))
*moves*)
(cdr *open*)))
(best-first)))))))
;;; generate-descendants produces all the descendants of a state
(defun generate-descendants (state depth moves)
(declare (special *closed*)
(special *open*))
(cond ((null moves) nil)
(t (let ((child (funcall (car moves) state))
(rest (generate-descendants state depth (cdr moves))))
(cond ((null child) rest)
((retrieve-by-state child rest) rest)
((retrieve-by-state child *open*) rest)
((retrieve-by-state child *closed*) rest)
(t (cons (build-record child state depth
(+ depth (heuristic child)))
rest)))))))
(defun build-solution (state)
(declare (special *closed*))
(cond ((null state) nil)
(t (cons state (build-solution
(get-parent
(retrieve-by-state state *closed*)))))))
尝试memoize实用程序。您可以在此处找到相关问题()。 Memoize跟踪对任何memoized函数的调用,并立即返回任何已知(以前计算过的)结果,以避免重新计算它们。对于像您这样的递归函数,结果是惊人的。代码中的问题:
- 递归。写循环以避免堆栈溢出
- 可能是长的打开和关闭列表。打开和关闭的列表可能相当长。一个操作是检查列表上是否有具有特定状态的记录。我将使用哈希表记录状态,然后使用该表检查状态是否存在
CL-USER 220 > (time (run-best '(0 1 2 3 4 5 6 7 8)
'(0 2 1 3 4 5 6 7 8)
'(right1 left1 up down)))
Timing the evaluation of (RUN-BEST (QUOTE (0 1 2 3 4 5 6 7 8))
(QUOTE (0 2 1 3 4 5 6 7 8))
(QUOTE (RIGHT1 LEFT1 UP DOWN)))
User time = 0:01:05.620
System time = 0.220
Elapsed time = 0:01:05.749
Allocation = 115386560 bytes
22397 Page faults
NO-SOLUTION
解决方案:
CL-USER 223 > (time (pprint (run-best '(2 1 5 3 4 6 0 8 7)
'(0 1 2 3 4 5 6 7 8)
'(right1 left1 up down))))
Timing the evaluation of (PPRINT (RUN-BEST (QUOTE (2 1 5 3 4 6 0 8 7))
(QUOTE (0 1 2 3 4 5 6 7 8))
(QUOTE (RIGHT1 LEFT1 UP DOWN))))
((2 1 5 3 4 6 0 8 7)
(2 1 5 0 4 6 3 8 7)
(2 1 5 4 0 6 3 8 7)
(2 0 5 4 1 6 3 8 7)
(0 2 5 4 1 6 3 8 7)
(4 2 5 0 1 6 3 8 7)
(4 2 5 1 0 6 3 8 7)
(4 2 5 1 6 0 3 8 7)
(4 2 5 1 6 7 3 8 0)
(4 2 5 1 6 7 3 0 8)
(4 2 5 1 0 7 3 6 8)
(4 2 5 1 7 0 3 6 8)
(4 2 0 1 7 5 3 6 8)
(4 0 2 1 7 5 3 6 8)
(0 4 2 1 7 5 3 6 8)
(1 4 2 0 7 5 3 6 8)
(1 4 2 3 7 5 0 6 8)
(1 4 2 3 7 5 6 0 8)
(1 4 2 3 0 5 6 7 8)
(1 0 2 3 4 5 6 7 8)
(0 1 2 3 4 5 6 7 8))
User time = 0.115
System time = 0.001
Elapsed time = 0.103
Allocation = 2439744 bytes
194 Page faults
旁注:使用
defstruct
或defclass
而不是状态列表我将删除各种递归并使用普通迭代。另请参见合并和查找…(defstruct srecord state parent depth weight)
@Asiax3使用Rainer Joswig提供的代码,已经为您生成了访问器。您不需要编写所有的get-*
函数。此外,您还可以使用(make srecord:states s…
)创建新状态。还有像(setf(srecord parent state)p)
这样的setf函数也被定义了,尽管您似乎没有改变插槽。这将简化您的代码。但代码的优点在于,您提供了一个功能接口,比在使用特定插槽的任何地方编写(n…
都要好。不是每个人都这么做。嗨,利奥,我试图利用记忆工具,但我一定是记住了错误的功能。我试着在函数“herusitic eval”和“best first”中使用它。你不会相信的。。。我使用了错误的开始和目标状态。。!我用的是(0 1,2,3,4,5,6,7,B),表示B是空白,而不是0。就在看到这个评论之后。现在我也成功地实现了defstruct,一切都很顺利D
CL-USER 223 > (time (pprint (run-best '(2 1 5 3 4 6 0 8 7)
'(0 1 2 3 4 5 6 7 8)
'(right1 left1 up down))))
Timing the evaluation of (PPRINT (RUN-BEST (QUOTE (2 1 5 3 4 6 0 8 7))
(QUOTE (0 1 2 3 4 5 6 7 8))
(QUOTE (RIGHT1 LEFT1 UP DOWN))))
((2 1 5 3 4 6 0 8 7)
(2 1 5 0 4 6 3 8 7)
(2 1 5 4 0 6 3 8 7)
(2 0 5 4 1 6 3 8 7)
(0 2 5 4 1 6 3 8 7)
(4 2 5 0 1 6 3 8 7)
(4 2 5 1 0 6 3 8 7)
(4 2 5 1 6 0 3 8 7)
(4 2 5 1 6 7 3 8 0)
(4 2 5 1 6 7 3 0 8)
(4 2 5 1 0 7 3 6 8)
(4 2 5 1 7 0 3 6 8)
(4 2 0 1 7 5 3 6 8)
(4 0 2 1 7 5 3 6 8)
(0 4 2 1 7 5 3 6 8)
(1 4 2 0 7 5 3 6 8)
(1 4 2 3 7 5 0 6 8)
(1 4 2 3 7 5 6 0 8)
(1 4 2 3 0 5 6 7 8)
(1 0 2 3 4 5 6 7 8)
(0 1 2 3 4 5 6 7 8))
User time = 0.115
System time = 0.001
Elapsed time = 0.103
Allocation = 2439744 bytes
194 Page faults