先决条件:博弈论中的极小极大算法,博弈论中的评估函数
Alpha-Beta修剪实际上并不是一种新算法,而是针对minimax算法的一种优化技术。它极大地减少了计算时间。这使我们可以更快地进行搜索,甚至可以进入游戏树的更深层次。它切断了游戏树中不需要搜索的分支,因为已经存在一个更好的移动方法。之所以称为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向B返回值5。在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向B返回值6。在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向C返回2的值。在C处,β= min(+ INF,2)。当beta = 2且alpha = 5时,条件beta <= alpha变为真。因此它会中断,甚至不必计算G的整个子树。
- 这种突破的直觉是,在C处,最小化器的值保证为2或更小。但是,如果他选择B,则已经保证了最大化器的值为5。那么,为什么最大化器会选择C并得到小于2的值?再次您可以看到,最后两个值是什么并不重要。通过跳过整个子树,我们还节省了大量计算。
- C现在向A返回2的值。因此, A处的最佳值为max(5,2),即5。
- 因此,最大化器可以获得的最佳值为5
这就是我们最终的游戏树的样子。如您所见,因为从未计算过G ,所以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
输出 :
The optimal value is : 5