【问题标题】:Best data structure for an N-ary treeN叉树的最佳数据结构
【发布时间】:2019-05-30 14:43:47
【问题描述】:

我需要用每个节点的多个分支来表示一棵树。我应该使用什么结构?它用于计算国际象棋游戏状态。它呈指数级增长,因此内存将成为一个问题。我正在使用 C++11,但对其他标准持开放态度。此外,修剪应该是 O(1)。

编辑1 为了扩展,我将举办一场国际象棋人工智能比赛。主要的 PvP 游戏已经完成,接下来我正在编写 AI API。参赛者将编写自己的 AI,然后我们将让他们参加比赛。获胜者的 AI 将用于 Player vs Computer 游戏。我只是在考虑存储我的游戏状态和 AI 想法的最佳结构。

我正在阅读 Deep Blue,它认为前进了 5 到 25 步。我可以想象大多数计算机能够使用 BFS 处理 5 次深度移动,但任何更深的移动,我相信我将不得不使用 DFS。

AI 将被计时,竞争 AI 将仅在本地播放,以免在 CPU 功率方面引入优势。

我现在正在阅读 Monte Carlo 和 Alpha Beta 搜索。

我对数据结构的初步想法如下:

union CHESS_MOVE {
   unsigned short m;
   ChessPosT pos[2];
   ///...
};

class ChessMoveNode {
   CHESS_MOVE move;
   std::set<ChessMoveNode> nextmoves;
};

class ChessMoveTree {
   std::set<ChessMoveNode> next;
};

可以通过连接从根到叶的路径随时计算板。尽管随着时间的推移重新计算电路板可能会变得非常昂贵。想法?我应该存放电路板吗?棋盘存储为一个包含 64 个字符索引的数组,其中包含一个棋子编号。所以是16个字节,相比2个,但是内存的使用会省去很多板子状态的重新计算。

对于我自己的个人 AI,我将实现一个棋盘评分功能,对游戏状态进行排名,然后丢弃所有非最大游戏状态,以及修剪因选择移动而无效的游戏状态。

【问题讨论】:

  • O(1) 是为了什么?总节点数?深度?
  • 如果您正在执行 alpha-beta 搜索之类的操作,则不应显式存储树。此类算法使用 DFS 来避免存储树。如果您使用的是 MCTS,情况会有所不同。明确你想要实现的算法将有助于回答这个问题。
  • @Matthieu Brucher 在游戏过程中移除所有未选择的分支。
  • "它用于计算国际象棋游戏状态。它呈指数增长,因此内存将成为一个问题" - 正确。截至目前,任何设备都不可能存储代表每种可能的国际象棋场景所需的数据。您需要找到另一种方法或引入树的深度。
  • @BugSquasher 可能的国际象棋游戏数量为 10^120。可观测宇宙中的原子数约为 10^80。即使您以某种方式使用单个位来表示移动或棋盘状态,您也不走运。

标签: c++ c++11 tree artificial-intelligence chess


【解决方案1】:

一种适用于蒙特卡洛树搜索 (MCTS) 的简单方法是使用某个自定义类的 vector。在类中,除了子信息之外,您还拥有所需的任何状态信息——孩子的数量及其在向量中的索引。这样可以避免为每个子节点存储一个单独的指针,这会带来很大的开销。

因此,根位于索引0。在该索引内将有两个整数表示子级从索引1 开始,并且有k 子级。 (从索引1k。)在索引1 处,子级将从索引k+1 开始,子级总数为l,依此类推。

基于以下假设,这非常有效:(1) 子节点的数量是固定的,(2) 它们都是一次性添加的,以及 (3) 状态不会从树中删除。

如果您尝试从树中修剪状态,这也不起作用,因为如果您删除它们,您会在树中留下空隙。使用显式指针来存储每个孩子的成本很高,因此在实践中需要做其他事情。

首先,使用 alpha-beta 搜索,您通常使用 DFS 搜索树并且不存储分支。但是,您使用哈希表来存储状态并检查重复项。树的分支可以从状态中隐式计算出来,因此您可以在不显式存储所有内容的情况下重建树。

但是请注意,哈希表(在博弈树搜索的上下文中称为转置表)通常不会在树的深处使用,因为存在许多状态并且存储成本会增加,而删除重复项的好处会减少。

总结,基于假设您正在做类似 alpha-beta 的事情并且您有充分的理由显式存储树,我建议将状态存储在哈希表中并保留要从移动生成函数隐式计算的边。 (这将应用移动并获取结果状态的哈希以在哈希表中找到它们。如果它们不存在,则它们已被修剪。)

【讨论】:

  • 我会查找蒙特卡洛树搜索
  • 状态的哈希值将比持有移动的单个无符号空头大得多。内存使用是一个问题。我还必须对每个位置的棋盘进行评分,并且只有在搜索了整个级别后才能知道最大分数,因为如果后面的位置导致将死,则可以大大增加较低节点的值。我仍然不确定该怎么做。你觉得我的编辑怎么样?
  • @BugSquasher 我觉得你有点过头了。首先让一些东西工作,然后优化内存。阅读更多关于游戏树搜索技术的信息——关于这个主题已经完成了大量的工作。对于你的 AI,要么走 alpha-beta 路线,在这种情况下,你根本不存储树,而是使用转置表,或者走 alpha-zero 路线并使用 MCTS。
  • 30^5 只有2000万左右。这棵树可以被完全探索 5 深,然后我正在计划 MCTS
  • @BugSquasher 所描述的方法适用于 MCTS - 这是我个人实现 MCTS 的方式。
猜你喜欢
  • 2020-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多