C++ N元树的最佳数据结构

C++ N元树的最佳数据结构,c++,c++11,tree,artificial-intelligence,chess,C++,C++11,Tree,Artificial Intelligence,Chess,我需要表示每个节点具有多个分支的树。我应该使用什么结构?它是用来计算棋局状态的。它呈指数级爆炸,因此内存将成为一个问题。我正在使用C++11,但我对其他标准持开放态度。此外,修剪应为O(1) 编辑1 为了扩大规模,我将举办一场国际象棋AI比赛。主要的PvP游戏已经完成了,接下来我将编写AI API。参赛者将编写自己的AI,然后我们将让他们参加一场比赛。获胜者的AI将用于玩家对电脑游戏。我正在考虑存储游戏状态和AI想法的最佳结构 我在读深蓝,它认为从5到25步前进。我可以想象,大多数电脑都能用BF

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

编辑1 为了扩大规模,我将举办一场国际象棋AI比赛。主要的PvP游戏已经完成了,接下来我将编写AI API。参赛者将编写自己的AI,然后我们将让他们参加一场比赛。获胜者的AI将用于玩家对电脑游戏。我正在考虑存储游戏状态和AI想法的最佳结构

我在读深蓝,它认为从5到25步前进。我可以想象,大多数电脑都能用BFS处理5个动作,但任何更深的动作,我相信我都必须使用DFS

人工智能将定时,竞争人工智能将只在本地播放,以免在CPU能力方面引入优势

我现在正在阅读蒙特卡洛和阿尔法-贝塔搜索

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

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

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

class ChessMoveTree {
   std::set<ChessMoveNode> next;
};
union CHESS\u移动{
无符号短m;
棋牌位置[2];
///...
};
类棋子移动节点{
棋步;
std::设置下一个移动;
};
类棋子树{
std::设置next;
};
通过连接从根到叶的路径,可以随时计算电路板。尽管重新计算电路板可能会随着时间的推移而变得非常昂贵。思想?我应该把板子存起来吗?电路板存储为64个字符索引的数组,其中包含一个工件编号。所以它是16个字节,而不是2个字节,但是内存的使用将节省大量重新计算电路板状态的时间


对于我个人的AI,我将实现一个棋盘计分功能,该功能将对游戏状态进行排名,然后所有非最大游戏状态将被丢弃,并通过选择一个移动来修剪无效的游戏状态。

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

因此,根位于索引
0
。在该索引中有两个整数,表示子项从索引
1
开始,并且有
k
子项。(从索引
1
k
)在索引
1
处,子项将从索引
k+1
开始,并在整个树中依次类推

基于以下假设(1)子对象的数量是固定的,(2)它们一次全部添加,以及(3)状态不会从树中移除,这一点非常有效

如果您试图从树中删除状态,这也不起作用,因为如果删除它们,将在树中留下间隙。使用显式指针来存储每个子对象是非常昂贵的,因此在实践中还需要做一些其他事情

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

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


总而言之基于这样的假设,即您正在执行类似alpha-beta的操作,并且您有充分的理由显式存储树,我建议将状态存储在哈希表中,并将边保留为从移动生成函数隐式计算。(这将应用移动并获取结果状态的哈希以在哈希表中找到它们。如果它们不在那里,它们将被删除。)

O(1)为什么?总节点数?深度?如果您正在执行alpha-beta搜索之类的操作,则不应显式存储树。这种算法使用DFS来避免存储树。如果您使用的是MCT,则情况会有所不同。澄清您想要实现的算法将有助于回答这个问题。@Matthieu Brucher在游戏过程中删除所有未选择的分支。“它用于计算国际象棋游戏状态。它以指数方式爆炸,因此内存将是一个问题”-正确。到目前为止,任何设备都不可能存储表示每个可能的国际象棋场景所需的数据。您需要找到另一种方法或介绍树的深度。@BugSquasher可能的国际象棋游戏数为10^120。可观测宇宙中的原子数约为10^80。即使你以某种方式使用一个位来表示一个移动或一个董事会状态,你也不走运。我将查找Monte Carlo树搜索一个状态的哈希值将远远大于一个持有移动的无符号空头。内存使用是一个问题。我还必须为每个位置的棋盘打分,并且只有在搜索了整个级别后才能知道最大分数,因为如果后面的位置导致将死,则会大大增加较低节点的值。我还是不知道该怎么办。你觉得我的编辑怎么样?@BugSquasher我觉得你有点不知所措。首先让一些东西工作,然后优化内存。阅读更多关于游戏树搜索技术的文章-在这个主题上已经做了大量的工作。对于你的人工智能,要么走阿尔法-贝塔路线,在这种情况下,你根本不存储树,而是使用换位表,要么走阿尔法-零路线,使用MCT。30^5大约只有2000万。这棵树可以被充分挖掘5个深度,然后我就开始计划MC了