我在理解维基百科上找到的alpha-beta剪枝伪代码时遇到了困难:
function alphabeta(node, depth, α, β, Player) if depth = 0 or node is a terminal node return the heuristic value of node if Player = MaxPlayer for each child of node α := max(α, alphabeta(child, depth-1, α, β, not(Player))) if β ≤ α break (* Beta cut-off *) return α else for each child of node β := min(β, alphabeta(child, depth-1, α, β, not(Player))) if β ≤ α break (* Alpha cut-off *) return β
让我感到困惑的是Player = MaxPlayer
这个条件。我理解整个函数递归调用not(Player)
来获取最小值,然后会再次递归调用函数使用Player
,直到达到深度限制或找到目标状态。然而,我不理解
if β ≤ α break
这个语句。我的理解是,一旦发现第二个值高于之前调用中找到的最小值(β
),就会使用这个值。但由于这是函数的MAX部分,我们不是应该想要最高值,而不是任何大于beta的值吗?
回答:
这是算法的修剪阶段,在MaxPlayer
条款中(当检查此节点中玩家的最大值时):
Beta是函数的参数,是“修剪因子”。它代表你到目前为止找到的最小分数。这意味着当前节点的父节点,是一个最小化节点——已经找到了一个beta的解决方案。
现在,如果我们继续迭代所有子节点,我们将得到至少与当前alpha一样好的结果。由于beta <= alpha
– 父节点 – 这是一个最小化节点 – 永远不会选择这个alpha(或任何大于它的值) – 它会选择一个beta或更低的值 – 而当前节点没有机会找到这样的值,所以我们可以修剪计算。
示例:
MIN / \ / \ / \ / \5 MAX / | \ / | \ / | \ 6 8 4
在评估MAX节点时,如果我们应用正常的min-max算法,我们将返回8。然而,我们知道MIN函数将执行min(5, MAX(6, 8, 4))
。由于在读取6之后我们知道max(6, 8, 4) >= 6
,我们可以在不继续计算的情况下返回6,因为上层的MIN计算将是min(5, MAX(6, 8, 4)) = min(5, 6) = 5
。
这是对一级的直觉理解,当然它是递归进行的,以同样的想法“流动”到所有级别。
同样的想法也适用于MIN顶点的修剪条件。