Minimax 与 Alpha-Beta 剪枝;类变量或通过递归发送它们?
当使用 Minimax 和 Alpha-Beta 剪枝时,是否可以将 alpha 和 beta 作为类变量,而不是通过递归发送它们?
而不是:
private ValuedMove AlphaBetaSearch(Board state)
{
return MaxValue(state, 0, int.MinValue, int.MaxValue);
}
private ValuedMove MaxValue(Board state, int d, int alpha, int beta)
{
if (d == depth || state.GameRunning == false)
return new ValuedMove(Heuristic.BoardValue(state, Player));
ValuedMove v = new ValuedMove(int.MinValue);
foreach (Move move in LegalMoves)
{
ValuedMove minCheck = MinValue(state.ImagineMove(move), d + 1, alpha, beta);
if (v.Value >= beta)
return v;
alpha = Max(alpha, v.Value);
}
return v;
}
private ValuedMove MinValue(Board state, int d, int alpha, int beta)
{
//Minimax and Alpha-Beta logic here
}
我可以写:
int alpha, beta;
private ValuedMove AlphaBetaSearch(Board state)
{
alpha = int.MinValue;
beta = int.MaxValue;
return MaxValue(state, 0);
}
private ValuedMove MaxValue(Board state, int d)
{
//Minimax and Alpha-Beta logic here
}
private ValuedMove MinValue(Board state, int d)
{
//Minimax and Alpha-Beta logic here
}
我问是因为当我尝试通过这样做来优化代码时(我的想法是,如果我不需要将整数发送到每个递归,我可能能够剥离一点时间),我的棋手突然变成了一个白痴,牺牲了他的皇后来杀死一个棋子,并犯了其他愚蠢的错误。
他的表现总是比他的“常规 Alpha-Beta”对手差很多,我猜这是因为与他的对手相比,他也只搜索了树的一小部分(他们都使用相同的深度,但修改后的玩家似乎修剪了更积极,从而减少访问的节点数量)。为了确保这一点,我现在已经这样做了两次,除了我在这里勾画的内容之外,我没有改变任何其他内容。
如果我正确理解了 Alpha-Beta 算法,这应该不会有任何区别,但对于我的国际象棋棋手来说,它确实有什么区别。我做错了什么吗?
所以,我现在的主要问题不是这是否是优化明智或代码实践明智的好事,而是它是否应该可以做到。
When using Minimax with Alpha-Beta pruning, is it possible to have alpha and beta as class variables instead of sending them through the recursion?
Instead of:
private ValuedMove AlphaBetaSearch(Board state)
{
return MaxValue(state, 0, int.MinValue, int.MaxValue);
}
private ValuedMove MaxValue(Board state, int d, int alpha, int beta)
{
if (d == depth || state.GameRunning == false)
return new ValuedMove(Heuristic.BoardValue(state, Player));
ValuedMove v = new ValuedMove(int.MinValue);
foreach (Move move in LegalMoves)
{
ValuedMove minCheck = MinValue(state.ImagineMove(move), d + 1, alpha, beta);
if (v.Value >= beta)
return v;
alpha = Max(alpha, v.Value);
}
return v;
}
private ValuedMove MinValue(Board state, int d, int alpha, int beta)
{
//Minimax and Alpha-Beta logic here
}
Can I write:
int alpha, beta;
private ValuedMove AlphaBetaSearch(Board state)
{
alpha = int.MinValue;
beta = int.MaxValue;
return MaxValue(state, 0);
}
private ValuedMove MaxValue(Board state, int d)
{
//Minimax and Alpha-Beta logic here
}
private ValuedMove MinValue(Board state, int d)
{
//Minimax and Alpha-Beta logic here
}
I am asking because when I tried to optimize the code by doing so (my thought was that if I didn't need to send the ints through to each recursion, I might be able to peel of a little time), my chess player suddenly became an idiot, sacrificing his queen to kill a pawn, and doing other silly mistakes.
He constantly performs a lot poorer than his "regular Alpha-Beta" opponent, which I guess is because he also searches only a small percentage of the tree compared to his opponent (they both use the same depth, but the modified player seems to prune more aggressive, and thereby reducing the number of nodes visited). I have done this twice now, to make sure, and I do not change anything else than what I scetch out here.
If I have understood the Alpha-Beta algorithm correct, this shouldn't make any difference, but for my chess player, it does. Am I doing anything wrong?
So, my main question now is not whether it is a optimization wise or code practice wise good thing to do, but rather whether it should be possible to do or not.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
你真的不能这样做。 AlphaBeta 是一种递归算法。这意味着它调用自己。每次它调用自己时,它都会使用(可能)不同的 alpha 和 beta 值。每个递归都需要自己的变量版本来保存不同的值。如果将变量包含在类中,则在对 AlphaBeta 的所有(递归)调用之间将仅共享一组变量。
话虽这么说,用类成员替换函数参数可能不是一个好的优化。这两种技术都有成本。参数需要在调用之前压入堆栈,并且需要通过指针(隐藏的 this 指针)间接访问成员。让我们忘记哪个成本更高。这种微观优化可能微不足道,根本不会产生任何影响。
You can't really do this. AlphaBeta is a recursive Algorithm. That means that it calls itself. Each time it calls itself it does so with (possibly) different values for alpha and beta. Each recursion needs its own version of the variables that will hold different values. If you include the variables in the class you will only have one set of variables shared between all (recursive) calls to AlphaBeta.
This being said, replacing function parameters with class members is probably not a good optimization. Both techniques have a cost. The parameters need to be pushed on the stack before the call and the members need to be accessed indirectly via a pointer (the hidden
this
pointer). Let's forget about which cost is the higher. This micro-optimization is probably too insignificant to make any difference at all.