模块度:
Louvain算法是一种基于图数据的社区发现算法。原始论文为:《Fast unfolding of communities in large networks》。
算法的优化目标为最大化整个数据的模块度,模块度的计算如下:
其中m为图中边的总数量,k_i表示所有指向节点i的连边权重之和,k_j同理。A_{i,j} 表示节点i,j之间的连边权重。
有一点要搞清楚,模块度的概念不是Louvain算法发明的,而Louvain算法只是一种优化关系图模块度目标的一种实现而已。
Louvain算法的两步迭代设计:
最开始,每个原始节点都看成一个独立的社区,社区内的连边权重为0.
- 算法扫描数据中的所有节点,针对每个节点遍历该节点的所有邻居节点,衡量把该节点加入其邻居节点所在的社区所带来的模块度的收益。并选择对应最大收益的邻居节点,加入其所在的社区。这一过程化重复进行指导每一个节点的社区归属都不在发生变化。
- 对步骤1中形成的社区进行折叠,把每个社区折叠成一个单点,分别计算这些新生成的“社区点”之间的连边权重,以及社区内的所有点之间的连边权重之和。用于下一轮的步骤1。
该算法的最大优势就是速度很快,步骤1的每次迭代的时间复杂度为O(N),N为输入数据中的边的数量。步骤2 的时间复杂度为O(M + N), M为本轮迭代中点的个数。
算法实现:
数据结构设计:
算法数据结构的设计主要有两方面的考虑:1. 如何高效地存储图中的节点和节点之间的关系2. 如何在设计的数据结构上高效地扫描数据、进行算法迭代。当前一些开源的算法实现主要通过hash表或set的结构来存储节点和节点之间的关系。主要有两个缺点:1. 维护hash 或 集合结构本身就需要不少内存开销2. 遍历过程中需要不断地创建、销毁、清空对应的Hash 或 Set 结构,尤其是在遍历不同的节点的邻居节点以及社区这点时。而且,在遍历过程中,结构对元素的访问也并不是严格O(1)的。
出于以上考虑,我们设计一种更高效的数据结构来存储图中的节点和边&