先决条件:博弈论中的极小极大算法,博弈论中的评估函数
Alpha-Beta 剪枝实际上并不是一种新算法,而是一种极小极大算法的优化技术。它大大减少了计算时间。这使我们能够更快地搜索,甚至可以进入博弈树的更深层次。它切断了游戏树中不需要搜索的分支,因为已经存在更好的移动可用。它被称为 Alpha-Beta 剪枝,因为它在 minimax函数传递了 2 个额外的参数,即 alpha 和 beta。
让我们定义参数 alpha 和 beta。
Alpha是最大化器当前可以保证在该级别或更高级别的最佳值。
Beta是最小化器当前可以保证在该级别或更高级别的最佳值。
伪代码:
function minimax(node, depth, isMaximizingPlayer, alpha, beta):
if node is a leaf node :
return value of the node
if isMaximizingPlayer :
bestVal = -INFINITY
for each child node :
value = minimax(node, depth+1, false, alpha, beta)
bestVal = max( bestVal, value)
alpha = max( alpha, bestVal)
if beta <= alpha:
break
return bestVal
else :
bestVal = +INFINITY
for each child node :
value = minimax(node, depth+1, true, alpha, beta)
bestVal = min( bestVal, value)
beta = min( beta, bestVal)
if beta <= alpha:
break
return bestVal
// Calling the function for the first time.
minimax(0, 0, true, -INFINITY, +INFINITY)
让我们通过一个例子来清楚地说明上述算法。
- 初始调用从A开始。此处 alpha 的值为-INFINITY ,而 beta 的值为+INFINITY 。这些值向下传递到树中的后续节点。在A 处,最大化器必须选择B和C 的最大值,因此A首先调用B
- 在B 处,最小化器必须选择D和E 的最小值,因此首先调用D。
- 在D 处,它查看其左子节点,即叶节点。此节点返回值 3。 现在D处的 alpha 值是 max( -INF, 3),即 3。
- 为了决定是否值得查看其正确的节点,它会检查条件 beta<=alpha。这是错误的,因为 beta = +INF 和 alpha = 3。所以它继续搜索。
- D现在查看它的右子节点,它返回值 5。在D 处,alpha = max(3, 5) 是 5。现在节点D的值是 5
- D将值 5 返回给B 。在B 处,beta = min( +INF, 5) 即 5。现在保证最小值为 5 或更小。 B现在打电话给E看看他是否能得到一个低于 5 的值。
- 在E处,alpha 和 beta 的值不是 -INF 和 +INF,而是分别是 -INF 和 5,因为 beta 的值在B 处发生了变化,这就是B传递给E 的值
- 现在E查看它的左孩子,即 6。在E 处,alpha = max(-INF, 6) 是 6。这里条件成立。 beta 是 5,alpha 是 6。所以 beta<=alpha 是真的。因此它中断并且E将 6 返回给B
- 请注意E的右孩子的值如何无关紧要。它可能是 +INF 或 -INF,它仍然无关紧要,我们甚至不必查看它,因为保证最小值为 5 或更小。因此,当最大化者看到 6 时,他知道最小化者永远不会以这种方式出现,因为他可以在B的左侧得到 5 。这样我们就不必查看那个 9,从而节省了计算时间。
- E将值 6 返回给B 。在B 处,beta = min( 5, 6) 即 5。节点B 的值也是 5
到目前为止,这就是我们的博弈树的样子。 9 被划掉,因为它从未被计算过。
- B将 5 返回给A 。在A 处, alpha = max( -INF, 5) 为 5。现在保证最大值为 5 或更大。 A现在调用C以查看它是否可以获得比 5 更高的值。
- 在C 处,alpha = 5 且 beta = +INF。 C调用F
- 在F 处,alpha = 5 且 beta = +INF。 F查看它的左孩子,它是 1。alpha = max( 5, 1) 仍然是 5。
- F看它的右孩子是 2。因此这个节点的最佳值是 2。Alpha 仍然是 5
- F将值 2 返回给C 。在C 处,beta = min( +INF, 2)。当 beta = 2 和 alpha = 5 时,条件 beta <= alpha 变为真。因此它中断了,它甚至不必计算G的整个子树。
- 这种中断背后的直觉是,在C 处,保证最小值为 2 或更小。但是如果他选择B ,最大化者已经保证了 5 的值。那么为什么最大化器会选择C并得到小于 2 的值?您再次可以看到,最后两个值是什么并不重要。我们还通过跳过整个子树节省了大量计算。
- C现在将值 2 返回给A 。因此, A处的最佳值是 max(5, 2),即 5。
- 因此最大化器可以得到的最优值是 5
这就是我们最终的游戏树的样子。如您所见, G已被划掉,因为它从未被计算过。
CPP
// C++ program to demonstrate
// working of Alpha-Beta Pruning
#include
using namespace std;
// Initial values of
// Aplha and Beta
const int MAX = 1000;
const int MIN = -1000;
// Returns optimal value for
// current player(Initially called
// for root and maximizer)
int minimax(int depth, int nodeIndex,
bool maximizingPlayer,
int values[], int alpha,
int beta)
{
// Terminating condition. i.e
// leaf node is reached
if (depth == 3)
return values[nodeIndex];
if (maximizingPlayer)
{
int best = MIN;
// Recur for left and
// right children
for (int i = 0; i < 2; i++)
{
int val = minimax(depth + 1, nodeIndex * 2 + i,
false, values, alpha, beta);
best = max(best, val);
alpha = max(alpha, best);
// Alpha Beta Pruning
if (beta <= alpha)
break;
}
return best;
}
else
{
int best = MAX;
// Recur for left and
// right children
for (int i = 0; i < 2; i++)
{
int val = minimax(depth + 1, nodeIndex * 2 + i,
true, values, alpha, beta);
best = min(best, val);
beta = min(beta, best);
// Alpha Beta Pruning
if (beta <= alpha)
break;
}
return best;
}
}
// Driver Code
int main()
{
int values[8] = { 3, 5, 6, 9, 1, 2, 0, -1 };
cout <<"The optimal value is : "<< minimax(0, 0, true, values, MIN, MAX);;
return 0;
}
Java
// Java program to demonstrate
// working of Alpha-Beta Pruning
import java.io.*;
class GFG {
// Initial values of
// Aplha and Beta
static int MAX = 1000;
static int MIN = -1000;
// Returns optimal value for
// current player (Initially called
// for root and maximizer)
static int minimax(int depth, int nodeIndex,
Boolean maximizingPlayer,
int values[], int alpha,
int beta)
{
// Terminating condition. i.e
// leaf node is reached
if (depth == 3)
return values[nodeIndex];
if (maximizingPlayer)
{
int best = MIN;
// Recur for left and
// right children
for (int i = 0; i < 2; i++)
{
int val = minimax(depth + 1, nodeIndex * 2 + i,
false, values, alpha, beta);
best = Math.max(best, val);
alpha = Math.max(alpha, best);
// Alpha Beta Pruning
if (beta <= alpha)
break;
}
return best;
}
else
{
int best = MAX;
// Recur for left and
// right children
for (int i = 0; i < 2; i++)
{
int val = minimax(depth + 1, nodeIndex * 2 + i,
true, values, alpha, beta);
best = Math.min(best, val);
beta = Math.min(beta, best);
// Alpha Beta Pruning
if (beta <= alpha)
break;
}
return best;
}
}
// Driver Code
public static void main (String[] args)
{
int values[] = {3, 5, 6, 9, 1, 2, 0, -1};
System.out.println("The optimal value is : " +
minimax(0, 0, true, values, MIN, MAX));
}
}
// This code is contributed by vt_m.
Python3
# Python3 program to demonstrate
# working of Alpha-Beta Pruning
# Initial values of Aplha and Beta
MAX, MIN = 1000, -1000
# Returns optimal value for current player
#(Initially called for root and maximizer)
def minimax(depth, nodeIndex, maximizingPlayer,
values, alpha, beta):
# Terminating condition. i.e
# leaf node is reached
if depth == 3:
return values[nodeIndex]
if maximizingPlayer:
best = MIN
# Recur for left and right children
for i in range(0, 2):
val = minimax(depth + 1, nodeIndex * 2 + i,
False, values, alpha, beta)
best = max(best, val)
alpha = max(alpha, best)
# Alpha Beta Pruning
if beta <= alpha:
break
return best
else:
best = MAX
# Recur for left and
# right children
for i in range(0, 2):
val = minimax(depth + 1, nodeIndex * 2 + i,
True, values, alpha, beta)
best = min(best, val)
beta = min(beta, best)
# Alpha Beta Pruning
if beta <= alpha:
break
return best
# Driver Code
if __name__ == "__main__":
values = [3, 5, 6, 9, 1, 2, 0, -1]
print("The optimal value is :", minimax(0, 0, True, values, MIN, MAX))
# This code is contributed by Rituraj Jain
C#
// C# program to demonstrate
// working of Alpha-Beta Pruning
using System;
class GFG
{
// Initial values of
// Aplha and Beta
static int MAX = 1000;
static int MIN = -1000;
// Returns optimal value for
// current player (Initially called
// for root and maximizer)
static int minimax(int depth, int nodeIndex,
Boolean maximizingPlayer,
int []values, int alpha,
int beta)
{
// Terminating condition. i.e
// leaf node is reached
if (depth == 3)
return values[nodeIndex];
if (maximizingPlayer)
{
int best = MIN;
// Recur for left and
// right children
for (int i = 0; i < 2; i++)
{
int val = minimax(depth + 1, nodeIndex * 2 + i,
false, values, alpha, beta);
best = Math.Max(best, val);
alpha = Math.Max(alpha, best);
// Alpha Beta Pruning
if (beta <= alpha)
break;
}
return best;
}
else
{
int best = MAX;
// Recur for left and
// right children
for (int i = 0; i < 2; i++)
{
int val = minimax(depth + 1, nodeIndex * 2 + i,
true, values, alpha, beta);
best = Math.Min(best, val);
beta = Math.Min(beta, best);
// Alpha Beta Pruning
if (beta <= alpha)
break;
}
return best;
}
}
// Driver Code
public static void Main (String[] args)
{
int []values = {3, 5, 6, 9, 1, 2, 0, -1};
Console.WriteLine("The optimal value is : " +
minimax(0, 0, true, values, MIN, MAX));
}
}
// This code is contributed by 29AjayKumar
Javascript
输出 :
The optimal value is : 5
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。