构建许多哈希表

时间:2012-06-04 00:58:51

标签: c++ c++11

我有一棵125,000个节点的树(最多2个孩子)。我正在尝试确定每个节点的子节点数(直接和间接)。因为树是DAG 但是每个子节点的链接数量是无限制的,所以许多节点实际上将所有其他节点都作为子节点。树的总复杂性仅供参考,如果没有记忆表达,则超过10 ^ 30。这意味着,即使存储一个指向每个子节点的简单指针(并记住输出)也会产生15.625GB的数据块,甚至会忽略散列表,内存分配器和其他开销。

虽然这是所需的输出,但它需要花费太长时间和太多的内存来实现。我只有一个工作站,公平但不是顶级电源(i7 930,6GB RAM)。

有什么方法可以记忆或以其他方式缓存表格,以便在合理的时间内仍然可以访问数据(我可能会对数据进行数十万次访问)?我考虑过懒惰地评估查询,但我担心访问它们需要多长时间。

另外,我并不特别感兴趣哪些节点是孩子,但我需要知道他们的 - 这基本上是相同的事情我相信,因为我不能算同一个孩子两次。

编辑:树是不可变的。我所要做的就是阅读孩子的数量。

5 个答案:

答案 0 :(得分:1)

如果要遍历直接非循环图而不经过两次节点(例如,计算每个节点一次),则可以向每个节点添加mutable布尔值,指示之前是否已遍历该节点。您可以通过标记节点,查看节点以及递归遍历节点的未标记子节点来查看节点的所有后代。

答案 1 :(得分:1)

看起来你已经找到了答案,但只是因为踢the transitive closure of a DAG可能对其他人考虑这类问题很有用。

Timothy Chan发布了a paper in 2005 with a footnote about efficiently computing the transitive closure of a DAG。引自论文:

  

...对于计算传递闭包的简单问题   未加权的有向图,Yuster和Zwick在最近的一篇论文中问道   对于O(mn)时间算法,但是O(mn / log n + n 2 )时间   绑定实际上很容易得到RAM。 2

     

...

     

2 证明:假设图是非循环的,因为我们可以预先计算   线性时间内紧密连接的组件,每个组合   零件。我们想在所有顶点上找到 S u 集合   可以从每个顶点 u 到达。对于每个顶点 u 反向   拓扑顺序,我们可以通过取结合来计算 S u    S v 来自 u 的所有顶点 v 事件。每个O(m)   set-union操作可以在O(n / log n)时间内执行   将一个集合表示为(n / log n)字向量并使用   按位或操作。

显然还有一点需要弄清楚 - 你需要预先计算“强连接组件”并且必须能够以反向拓扑顺序访问节点 - 但是他描述了有效地采用重复联合声音的过程比如计算DAG中给定节点的子节点数的合理方法。

答案 2 :(得分:0)

我会缓存节点本身中节点的后代数。由于节点是不可变的,因此您可以计算并缓存后代计数,而不必担心缓存的值会变得陈旧。

给定节点的后代数是1加上每个子节点的子孙数(直接子节点,而不是间接子节点)的总和。由于每个孩子都缓存了其后代的计数,这是一个非常快速的计算。

答案 3 :(得分:0)

试图打破一些算法? :顽皮:最简单的方法是使用n / nlogn(一般工作)n(一般参考)情况,在这种情况下,您将存储对该表的键引用,并在需要特定映射哈希堆栈时查找。因此,例如,n个根节点有n1个子节点和n2个子节点,n1子节点没有被处理,但是在磁盘上引用并保存在某个地方,但是n1根节点(这与n1子节点基本相同,但是没有将被计算)将进一步处理并具有n11和n12子节点,然后n11根节点将被映射为对该树的引用,依此类推。因此,例如,您将只有125,000个引用键,其中包含指向其他引用键的指针,如果需要,您可以使用任何您想要的内容,这将是您的PC真正处理的内容,其余内容只会在同一时间进行处理和磨损。

答案 4 :(得分:0)

这可以通过mapreduce方法完成,因为部分问题是大型数据集。这是一种非常不同的方式,因为它在文本文件和集群领域而不是C ++领域,但它至少会随着机器数量的增加而不会影响完成时间。

该方法将以key:value对开始,表示每个有向边,并构建从每个节点下降的节点集,直到每个节点标记为done。从两个子节点收集时,使用子集的交集,并分别维护一组不完整的节点。这应该采取像2地图和&收集每个深度级别,但显然随着您提升图表而减少工作量。

显然计算工作量更大,但它可以利用现有的hadoop等系统来轻松扩展。

我认为这个答案有两个层次:a)考虑多遍方法,b)考虑hadoop等人分发工作。