我正在处理一个包含5亿个节点的非常大的图,平均节点数为100.所以它是一种稀疏图。我还必须存储每个边缘的重量。我目前正在使用两个向量,如下面的
vector
使用vector
<h2> {{user.first_name}} </h2>
<h2> {{user.last_name}} </h2>
<h2> {{user.email}} </h2>
<h2> {{user.website}} </h2>
<h2> {{user.bio}} </h2>
<h2> {{user.phone}} </h2>
<h2> {{user.city}} </h2>
<h2> {{user.country}} </h2>
似乎没有空间效率。它需要超过400GB的存储空间。有没有更好的节省空间的方法将这个大图存储在内存中?有关使用任何c ++库的建议吗?
答案 0 :(得分:6)
初步评论
您可以考虑使用向量向量而不是使用动态内存分配:
vector<vector<int>> AdjList(V);
在任何情况下,您的邻接列表中都会有V vector<int>
。每个向量都需要一些空间开销来管理其项目的大小和位置。不幸的是,通过将权重保持在不同的向量/数组中,您可以将此开销(以及添加新链接时的相关隐藏内存管理)加倍。
那么为什么不重新组合邻接列表和权重?
struct Link {
int target; // node number that was in adj list. Hope none is negative!!
int weight;
};
vector<vector<Link>> AdjList(V);
结构是否稀疏?
如果绝大多数节点都有某种链接,那就很好了。
如果相反,许多节点没有外向链接(或者如果您有大量未使用的节点ID范围),那么您可以考虑:
map<int, vector<Link>> AdjList;
map是一个关联数组。只有具有传出链接的节点的向量。顺便说一句,您可以使用您想要的任何编号方案,甚至是负数。
你甚至可以更进一步,并使用双地图。第一个映射为您提供传出节点。第二个映射将目标节点映射到权重:
map<int, map<int, int>> Oulala;
但这可能会带来更大的内存密集性。
大卷?
map
和vector
使用默认分配器动态管理内存。但是你有很多预定大小的小物件。因此,您可以考虑使用自己的allocator。这可以显着优化内存管理开销。
此外,如果使用向量,当您加载新节点的邻接列表时,立即保留向量的大小(如果您知道)可能是有效的。这可以避免几次连续重新分配矢量的增长。有数百万个节点,这可能非常昂贵。
图书馆?
搜索第三方库超出了SO的范围。但如果上述提示不充分,您可以考虑使用现有的图库,例如:
还有其他几个图形库,但很多似乎不再维护或不适用于大卷。
答案 1 :(得分:3)
您应该将图表实现为binary decision diagram data structure。
简而言之,这个想法是,图表可以通过使用图表的characteristic function表示为二元函数。
通过使用特征函数,有多种方法可以将图形编码为二元函数。在我发布的文章和视频中,有一种方法可以做到。
BDD以紧凑的方式编码二进制函数,具有快速操作。可能这是宇宙中最强大的数据结构。
BDD的概念几乎与trie中的概念相同,但在每个节点处我们不会在下一个输入的函数中进行调度,而是每个节点都具有as属性
X
,表示变量的索引,如果函数F(.. X = true ..)为真,则在节点的高分支上继续并到达叶true
,如果F(.. X = true ..)为真,在低分支上继续向下到叶节点表示真。这称为布尔函数的Shannon expansion(通过使用相同的扩展公式,也可以使用多路复用器计算布尔函数的硬件设计)。
通常,对于函数为true的每个可能的输入值X_i组合,我们有一个从根节点到true
叶子的唯一分支,在输入变量的函数中在每个节点处分支Xi
(我们在Xi的值为true或false的函数的低或高方向上分支)。可以使用相同的图表来保留多个功能(每个节点都是不同的功能)。
有两个优化可以从二元决策树转换为二元决策图,这使得这个紧凑。优化的想法与有限自动机的最小化算法的优化相同。与自动机的情况相同,最小BDD对于函数是唯一的(因此,要查看2个任意函数是否相同,它是否足以将它们转换为BDD并查看表示一个函数的节点是否与根相同另一个函数的节点(复杂度O(1)(常数时间)比较2个指针值))。
一个优化说,如果一个节点的所有边缘都在与其他节点相同的物理节点中,我们将两个节点统一在一个节点中(这可以通过保留所有已创建节点的哈希表来创建)。 / p>
其他优化说,如果变量X的节点的低边缘和高边缘进入变量Y的同一物理节点,则X节点消失,因为该函数具有相同的F值(... X =真...)= F(... X =假...)。
有数千篇关于BDD及其衍生物的文章(改变了我们得到的每个节点的调度解释,例如ZDD,用于无序集的紧凑表示)。关于主题的典型文章是C. Dong P. Molitor的What graphs can be efficiently represented by BDDs ?。
在您了解BDD的基础知识后,如果您有更长时间的演示文稿,this video非常出色,并总结了如何将图形编码为BDD。
当人们需要管理数百万个节点时,BDD就是当今专业软件的功能。
答案 2 :(得分:0)
好吧,如果图形不会被修改,请考虑使用两个向量:
struct edge { int target; int weight; };
std::vector<edge> edges;
std::vector<int> nodes; // begin-index into edges
那应该使开销最小化。
当然,如果您无法进一步缩小图表。