我有一个alpha-beta修剪的基本实现,但我不知道如何改进移动顺序。我已经读过可以通过浅搜索,迭代加深或将bestMoves存储到转换表来完成。
有关如何在此算法中实施这些改进之一的任何建议吗?
public double alphaBetaPruning(Board board, int depth, double alpha, double beta, int player) {
if (depth == 0) {
return board.evaluateBoard();
}
Collection<Move> children = board.generatePossibleMoves(player);
if (player == 0) {
for (Move move : children) {
Board tempBoard = new Board(board);
tempBoard.makeMove(move);
int nextPlayer = next(player);
double result = alphaBetaPruning(tempBoard, depth - 1, alpha,beta,nextPlayer);
if ((result > alpha)) {
alpha = result;
if (depth == this.origDepth) {
this.bestMove = move;
}
}
if (alpha >= beta) {
break;
}
}
return alpha;
} else {
for (Move move : children) {
Board tempBoard = new Board(board);
tempBoard.makeMove(move);
int nextPlayer = next(player);
double result = alphaBetaPruning(tempBoard, depth - 1, alpha,beta,nextPlayer);
if ((result < beta)) {
beta = result;
if (depth == this.origDepth) {
this.bestMove = move;
}
}
if (beta <= alpha) {
break;
}
}
return beta;
}
}
public int next(int player) {
if (player == 0) {
return 4;
} else {
return 0;
}
}
答案 0 :(得分:16)
浅层搜索的节点重新排序很简单:计算 递归之前状态的每个子项的启发式值 检查他们。然后,对这些状态的值进行排序[降序 对于最大顶点,并为最小顶点提升],并递归调用 排序列表上的算法。这个想法是 - 如果一个国家擅长 浅深度,更有可能擅长深度状态, 如果这是真的 - 你会得到更多的修剪。
排序应在之前完成[在[{1}}和if
条款中
else
存储动作也很简单 - 许多状态计算两次,
当你完成任何状态的计算时,存储它[深度为
计算!它是重要的!] for (Move move : children) {
。你做的第一件事
当你开始计算一个顶点 - 检查它是否已经
计算 - 如果是,则返回缓存的值。背后的想法
可以从不同的路径到达许多州,所以这个
方式 - 您可以消除冗余计算。
改变应该在方法的第一行[类似HashMap
] [请原谅我缺乏优雅和效率 - 只是在这里解释一个想法]。
您还应在每个if (cache.contains((new State(board,depth,player)) return cache.get(new State(board,depth,player))
声明之前添加cache.put(...)
。
答案 1 :(得分:1)
首先,我必须了解alpha-beta修剪算法中移动排序背后的原因。 Alpha-beta产生与minimax相同的结果,但在很多情况下可以更快地完成,因为它不会搜索不相关的分支。
并不总是更快,因为它不能保证修剪,如果事实上在更糟糕的情况下它根本不会修剪并且搜索与minimax完全相同的树并且因为a / b值簿记而会更慢。在最好的情况下(最大修剪),它允许同时搜索2倍深的树。对于随机树,它可以在同一时间内深度搜索4/3倍。
移动排序可以通过以下几种方式实现:
您提到的第二种方法与移动排序无关。这与评估功能可能很昂贵且许多位置被多次评估的事实有关。为了绕过这个,您可以在计算后将其存储在哈希值中,并在以后重复使用。