《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——Alpha-Beta剪枝

2014.07.08 22:43

简介:

  “搜索”与“剪枝”几乎是如影随形的。此处的“搜索”指的是带有回溯算法的深度优先搜索。

  在之前的“Minimax策略”中我们给出了一个三连棋的程序,运行后你就知道计算一步棋要花多少时间。

  为了计算最优的一步棋,我们可能需要递归9万多次。如果毫无疑问这种阶乘式的穷举过程必须通过剪枝来加速。

  本篇介绍一种用于Minimax策略的剪枝思路——α-β剪枝

  剪枝的英语是pruning,所以不要想当然说成trimming。

图示:

  在上一篇讲解Minimax策略的博文中我们给出了一棵博弈树:

  

  如果按照这棵树的形状来看,延伸下去应该会造成指数级的时间增长。

  那么如果我们想做到“全知全能”,就得在下第一步棋的时候穷举所有可能的结局。因此你走一步棋就得推理9万多次。

  按照这种脑力消耗,就是诸葛亮也吃不消,于是我们有两种办法可以偷懒:

    1. 截断:我的脑容量没那么大,所以我只考虑三步棋以内的结果。三步棋之外的结果,姑且不管了。

    2. 剪枝:有些结局实际上根本不需要分析,因为我们有充分的理由提前放弃那条路径。

  本次不考虑截断的问题,只谈剪枝。

  我们将刚才那棵博弈树中的两个结点标记为红色:

  

  此处我们强调两条性质:

    1. 对于Min层的每个节点,它的任何子节点的值都不小于它自身。

    2. 对于Max层的每个节点,它的任何子节点的值都不大于它自身。

  这棵树中78和36两个结点下方的节点不会被访问到,因为没有必要

  注意78所处的是Min层,对手为了使我的利益最小化,会尽力找出最小值。而68比78小,因此在78的下方不可能找到小于68的节点。所以没有必要继续深入78中。

  36的道理相同,只不过处于Max层,所以解释自然是相反的。

  在每一层都可能会有这样的节点,因此随时留意这样的剪枝机会,能极大地提高效率。

  如果我没记错,经过这种剪枝的程序最多只需要递归一百多次,和9万多次相比确实快了不少。

  因为Min和Max层交替出现,因此这种剪枝也需要两套代码逻辑,分别称为α剪枝β剪枝。二者合称α-β剪枝

  教材中只给出了其中一个剪枝的伪代码,如果你没想明白,肯定写不出另一个的。

  接下来请运行代码吧,看看效率上的差异。不过可能的话,我还是建议你在不看代码的情况下,自己修改三连棋的程序来实现α-β剪枝。

  

  别忘了确保程序的正确性:如果你赢了电脑,那程序肯定是错的。

  (提问:对于一个没有必胜策略的游戏,完美的AI是无法打败的,你只能打平手或者输给它,是这样吗?)

