`
univasity
  • 浏览: 808812 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

讲解极小极大 (Minimax Explained) [译]

阅读更多

原文链接:Minimax Explained

讲解极小极大

Written by Paulo Pinto .

 

本文讨论如何把搜索应用到带有完整信息的逻辑游戏上。会提及到博弈树,和一个对其进行搜索的算法。给出了伪代码并对变形alpha-beta进行了讲解。

 

 

算法 (The Algorithm)

简介(Introduction)

AI有广泛的应用领域,其中游戏是最有趣的。当今每个主流的操作系统都会附带一些游戏。因此,对于某些算法是专门为游戏设计的,并不需要感到意外。

极小极大算法 (The Min-Max Algorithm)

极小极大算法被应用于二人博弈中,比如井字过三关、西洋棋、国际象棋、围棋等等。所有这些游戏都有一个共通点,都是逻辑游戏。这意味着,可以通过一 组规则和前置条件来定义它们。利用这些(规则和条件),就能够从一个给定的游戏局面得知下一步可能的移动。它们(这些游戏)同时还共同拥有其他一些特性, 都是“拥有完整信息的博弈”。每个玩家都能知道一切关于对手可能的移动方式。

 

Search Tree

图1 :展示了一个逻辑游戏的搜索树

 

在讲解算法之前,有必要简短地介绍一下搜索树。搜索树是展示搜索的一种途径。方形代表节点,标志了搜索中的决策点。这些节点通过分支连接在一起。搜 索始于根节点,图中最顶上的那个。在每个决策点,可行搜索路径上的节点会被创建,直到再没有更多可能的决策。标志搜索结束的节点是叶节点。

 

这里涉及两个玩家,MAX与MIN。一颗搜索树按深度优先(从当前的游戏位置(进度)开始,直到游戏结束的位置)被生成。最终的选择的位置(走法) 是以MAX的角度来评估的,图1中展示的正是如此。然后,自底向上给树上的节点赋予一个估值。属于MAX玩家的节点获取其子节点中的最大值。MIN玩家的 节点会选择其子节点中的最小值。该算法被描述在清单1中。

MinMax (GamePosition game) {
  return MaxMove (game);
}
 
MaxMove (GamePosition game) {
  if (GameEnded(game)) {
    return EvalGameState(game);
  }
  else {
    best_move < - {};
    moves <- GenerateMoves(game);
    ForEach moves {
       move <- MinMove(ApplyMove(game));
       if (Value(move) > Value(best_move)) {
          best_move < - move;
       }
    }
    return best_move;
  }
}

MinMove (GamePosition game) {
  best_move <- {};
  moves <- GenerateMoves(game);
  ForEach moves {
     move <- MaxMove(ApplyMove(game));
     if (Value(move) > Value(best_move)) {
        best_move < - move;
     }
  }

  return best_move;
}
 

这里发生了什么事?这些数值表明了该步移动有多好。因此,玩家MAX最终会选择最高分数的进行移动。对此玩家MIN也有所行动,将选择对其最有利的移动,即最小化了MAX的得益。


优化 (Optimisations)

优化(Optimisation)

然而,只有非常简单的博弈能够在短时间内生成整棵搜索树。对大多数博弈来说,这是不可能的,到那时可能宇宙都消失了(需要很长的时间)。因此,要对算法进行一些优化。

 

首先需要注意的是,优化是有代价的。我们用与博弈发展有关(几率和捷径)的完整信息来换取算法的最优化。把“通往胜利的完整路径”取代为“通过有机 会获得胜利的路径”来进行决策。如果优化不是最佳选择,或者优化得不好,那么我们能用无用的AI来结束算法。但这也比使用随机选取要好。

 

一个基本的优化是,限制搜索树的深度。为什么这会有所帮助?因为生成完整的树要耗费年月。如果博弈树的分子因子是3,即每个节点都有子树,树中每个层次中节点的数量如下:

Depth Node Count
0 1
1 3
2 9
3 27
..
n 3^n

 

这个序列展示了深度为n的树将有3^n个节点。要获得所有节点的总数,我们需要把每层的节点数加起来。因此,一棵n层的树其节点总数为(0, n, 3^n)。对于多数博弈,像国际象棋就有一个庞大的分支因子,这意味着整棵树可能无法被加载到内存中。即便能行,生成整棵树也将需要很长一段时间。如果分析一个节点需要1秒,那就是说在前面的例子中,每次搜索数都需要耗费(0, 3, 3^n)*1秒。对于一个5层的搜索树,那将是1+3+9+27+81+243 = 364 * 1 = 364秒 = 6分钟! 这对于一个博弈来说太久了。如果电脑的每次移动都要玩家等上6分钟,估计玩家就退出游戏了。

 

第二项优化是,使用一个函数,以某个玩家的角度对当前的棋步进行评估。它将对当前博弈状态给定一个值。例如,统计棋盘上棋子的数量、或离博弈结束还剩余的步数,或是任何我们可以用来衡量棋步的数值。

 

除了评估当前的棋步,该函数可能还计算出当前棋步对结束博弈的帮助有多大。或者换种说法,当前棋步带来的胜数有多大。在这种情况下,该函数被称为“评估函数”。

 

该函数必须把一些启发性考虑进去。启发性即我们对与博弈相关的事物的认知,这有助于创建更好的估值函数。例如,在西洋棋中,在角和边上的棋子是不会 被吃掉的。因此我们可以创建一个估值函数,对在棋盘中这些位置上的棋子给出更高的分值,从而令棋子下到这些位置,使博弈获得更好的结果。

 

估值函数必须能够同时为两个玩家评估棋步,因为你不知道当前(有限的)深度是轮到哪个玩家了。

 

尽管如此,如果博弈是对称的(这是说一个玩家的损失等于另一个玩家的得益,这样的博弈被称为“零和博弈”),则可以不用分别创建两个估值函数。对于这些博弈,一个估值函数就足够了,其中一个玩家只要取反值即可。

 

改进后的算法在清单2中展示:

MinMax (GamePosition game) {
  return MaxMove (game);
}
 
MaxMove (GamePosition game) {
  if (GameEnded(game) || DepthLimitReached()) {
    return EvalGameState(game, MAX);
  }
  else {
    best_move < - {};
    moves <- GenerateMoves(game);
    ForEach moves {
       move <- MinMove(ApplyMove(game));
       if (Value(move) > Value(best_move)) {
          best_move < - move;
       }
    }
    return best_move;
  }
}
 
MinMove (GamePosition game) {
  if (GameEnded(game) || DepthLimitReached()) {
    return EvalGameState(game, MIN);
  }
  else {
    best_move <- {};
    moves <- GenerateMoves(game);
    ForEach moves {
       move <- MaxMove(ApplyMove(game));
       if (Value(move) > Value(best_move)) {
          best_move < - move;
       }
    }
    return best_move;
  }
}
 

即便如此,该算法仍然存在一些缺陷,某些缺陷能通过选择另一个算法来被修复。

 

其中一个缺陷是,如果博弈很复杂,即使有限的深度,答应也会耗费很长一段时间。一个解决方案是限制搜索的时间。如果超时了则返回当前所找到的最好选择。

 

而最大的缺陷是,“有限的视界问题”。一个看似很好的棋步,却可能导致糟糕的结果。这是因为算法没能预见对手将会采取并导致其获利的棋路所导致的。由于算法被有限的深度蒙住了双眼,错失了致命的一步棋。

 

给算法提速(Speeding the algorithm)

仍然有一些东西是可以用来降低搜索时间的。来看一下图2。节点A的值是3,对于起源自节点B的子树,被找到的第一个值是2。因为节点B是在MIN 层,我们是要为节点B选择小于等于2的值。同时我们知道节点A的值是3,并且节点A和B共享同一个在MAX层级的父节点。这意味着起源自节点B的路径不会 被选择,因为对于MAX层的节点来说,3比2更好。因此,不值得继续对节点B的子树进行搜索,我们将安全地忽略掉所有剩余的子节点。

 

Minimax Cutoff

图2 :极小极大搜索树,展示了可以被剪断的分支。

所有的这些都表明,当我们发现对子树的搜索不会带给我们任何有用的结论的时候,搜索可以被跳过。

 

这一优化被称为alpha-beat剪枝,算法描述如下:

   1. 有两个值被传递到每个树的节点:
            值alpha,记录着找到的最好的“大值”(MAX value);
            值beta,记录着找到的最好的“小值”(MIN value)。
   2. 在MAX层时,在对每个子路径进行估值前,用前一路径的返回值与beta值作比较。如果该值大于beta值,那么跳过对当前节点的搜索;
   3. 在MIN层时,在对每个子路径进行估值前,用前一路径的返回值与alpha值作比较。如果该值小于alpha值,那么跳过对当前节点的搜索。

 

清单3展示了使用了alpha-beta剪枝的MiniMax的完整伪代码:

MinMax (GamePosition game) {
  return MaxMove (game);
}
 
MaxMove (GamePosition game, Integer alpha, Integer beta) {
  if (GameEnded(game) || DepthLimitReached()) {
    return EvalGameState(game, MAX);
  }
  else {
    best_move < - {};
    moves <- GenerateMoves(game);
    ForEach moves {
       move <- MinMove(ApplyMove(game), alpha, beta);
       if (Value(move) > Value(best_move)) {
          best_move < - move;
          alpha <-  Value(move);
       }
 
       // Ignore remaining moves
       if (beta > alpha)
         return best_move;
    }

    return best_move;
  }

}
 
MinMove (GamePosition game) {
  if (GameEnded(game) || DepthLimitReached()) {
    return EvalGameState(game, MIN);
  }
  else {
    best_move < - {};
    moves <- GenerateMoves(game);
    ForEach moves {
       move <- MaxMove(ApplyMove(game), alpha, beta);
       if (Value(move) > Value(best_move)) {
          best_move < - move;
          beta <- Value(move);
       }
 
       // Ignore remaining moves
       if (beta < alpha)
         return best_move;
    }

    return best_move;
  }

}
 

使用了alpha-beta剪枝的MiniMax比默认的MiniMax好多少?这取决于搜索的次序。如果该步棋看起来不会造成影响,那么算法使用 alpha-beta剪枝也不会带来改善。但是,如果是估值函数和产生的棋步所导向的alpha-beta剪枝,会带来更显著的效果。


Alpha-Beta剪枝 (Alpha-Beta Cutoff)

增添Alpha-Beta剪枝 (Adding Alpha-Beta Cutoffs)

大部分人都很想知道这里所讲的“搜索速度”到底是什么。搜索速度在AI中是非常重要的,因为如果一个算法要耗费很长的时间才能给出一个满意的答复,那么该算法是不适合的。

 

例如,一个好的MiniMax算法实现,其估值函数能够给出高质量的评估,或许能够在1秒内探索1000种下法。在象棋比赛中,每个玩家有150秒来决定如何走棋。因此,在这段时间内函数将能够分析150 000种下法。但是,象棋中的每一步都有35种可能的分支!在最后,程序将只能分析出每个棋步的3-4个分支。即便是没有练习过的人也能做得比这好。

 

但是,如果我们把alpha-beta剪枝应用到MiniMax中,加上一个正确的估值函数,结果会更加好。这种情况下,程序就有可能对双倍数量的下法进行分析,从而变成一个更强大的对手。

一个实现的例子 (An example implementation)

例子总是用来说明算法如何实现的一个好途径。回到1999年,我和一个要好的朋友就已经实现了一个西洋棋游戏的Java应用(为了大学的AI课程)。我最近将游戏移植到了C#上。

 

其中的MiniMax算法不是一个最好的实现。其实我要说明的是“最重要的是它能运行”。无论如何,我认为这展示了算法的一种可能实现方式,并且作为例子它已经足够好了。

 

Minimax Board

图3 :每个格子都带有估值的一个棋盘的例子

 

游戏中使用了带有alpha-beta剪枝的MiniMax来为电脑提供移动决策。估值函数为每个被棋子所占据的格子提供一个加权平均数。图3展示 了对每个棋盘上格子的估值。每个格子上的数值被乘以该格子上棋子的类型,在清单5中列出。清单4展示了估值函数的Java实现。它已经被轻微的做了修改, 以符合该主题。

 

                  Piece                                 Value
Normal                                                    5
Normal but close to being promoted       7
King                                                       10

表1 : Piece Values

 

 

// Snippet from Computer.java.
// Contains the evaluation function
/**
* Evaluation function.
*/
private int eval (CheckersBoard board) {
    int colorKing;
    // Finds out who is the current player
    if (color == CheckersBoard.WHITE)
        colorKing = CheckersBoard.WHITE_KING;
    else
        colorKing = CheckersBoard.BLACK_KING;
    int colorForce = 0;
    int enemyForce = 0;
    int piece;
    try {
        // Searchs all board positions for pieces
        // and evaluates each position.
        for (int i = 0; i < 32; i++) {
            piece = board.getPiece (i);
            if (piece != CheckersBoard.EMPTY)
                if (piece == color || piece == colorKing)
                    colorForce += calculateValue (piece, i);
                else
                    enemyForce += calculateValue (piece, i);
        }
    }
    catch (BadCoord bad) {
        bad.printStackTrace ();
        System.exit (-1);
    }
    return colorForce - enemyForce;
}


/**
* Measures the value of a checkers piece, given
* it’s position in the board
*/
private int calculateValue (int piece, int pos) {
    int value;
    if (piece == CheckersBoard.WHITE ) //Simple piece
    if (pos >= 4 && pos <= 7) // White pieces are more
        value = 7; // valuable the closer they get
    else // to the oponent
        value = 5;
    else if (piece != CheckersBoard.BLACK) //Simple piece
        if (pos >= 24 && pos <= 27) // White pieces are more
            value = 7; // valuable the closer they get
        else // to the oponent
            value = 5;
        else // King piece
            value = 10; // King pieces are always the
    // most valuable
    return value * tableWeight[pos];
} 

清单5 : The Java implementation of the evaluation function.

 

注意,代码中使用了一个容器,基于0-31,用来表示棋盘上的位置。

 

游戏代码在这里下载:


结论(Conclusion)

对于各种需要模拟人类AI的电脑博弈,MiniMax可能并不是最好的。但是,一个好的实现也能创建出一个强大的对手。我希望这篇文章能在让你了解MiniMax算法并懂的如何将它用到你的游戏中。

 

参考资料(References)

[1] - Russell Stuart J., Norvig Peter. “Artificial Intelligence. A modern approach.”. Prentice Hall, 1995. ISBN 0-13-103805-2.
[2] - Bratko Ivan. “PROLOG. Programming for artificial intelligence” Addison-Wesley, 1990. ISBN 0-201-41606-9
[3] - Rich Elaine, Knight Kevin. “Artificial Intelligence”. McGraw-Hill Inc., 1991. ISBN 0-07-100894-2
[4] - Analytical Reasoning FAQ. Available at http://www.west.net/~stewart/lwfaq.htm

 

Written by Paulo Pinto .

1
3
分享到:
评论

相关推荐

    基于python的AI五子棋实现(极大极小值搜索和alpha beta剪枝)

    在本项目中,我们探索了如何使用Python编程语言来实现一个智能五子棋游戏,它利用了人工智能领域的经典算法——极大极小值搜索(Minimax)以及Alpha-Beta剪枝技术。五子棋是一个简单的二人对弈游戏,但在AI领域却...

    统计线性模型极小极大估计

    在风险型决策的背景下,统计线性模型的极小极大估计(minimax estimation)成为了一个关键的理论工具,因为它可以帮助决策者在面对不确定性时,寻找最优的决策策略。 极小极大估计的核心思想是在最坏可能的情况下...

    五子棋AI算法-极大极小值搜索算法代码实现

    在本文中,我们将深入探讨五子棋AI算法的核心——极大极小值搜索(Minimax Search)算法及其优化版本,Alpha Beta剪枝。这个算法在计算机博弈领域有着广泛的应用,尤其适用于像五子棋这样的两人对弈游戏。我们将讨论...

    极小化极大准则matlab仿真

    信号检测与估计理论极小化极大准则,自己编写,欢迎下载

    岭函数Bandit凸优化的极大极小遗憾_Minimax Regret for Bandit Convex Optimisatio

    本文《岭函数Bandit凸优化的极大极小遗憾》深入探讨了这一主题,特别是针对具有特定约束的对手策略。 首先,我们定义问题背景:考虑一个在高维空间中的凸体K,它是一个非空、紧致且凸的集合。游戏过程分为n个回合...

    基于改进型极大极小搜索策略开发零和博弈类游戏

    极大极小搜索(Minimax Search)是一种经典的深度优先搜索算法,广泛应用于决策树结构的游戏策略中。该算法的基本思想是模拟对手的最佳响应,以预测未来可能的结果。在每一步,算法都会预测对手的最优行动,并以此...

    极大极小博弈树-一种数据结构

    极大极小博弈树(Minimax Game Tree,简称MGT)是一种在游戏智能(Game AI)领域极为重要的数据结构。它主要用于描述两人轮流行棋的游戏中所有可能的走法及其结果。MGT的核心思想在于通过模拟所有可能的游戏进程来...

    极小极大准则matlab验证

    极小极大准则(Minimax Principle)是决策理论中的一个基本原则,它旨在通过找到最坏情况下的最优策略来最小化可能的最大损失。在信号处理中,这一准则通常用于设计鲁棒的检测算法,即在噪声或未知干扰存在的情况下...

    人工智能-极大极小算法

    极大极小算法(Minimax Algorithm)是一种常用于两人零和博弈游戏中的搜索算法。这种算法的主要目的是帮助一方(通常称为MAX)在游戏中选择最佳行动策略,以最大化其得分,同时假设对手(通常称为MIN)也会采取最佳...

    非线性极小极大问题的分数阶粒子群算法.pdf

    非线性极小极大问题(Nonlinear Minimax Problem,NMP)是一类典型的非可微优化问题,广泛应用于工程优化设计、电子线路优化设计、计算机辅助几何设计、最优控制及对策论等领域。工程实际中许多问题,如非线性L₁、L...

    基于极大极小值算法的五子棋AI实现.rar

    首先,极大极小值搜索(Minimax)算法是AI决策的基础。它是一种递归方法,通过假设对手总是选择最不利于当前玩家的走法(最小化对手的得分),而当前玩家总是选择最有利于自己的走法(最大化自己的得分),来预测...

    基于α-β剪枝(Alpha-beta-pruning)的极小极大算法实现的井字棋游戏源码+项目说明.zip

    【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用!...基于α-β剪枝(Alpha-beta-pruning)的极小极大算法(Minimax-algorithm)实现的井字棋(一字棋、tic-tac-toe)游戏源码+项目说明.zip

    minimax_180:极小极小游戏项目

    极小极大算法(Minimax Algorithm)是博弈论中的一种经典算法,主要用于解决两人零和博弈问题,例如国际象棋、井字游戏等。在这个名为"minimax_180"的项目中,我们可以推测它可能是一个基于Java实现的极小极大算法的...

    安卓人机对战五子棋 极大极小搜索算法

    在安卓平台上开发一款人机对战的五子棋游戏,其中关键的技术实现是极大极小搜索(Minimax Search)算法。这种算法广泛应用于棋类游戏的人工智能中,通过模拟对手的最佳策略来预测最佳走法,确保计算机在有限的搜索...

    五子棋之极大极小算法

    五子棋的编程实现中,一个重要的算法就是极大极小(Minimax)搜索算法。该算法可以用来评估在特定游戏状态下,双方可能采取的最佳行动。五子棋利用此算法实现AI对弈,通过模拟未来可能的走法,找到最有利于自己(极...

    通过alpha-belta剪枝的极大极小值算法实现简单的五子棋+A*算法与IDA*算法解决走迷宫问题

    本文将探讨如何利用α-β剪枝的极大极小值算法实现简单的五子棋游戏,以及A*算法和IDA*算法在解决迷宫问题中的应用。 首先,五子棋是一个典型的二人对弈游戏,适合使用基于搜索的决策方法。α-β剪枝极大极小值算法...

    用C ++ 编码的井字游戏使用极小极大算法和 alpha-beta 剪枝

    用 C++ 编码的井字游戏使用极小极大算法和 alpha-beta 剪枝 这个游戏是用基本的 C++ 编写的,没有任何 STL。这是一个单人游戏。CPU使用minimax播放它的动作。它还具有 alpha-beta 剪枝概念,以提高 CPU 的处理效率

    minimax:井字棋游戏中极小极大算法的 LISP 实现

    - `minimax.lisp`: 极小极大算法的核心实现,包括递归的minimax函数和剪枝功能。 - `move-selector.lisp`: 选择最佳动作的函数,基于minimax的输出。 - 可能还会有测试和示例文件,用于验证算法的正确性和性能。 ...

    基于Python使用最小最大算法(MINIMAX)实现自动吃豆人【100011655】

    最小最大算法(MINIMAX)是一种在博弈论中广泛使用的决策算法,特别是在两人零和游戏中,如棋类游戏。这个算法的基本思想是模拟所有可能的游戏结果,并预测对手的最佳策略,以此来选择当前最优的行动。在自动吃豆人...

Global site tag (gtag.js) - Google Analytics