You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Common Lisp实现Alpha-beta剪枝遇循环问题求助

修复Alpha-beta剪枝的递归循环问题

我帮你分析下代码里的问题哈,主要有两个关键点导致depth持续减小、出现循环:

1. 终止条件的判断不可靠

你用了(EQ depth 0)来判断递归终止,但Lisp里EQ是用来判断对象同一性的,对于数字来说,虽然小整数可能共享对象,但用=或者eql来判断数值相等才是更稳妥的做法。如果EQ判断失败,depth到0的时候不会触发终止,递归会继续执行,depth就会一直减小下去。

把终止条件改成这样:

((= depth 0) (calculaH tab))

2. 循环中没有更新移动对象

你的代码里只在循环开始前初始化了一次movimiento,但循环过程中没有获取下一个可能的移动,导致循环一直处理同一个移动,每次递归调用都会让depth减1——就算某次递归触发了depth=0返回启发值,循环又会再次调用递归,depth变成-1,这时候终止条件不生效,就无限递归了。

必须在每次循环结束后更新movimiento为下一个合法移动,比如在循环体末尾加上:

(setq movimiento (sigMov depth node tab))

另外,你之前的剪枝逻辑没写完,我帮你补全了完整的alpha-beta剪枝逻辑,修复后的完整代码大概是这样:

(defun ab(tab node depth a b)
  (cond
    ;; 数值相等判断,确保depth为0时终止递归
    ((= depth 0) (calculaH tab))
    ;; 偶数层:最大化玩家回合
    ((evenp depth)
     (let ((v -999999)
           (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))
         ;; Alpha剪枝:如果当前值大于等于beta,直接跳出循环
         (when (>= v b)
           (return))
         ;; 更新为下一个移动,避免无限循环
         (setq movimiento (sigMov depth node tab)))
       v))
    ;; 奇数层:最小化玩家回合
    ((oddp depth)
     (let ((v 999999)
           (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 b (min b v))
         ;; Beta剪枝:如果当前值小于等于alpha,直接跳出循环
         (when (<= v a)
           (return))
         (setq movimiento (sigMov depth node tab)))
       v))))

这里还做了个小优化:用let来初始化局部变量,比直接用setq更符合Lisp的函数式编程习惯,避免意外修改全局变量。

另外补充一句:如果你的sigMov函数是一次性返回所有合法移动的列表,那循环可以改成更简洁的遍历写法,比如(loop for movimiento in (sigMov depth node tab) do ...),这样就不用手动更新movimiento了,也能避免循环问题。

内容的提问来源于stack exchange,提问作者Jorge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:28:22