C# 处理非常大的树状数据结构:OutOfMemoryException

C# 处理非常大的树状数据结构:OutOfMemoryException,c#,tree,C#,Tree,我正在创建一个象棋程序的变体,它需要同时生成和遍历一个非常大的树状结构。每个节点有10个布尔、一个整数、8个ulong、一个短[64]和2个ulong[64]s。根节点接收一些初始参数,然后通过编程(递归)从那里确定有效的子节点 基本上,当用户和程序轮流从一个子节点遍历到另一个子节点时,我的程序会不断地增长这棵树。每次“选择”新的子节点时,不再需要它的父节点和同级节点,并将其丢弃。当树达到(平均)约60个深度(从初始根节点开始)时,有效子节点的数量自然会开始减少,直到达到约75个深度时,树解析为

我正在创建一个象棋程序的变体,它需要同时生成和遍历一个非常大的树状结构。每个节点有10个布尔、一个整数、8个ulong、一个短[64]和2个ulong[64]s。根节点接收一些初始参数,然后通过编程(递归)从那里确定有效的子节点

基本上,当用户和程序轮流从一个子节点遍历到另一个子节点时,我的程序会不断地增长这棵树。每次“选择”新的子节点时,不再需要它的父节点和同级节点,并将其丢弃。当树达到(平均)约60个深度(从初始根节点开始)时,有效子节点的数量自然会开始减少,直到达到约75个深度时,树解析为最后一个节点,不再有子节点

这背后的逻辑一开始看起来相当直截了当,但我经常遇到OutOfMemoryException,这会彻底扼杀任何进一步的进展

以下是每“一代”有效子代的一些平均值:

Generation    New Nodes
1             1    
2             20
3             4,000
4             30,000
5             2,200,000
6             > 50,000,000
在我的实际程序中,我甚至不能完全扩展第五代。当我不持久化特定于节点的数据时(一旦节点的数据被用来确定它自己的子节点,我就会清除它),我可以完全扩展第五代,但在第六代的过程中遇到了一堵非常坚固的墙

理想情况下,我希望我的程序最终到达并在“当前”节点之后维护8代节点。我看得越多,这种可能性就越小

我厌倦了用sqlite数据库运行它,但是它不能足够快地增长树


有没有人知道,除了处理一个非常大的树结构之外,还有什么潜在的替代方案

你的问题没有一般性的答案。我会假设这棵大树是用来计算你的计算机程序的最优移动的

在这种情况下,定义一系列动作的效用函数可能会对您有所帮助,该函数用于衡量在游戏中进行这一系列动作的价值。如果目标是达到最高分数或类似的分数,那么该分数是一个很好的效用函数

有时,你无法得到一个准确的效用函数,在这种情况下,一个常见的方法是对效用进行启发式评估。基本上,这是一个近似值,或者说是一个最佳猜测。启发式越强,对手越强

您希望使用实用工具度量的原因是执行修剪。例如,首先遍历树的深度几次,然后计算最小和最大效用。这些值可以帮助您修剪完整的算法,这意味着您可以使用这些边界来确定树遍历算法是否可以在完成之前终止


同样,这完全取决于你的游戏机制和你如何遍历树,但希望这能让你朝着正确的方向思考。

通常你在构建树时会有一个评估,这已经给了你一个边上的权重。使用这些权重可以查看哪些路径比其他路径评估的路径更强,并在看到哪些路径更有价值后,立即对这些边进行处理。由于您的算法中的第五代已经存在问题,因此您只能选择深入研究更高权重的分支,然后选择其中一个,忽略大量其他分支。只是一个想法,所以。。。也许你可以在第三代人身上运行它,选择走哪条路。据我所知,这可能会让你只使用更多可移动的棋子进行棋步,因为它们可能会对游戏产生更大的影响,与第五代棋步相比,这可能不是最好的解决方案。非常有趣的问题

您应该调查国际象棋编程:

以下是有关发动机的更多信息:


还有一个论坛,讨论不同的方法

每一行占用222个字节,乘以5000万可以得到11GB的内存负载。您面临的问题并不奇怪。是不是您试图让所有节点都存在于内存中的问题?@KirkWoll如果数组是按节点分配的,那么每个节点的总数据实际上要大得多。@Brian,您是对的。我把
2 ulong[64]
的意思混淆为
ulong[2]
——所以这比我说的要糟糕得多。另一个适用于某些游戏的一般想法是使用某种类似的扣球动作,从而减少每个节点的分支因子。它将使计算最优决策变得不可能,但如果计算最优决策在计算上无论如何都是不可行的,那么仍然可以做出更好的决策。