在Common Lisp中实现Alpha-beta剪枝

我尝试使用维基百科上找到的伪代码来编写Alpha-beta算法。当程序到达(EQ depth 0)时,它返回了启发式值,但深度值继续减少,导致循环。现在我的代码看起来像这样:

(defun ab(tab node depth a b)(cond ((EQ depth 0) (calculaH tab))        ((eq (mod depth 2) 0) (setq v -999999) (setq movimiento (sigMov depth node tab))  (loop while (not(null movimiento))                                                          do (setq v (max v (ab (nth 3 movimiento) movimiento (- depth 1) a b)))                                                           (setq a (max a v))                                                           (cond((<= b a) (break))                                                                (t (setq movimiento (sigMov depth movimiento tab))))) (return v))        (t (setq v 999999) (setq movimiento (sigMov depth node tab)) (loop while (not(null movimiento))                                                           do (setq v (min v (ab (nth 3 movimiento) movimiento (- depth 1) a b)))                                                           (setq a (min b v))                                                           (cond((<= b a) (break))                                                                (t (setq movimiento (sigMov depth movimiento tab))))) (return v))))

我应该在代码的某个地方增加深度值吗?为什么递归没有自动增加这个值?


回答:

维基百科上的Alpha-beta剪枝算法可以几乎原样翻译成Lisp。由于它使用了无穷值,我们不应该用“999999”来凑合,而是定义能够可靠处理这些特殊值的minmax函数:

(defpackage :alphabeta  (:use :cl)  ;; 自定义支持无穷大的min/max函数  (:shadow min max))(in-package :alphabeta)(defvar -∞ '-∞ "负无穷大符号")(defvar +∞ '+∞ "正无穷大符号")(defun min (a b)  (cond    ((eql a +∞) b)    ((eql b +∞) a)    ((eql a -∞) -∞)    ((eql b -∞) -∞)    (t (cl:min a b))))(defun max (a b)  (cond    ((eql a -∞) b)    ((eql b -∞) a)    ((eql a +∞) +∞)    ((eql b +∞) +∞)    (t (cl:max a b))))

代码还依赖于辅助函数,我在这里声明它们以避免警告:

 ;; 你需要实现以下函数(declaim (ftype function terminal-node-p heuristic-value children))

然后,伪代码可以几乎原样编写。为了回答这个问题,我保留了相同的希腊变量,但正如Dan Robertson在评论中指出的,这可能会导致意外:

使用像α或β这样的名称时要小心,因为一个典型的支持Unicode的Lisp实现会将它们大写为Α和Β。你能区分A和Α或B和Β之间的区别吗?

(defun alphabeta (node depth α β maximizing-player-p)  (when (or (= depth 0) (terminal-node-p node))    (return-from alphabeta (heuristic-value node)))  (if maximizing-player-p      (let ((value -∞))        (dolist (child (children node))          (setf value (max value (alphabeta child (1- depth) α β nil)))          (setf α (max α value))          (when (<= β α)            ;; β剪枝            (return)))        value)      (let ((value +∞))        (dolist (child (children node))          (setf value (min value (alphabeta child (1- depth) α β t)))          (setf α (min α value))          (when (<= β α)            ;; α剪枝            (return)))        value)))
  • 永远不要用EQ比较数字。如果你只打算比较数字,请使用=

  • 始终使用let引入本地变量,永远不要setq一个在当前作用域中未定义的变量。你的代码失败是因为你的Lisp实现会在你首次对未绑定符号调用setq时定义全局变量。之后,你会在递归代码中改变全局变量,这使得它无法正常工作。

  • 不要使用过长的行(这在大多数语言中都是正确的),正确地缩进,将每个新形式放在自己的行上,并从相同的缩进开始。

  • Lisp中的BREAK进入调试器。如果你想提前退出循环,请使用RETURN(这是因为像DO这样的迭代构造会引入名为nil的隐式BLOCK)。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注