使用转置表进行 Alpha-beta 修剪,迭代加深

Posted

技术标签:

【中文标题】使用转置表进行 Alpha-beta 修剪,迭代加深【英文标题】:Alpha-beta prunning with transposition table, iterative deepening 【发布时间】:2015-07-11 11:26:39 【问题描述】:

我正在尝试使用转置表实现增强的 alpha-beta min-max 修剪。我使用这个伪代码作为参考:

http://people.csail.mit.edu/plaat/mtdf.html#abmem

function AlphaBetaWithMemory(n : node_type; alpha , beta , d : integer) : integer;
    if retrieve(n) == OK then /* Transposition table lookup */
        if n.lowerbound >= beta then return n.lowerbound;
        if n.upperbound <= alpha then return n.upperbound;
        alpha := max(alpha, n.lowerbound);
        beta := min(beta, n.upperbound);
    if d == 0 then g := evaluate(n); /* leaf node */
    else if n == MAXNODE then
        g := -INFINITY; a := alpha; /* save original alpha value */
        c := firstchild(n);
        while (g < beta) and (c != NOCHILD) do
            g := max(g, AlphaBetaWithMemory(c, a, beta, d - 1));
            a := max(a, g);
            c := nextbrother(c);
    else /* n is a MINNODE */
        g := +INFINITY; b := beta; /* save original beta value */
        c := firstchild(n);
        while (g > alpha) and (c != NOCHILD) do
            g := min(g, AlphaBetaWithMemory(c, alpha, b, d - 1));
            b := min(b, g);
            c := nextbrother(c);

    if g <= alpha then 
        n.upperbound := g; 
        store n.upperbound;
    if g >  alpha and g < beta then
        n.lowerbound := g; 
        n.upperbound := g; 
        store n.lowerbound, n.upperbound;
    if g >= beta then 
        n.lowerbound := g; 
        store n.lowerbound;
return g;

这个算法的三个问题:

    我相信我应该在每个保存的转置表条目中存储深度(= 到叶层的距离),并且仅当 entry.depth>=currentDepth(= 条目与叶层的距离大于或等于)时才使用条目。上面的伪代码中没有显示,也没有在那里讨论,我想确保我理解正确。

    我想为每个位置存储最佳移动,以便在搜索停止后将其用于移动排序和提取最佳移动。在纯 min-max 中,哪个动作是最好的很明显,但是在使用 alpha-beta 截止值迭代时哪个动作是最好的?我可以假设给定位置的最佳移动是循环结束时找到的最佳移动(有或没有中断)吗?

    在迭代深化方案中执行此算法时 - 我应该在每次深度增加之前清除转置表吗?我认为不是,我希望你使用之前迭代中存储的位置,但我不确定这些信息是否足以进行更深入的搜索(应该是在检查表条目深度时)?

【问题讨论】:

【参考方案1】:

    你是对的。 entry.depth 存储换位表条目中的信息所基于的层数。所以你只能在entry.depth &gt;= remaining_depth时使用这些信息。

    逻辑是我们不想使用比“正常”搜索弱的结果。

    有时,出于调试目的,条件更改为:

    entry.depth == remaining_depth
    

    这避免了一些search instabilities。无论如何它不能保证没有转置表的搜索结果相同。

    存储并不总是最好的。

    当搜索失败率很低时,没有“最佳移动”。我们唯一知道的是,没有任何动作足以产生大于alpha 的分数。没有办法猜测哪个动作最好。

    因此,您应该仅在哈希表中存储下限(beta-cutoff 即反驳移动)和精确分数(PV 节点)的移动。

    不,你不应该。通过迭代加深,一次又一次到达相同的位置,换位表可以加快搜索速度。

    您应该清除移动之间的换位表(或者,最好使用额外的entry.age 字段)。

【讨论】:

如果在某些情况下没有最好的棋步存储,调用v=aphaBeta(root, -inf, +inf)后如何判断哪个棋步最好?我想我会为根节点调用 alphaBeta,除了 minmax 值(这对我来说不是那么有趣)我会得到“获胜”的举动。我知道我可以为根节点的每个子节点执行 alphaBeta(child, -inf, +inf) 并选择最好的 - 这是唯一的选择吗? 不确定...调用aphaBeta(root, -inf, +inf) 你不能得到失败低/失败高,只是一个准确的分数/最佳移动(你应该注意不要覆盖与@相关的条目987654331@ 节点)。您不必调用搜索函数来提取主线:它可以直接从转置表中提取(例如,参见chessprogramming.wikispaces.com/Principal+variation 或open-aurec.com/wbforum/viewtopic.php?f=4&t=3440)。有时会使用 ad-hoc PV TT。 当然,我忘了用 -inf,+inf 调用 alphaBeta 可以保证准确的分数。因此,如果我理解正确,在调用 alphaBeta(root, -inf, +inf) 之后,我将始终在 tt[root.getHash()] 中有最好的移动,但如果我尝试从转置表中提取进一步的移动,可能会更少它们比搜索深度(因为某些位置不会有最佳移动)? 它们可能更少(臭名昭著的“截断 PV”问题),但出于其他原因。理论上,由于 PV 中的每个元素都包含一个介于alphabeta 之间的分数,因此每个哈希元素中都应该存储一个“最佳移动”。不幸的是,哈希表条目可以被覆盖。

以上是关于使用转置表进行 Alpha-beta 修剪,迭代加深的主要内容,如果未能解决你的问题,请参考以下文章

python zip()函数转置表,操作列

使用幂查询转置表

使用带有 Alpha-Beta 修剪的 MinMax 找到最佳移动

转置表,包括标题

PostgreSQL中的转置表

SQL转置表 - sqlserver [重复]