在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

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

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