实现:

  1 // Optimization for Minimax game strategy, using Alpha-Beta Pruning.
  2 // You can watch over the ‘function_call_count‘ variable.
  3 #include <iostream>
  4 #include <vector>
  5 using namespace std;
  6
  7 int function_call_count;
  8
  9 bool computerWin(const vector<int> &board)
 10 {
 11     int i, j;
 12
 13     for (i = 0; i < 3; ++i) {
 14         for (j = 0; j < 3; ++j) {
 15             if (board[i * 3 + j] != -1) {
 16                 break;
 17             }
 18         }
 19         if (j == 3) {
 20             return true;
 21         }
 22     }
 23
 24     for (i = 0; i < 3; ++i) {
 25         for (j = 0; j < 3; ++j) {
 26             if (board[j * 3 + i] != -1) {
 27                 break;
 28             }
 29         }
 30         if (j == 3) {
 31             return true;
 32         }
 33     }
 34
 35     if (board[0] == board[4] && board[4] == board[8] && board[8] == -1) {
 36         return true;
 37     }
 38
 39     if (board[2] == board[4] && board[4] == board[6] && board[6] == -1) {
 40         return true;
 41     }
 42
 43     return false;
 44 }
 45
 46 bool humanWin(const vector<int> &board)
 47 {
 48     int i, j;
 49
 50     for (i = 0; i < 3; ++i) {
 51         for (j = 0; j < 3; ++j) {
 52             if (board[i * 3 + j] != 1) {
 53                 break;
 54             }
 55         }
 56         if (j == 3) {
 57             return true;
 58         }
 59     }
 60
 61     for (i = 0; i < 3; ++i) {
 62         for (j = 0; j < 3; ++j) {
 63             if (board[j * 3 + i] != 1) {
 64                 break;
 65             }
 66         }
 67         if (j == 3) {
 68             return true;
 69         }
 70     }
 71
 72     if (board[0] == board[4] && board[4] == board[8] && board[8] == 1) {
 73         return true;
 74     }
 75
 76     if (board[2] == board[4] && board[4] == board[6] && board[6] == 1) {
 77         return true;
 78     }
 79
 80     return false;
 81 }
 82
 83 bool fullBoard(const vector<int> &board)
 84 {
 85     for (int i = 0; i < 9; ++i) {
 86         if (board[i] == 0) {
 87             return false;
 88         }
 89     }
 90
 91     return true;
 92 }
 93
 94 void findComputerMove(vector<int> &board, int &best_move, int &result,
 95     int alpha, int beta)
 96 {
 97     void findHumanMove(vector<int> &, int &, int &, int, int);
 98     int dc, i, response;
 99
100     ++function_call_count;
101     best_move = -1;
102
103     if (fullBoard(board)) {
104         result = 0;
105         return;
106     }
107
108     if (computerWin(board)) {
109         result = -1;
110         return;
111     }
112
113     result = alpha;
114     for (i = 0; i < 9 && result > beta; ++i) {
115         if (board[i] != 0) {
116             continue;
117         }
118         board[i] = -1;
119         findHumanMove(board, dc, response, result, beta);
120         board[i] = 0;
121
122         if (best_move == -1 || response < result) {
123             result = response;
124             best_move = i;
125         }
126     }
127 }
128
129 void findHumanMove(vector<int> &board, int &best_move, int &result, int alpha,
130     int beta)
131 {
132     void findComputerMove(vector<int> &, int &, int &, int, int);
133     int dc, i, response;
134
135     ++function_call_count;
136     best_move = -1;
137
138     if (fullBoard(board)) {
139         result = 0;
140         return;
141     }
142
143     if (humanWin(board)) {
144         result = 1;
145         return;
146     }
147
148     result = beta;
149     for (i = 0; i < 9 && result < alpha; ++i) {
150         if (board[i] != 0) {
151             continue;
152         }
153         board[i] = 1;
154         findComputerMove(board, dc, response, alpha, result);
155         board[i] = 0;
156
157         if (best_move == -1 || response > result) {
158             result = response;
159             best_move = i;
160         }
161     }
162 }
163
164 void printBoard(const vector<int> &board)
165 {
166     cout << "  1 2 3" << endl;
167     int i, j;
168
169     for (i = 0; i < 3; ++i) {
170         cout << i + 1;
171         for (j = 0; j < 3; ++j) {
172             cout << ‘ ‘;
173             switch(board[i * 3 + j]) {
174             case -1:
175                 cout << ‘X‘;
176                 break;
177             case 0:
178                 cout << ‘.‘;
179                 break;
180             case 1:
181                 cout << ‘O‘;
182                 break;
183             }
184         }
185         cout << endl;
186     }
187 }
188
189 int main()
190 {
191     vector<int> board;
192     int n;
193     int result;
194
195     board.resize(9, 0);
196     while (cin >> n) {
197         if (n < 0 || n >= 9 || board[n]) {
198             cout << "Invalid move" << endl;
199             continue;
200         }
201
202         board[n] = 1;
203         printBoard(board);
204         if (humanWin(board)) {
205             cout << "You win." << endl;
206             break;
207         }
208
209         if (fullBoard(board)) {
210             cout << "Draw." << endl;
211             break;
212         }
213
214         result = 1;
215         function_call_count = 0;
216         findComputerMove(board, n, result, 1, -1);
217         cout << "Number of function calls: " << function_call_count << endl;
218         board[n] = -1;
219         printBoard(board);
220         if (computerWin(board)) {
221             cout << "Computer win." << endl;
222             break;
223         }
224
225         if (fullBoard(board)) {
226             cout << "Draw." << endl;
227             break;
228         }
229     }
230
231     return 0;
232 }

《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——Alpha-Beta剪枝,布布扣,bubuko.com

时间: 2024-10-05 06:12:05

《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——Alpha-Beta剪枝的相关文章

《数据结构与算法分析—C语言描述》pdf

下载地址:网盘下载 内容简介 编辑 <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行时间分析的基础上考查了一些高级数据结构,从历史的角度和近年的进展对数据结构的活跃领域进行了简要的概括.由于<数据结构与算法分析:C语言描述(原书第2版)>选材新颖,方法实用,题例丰富,取舍得当.<数据结构与算法分析:C语言描述(原书第2版)>的目的是培养学生良好的程序设计技巧和熟练的算

数据结构与算法分析 c语言描述 pdf 高清下载

网盘下载:数据结构与算法分析 c语言描述 pdf 高清下载 – 易分享电子书PDF资源网 作者: [美] Mark Allen Weiss 出版社: 机械工业出版社 副标题: C语言描述 原作名: Data Structures and Algorithm Analysis in C:Second Edition 译者: 冯舜玺 出版年: 2004-1-1 页数: 391 定价: 35.00元 装帧: 平装 内容简介 · · · · · · 本书是<Data Structures and Alg

数据结构与算法分析_Java语言描述(第2版)高清版pdf免费下载

下载地址:网盘下载 备用地址:网盘下载 内容简介编辑“数据结构”是计算机专业的基础与核心课程之一,Java是现今一种热门的语言.本书在编写过程中特别考虑到了面向对象程序设计(OOP)的思想与Java语言的特性.它不是从基于另一种程序设计语言的数据结构教材简单地“改编”而来的,因此在数据结构的实现上更加“地道”地运用了Java语言,并且自始至终强调以面向对象的方式来思考.分析和解决问题.本书是为数据结构入门课程(通常课号是CS-2)而编写的教材.作者Frank Carrano在编写过程自始至终特别

《数据结构与算法分析:C语言描述_原书第二版》CH3表、栈和队列_reading notes

表.栈和队列是最简单和最基本的三种数据结构.基本上,每一个有意义的程序都将明晰地至少使用一种这样的数据结构,比如栈在程序中总是要间接地用到,不管你在程序中是否做了生命. 本章学习重点: 理解抽象数据类型(ADT)的概念 学习如何对表进行有效的操作 熟悉栈ADT及其在实现递归方面的应用 熟悉队列ADT及其在操作系统和算法设计中的应用 ADT 抽象数据类型(abstract data type)是一个操作的集合,是数学的抽象,在ADT中不涉及如何实现操作的集合,这可以看作是模块化设计的扩充. 对于每

数据结构与算法分析_Java语言描述(第2版)pdf

下载地址:网盘下载 本书是国外数据结构与算法分析方面的经典教材,使用卓越的Java编程语言作为实现工具讨论了数据结构(组织大量数据的方法)和算法分析(对算法运行时间的估计). 随着计算机速度的不断增加和功能的日益强大,人们对有效编程和算法分析的要求也不断增长.本书将算法分析与最有效率的Java程序的开发有机地结合起来,深入分析每种算法,并细致讲解精心构造程序的方法,内容全面.缜密严格. 第3版的主要更新如下: ? 第4章包含AVL树删除算法的实现. ? 第5章进行了全面修订和扩充,现在包含两种较

《数据结构与算法分析Java语言描述》PDF文件免费下载

图书简介: 本书是国外数据结构与算法分析方面的经典教材,使用卓越的Java编程语言作为实现工具讨论了数据结构(组织大量数据的方法)和算法分析(对算法运行时间的估计). 随着计算机速度的不断增加和功能的日益强大,人们对有效编程和算法分析的要求也不断增长.本书把算法分析与最有效率的Java程序的开发有机地结合起来,深入分析每种算法,内容全面.缜密严格,并细致讲解精心构造程序的方法. 图书目录部分截图: 结语:总结 数据结构与算法分析需要这份PDF文档的朋友,欢迎加Q群:219571750,免费领取,

《数据结构与算法分析 C语言描述》读书笔记——分治算法

书中用求解最大子序列和的方式介绍了分治算法(divide-and-conquer) 分治算法是一种相对快速的算法 运行时间为O(logN) 最大子序列和的问题如下: 给出一组整数 A1  A2 … AN 求∑jk=i Ak 若所有整数均为负 则最大子序列和为0 e.g. 输入-2, 11,-4, 13, -5, -2 输出20(A2到A4) 分治算法就如同字面描述的一样 先分再治 分 指的是将问题分为两部分几乎相同的子问题 进行递归求解 治 指的是将 分 的解通过简单的手段合并 得到最终解 对于

【数据结构与算法分析——C语言描述】练习1.1——选择问题

本部分内容来自http://www.cnblogs.com/mingc,笔者在此只用于整理学习. 问题描述:编写一个程序解决选择问题.令k=N/2.画出表格显示你的程序对于N为不同值时的运行时间. 理解:设有一组N个数确定其中第k个最大者,称选择问题(selection problem) 思路:读入前k个数到临时数组tmp(并按降序排列).然后逐个读取后续数字X,当X大于第k个数时,将其加入数组tmp(并按降序排列).最后返回位置k-1上的值 #include <stdio.h> #inclu

《数据结构与算法分析——C语言描述》ADT实现(NO.01) : 栈(Stack)

这次的数据结构是一种特殊的线性表:栈(Stack) 栈的特点是后入先出(LIFO),可见的只有栈顶的一个元素. 栈在程序中的地位非常重要,其中最重要的应用就是函数的调用.每次函数调用时都会创建该函数的一个“活动记录”( Activation Record ,或称作“帧”( Frame ))压入运行时堆栈中,用于保存函数的参数,返回值,返回指令地址,返回活动记录地址,局部变量等内容.当然,在高级语言中我们不需要手动完成这一工作,学习汇编语言时会体会到其真正过程. 下面给出笔者对于堆栈的两种实现.首