原始论文为:《Fast unfolding of communities in large networks》。
所以又被称为Fast unfolding算法。
Louvain算法是一种基于模块度的社区发现算法。其基本思想是网络中节点尝试遍历所有邻居的社区标签,并选择最大化模块度增量的社区标签。在最大化模块度之后,每个社区看成一个新的节点,重复直到模块度不再增大。
首先复习下模块度:

这里引入了权重方便扩展到有权图,但其实对于无权图,可以看做所有边权重为1,这时候就等于用节点的度计算,用度理解一样。
算法详述:
模块度优化阶段:每个节点将自己作为自己社区标签。每个节点遍历自己的所有邻居节点,尝试将自己的社区标签更新成邻居节点的社区标签,选择模块度增量最大(贪婪思想)的社区标签,直到所有节点都不能通过改变社区标签来增加模块度。
也就是说,首先将每个节点指定到唯一的一个社区,然后按顺序将节点在这些社区间进行移动。怎么移动呢?假设有一节点 i ,它有三个邻居节点 j1, j2, j3,我们分别尝试将节点 i 移动到 j1, j2, j3 所在的社区,并计算相应的 modularity 变化值ΔQ,哪个变化值最大就将节点 i 移动到相应的社区中去(当然,这里我们要求最大的 modularity 变化值要为正,如果变化值均为负,则节点 i 保持不动)。按照这个方法反复迭代,直到网络中任何节点的移动都不能再改善总的 modularity 值为止。
移动到一个社区C中所获得的模块化Q增益可以很容易地计算出来:
= 1 2 m ( k i , i n − Σ t o t k i m ) \frac{1}{2m}(k_{i,in}-{Σ_{tot}ki\over m}) 2m1(ki,in−mΣtotki)
其中 Σ i n Σ_{in} Σin是在社区C内的链路的权重总和(权重为1时就等于度数),如果是初始情况,即一个节点作为一个社区时,它就为这个节点自己到自己的连接,这时候仍然需要起点加weight,终点加weight(即使这个时候起点和终点为同一节点,对于无环图权为0)
Σ t o t Σ_{tot} Σtot是关联到C中的节点的链路上的权重的总和
ki是关联到节点i的链路的权重的总和
ki,in是从节点i连接到C中的节点的链路的总和
m是网络中的所有链路的权重的总和
网络凝聚阶段:每个社区合并为一个新的超级节点,超级节点的边权重为原始社区内所有节点的边权重之和,形成一个新的网络。
将第一个阶段得到的社区视为新的“节点”(一个社区对应一个),重新构造子图,两个新“节点”之间边的权值为相应两个社区之间各边的权值的总和。如这个图所示,如果第一个阶段得到的社区有以下三个,那么第二个阶段的任务就是将这三个社分别看一个新的节点,然后将任意两个新节点之间的所有连线的权重相加得到的和,作为这两个新节点之间的连线的权重。

上述两个阶段迭代进行,直到模块度不再增大。
下图很好的描述了这两个阶段。第一次迭代,经过模块度优化阶段,总的 modularity 值不再改变,算法将16个节点划分成4个社区;在网络凝聚阶段,4个社区被凝聚成4个超级节点,并重新更新了边权重。之后就进入第二次迭代中。

算法通过引入分步迭代的思想(类比EM算法),避免陷入贪婪思想导致的局部最优。
算法流程:
1、初始时将每个顶点当作一个社区,社区个数与顶点个数相同。
2、依次将每个顶点与之相邻顶点合并在一起,计算它们最大的模块度增益是否大于0,如果大于0,就将该结点放入模块度增量最大的相邻结点所在社区。
3、迭代第二步,直至算法稳定,即所有顶点所属社区不再变化。
4、将各个社区所有节点压缩成为一个结点,社区内点的权重转化为新结点环的权重,社区间权重转化为新结点边的权重。
5、重复步骤1-3,直至算法稳定。
可以看到Louvain 算法与FN算法非常相似,我觉得最大的不同时Louvain 算法在凝聚阶段是将整个社区看做一个新的超节点来看,而FN算法是以社区的观点来凝聚。
算法优缺点
优点
1、时间复杂度低,适合大规模的网络。
2、社区划分结果稳定,有具体指标能够评估社区划分好坏。
3、消除了模块化分辨率的限制。由于模块度是全局指标,最大化的时候很难发现小型的社区,很容易将小型社区合并。而算法第一次迭代是以单个节点作为社区的粒度,规避了这种问题。
4、天然自带层次化的社区划分结果,每一次迭代的社区划分结果都可以保留下来,作为社区划分的中间结果,可供选择。
缺点
1、社区过大,不能及时收敛。如果我们将模块度类比为损失函数的话,Fast Unfolding在模块度优化阶段采用的贪婪思想很容易将整个社区划分“过拟合”。因为Fast Unfolding是针对点遍历,很容易将一些外围的点加入到原本紧凑的社区中,导致一些错误的合并。这种划分有时候在局部视角是优的,但是全局视角下会变成劣的。
Python代码如下:代码与数据下载
class Louvain:
def __init__(self, G):
self._G = G
self._m = 0 # 边数量 图会凝聚动态变化
self._cid_vertices = {} # 需维护的关于社区的信息(社区编号,其中包含的结点编号的集合)
self._vid_vertex = {} # 需维护的关于结点的信息(结点编号,相应的Vertex实例)
for vid in self._G.keys():
# 刚开始每个点作为一个社区
self._cid_vertices[vid] = {vid}
# 刚开始社区编号就是节点编号
self._vid_vertex[vid] = Vertex(vid, vid, {vid})
# 计算边数 每两个点维护一条边
self._m += sum([1 for neighbor in self._G[vid].keys() if neighbor > vid])
# 模块度优化阶段
def first_stage(self):
mod_inc = False # 用于判断算法是否可终止
visit_sequence = self._G.keys()
# 随机访问
random.shuffle(list(visit_sequence))
while True:
can_stop = True # 第一阶段是否可终止
# 遍历所有节点
for v_vid in visit_sequence:
# 获得节点的社区编号
v_cid = self._vid_vertex[v_vid]._cid
# k_v节点的权重(度数) 内部与外部边权重之和
k_v = sum(self._G[v_vid].values()) + self._vid_vertex[v_vid]._kin
# 存储模块度增益大于0的社区编号
cid_Q = {}
# 遍历节点的邻居
for w_vid in self._G[v_vid].keys():
# 获得该邻居的社区编号
w_cid = self._vid_vertex[w_vid]._cid
if w_cid in cid_Q:
continue
else:
# tot是关联到社区C中的节点的链路上的权重的总和
tot = sum(
[sum(self._G[k].values()) + self._vid_vertex[k]._kin for k in self._cid_vertices[w_cid]])
if w_cid == v_cid:
tot -= k_v
# k_v_in是从节点i连接到C中的节点的链路的总和
k_v_in = sum([v for k, v in self._G[v_vid].items() if k in self._cid_vertices[w_cid]])
delta_Q = k_v_in - k_v * tot / self._m # 由于只需要知道delta_Q的正负,所以少乘了1/(2*self._m)
cid_Q[w_cid] = delta_Q
# 取得最大增益的编号
cid, max_delta_Q = sorted(cid_Q.items(), key=lambda item: item[1], reverse=True)[0]
if max_delta_Q > 0.0 and cid != v_cid:
# 让该节点的社区编号变为取得最大增益邻居节点的编号
self._vid_vertex[v_vid]._cid = cid
# 在该社区编号下添加该节点
self._cid_vertices[cid].add(v_vid)
# 以前的社区中去除该节点
self._cid_vertices[v_cid].remove(v_vid)
# 模块度还能增加 继续迭代
can_stop = False
mod_inc = True
if can_stop:
break
return mod_inc
# 网络凝聚阶段
def second_stage(self):
cid_vertices = {}
vid_vertex = {}
# 遍历社区和社区内的节点
for cid, vertices in self._cid_vertices.items():
if len(vertices) == 0:
continue
new_vertex = Vertex(cid, cid, set())
# 将该社区内的所有点看做一个点
for vid in vertices:
new_vertex._nodes.update(self._vid_vertex[vid]._nodes)
new_vertex._kin += self._vid_vertex[vid]._kin
# k,v为邻居和它们之间边的权重 计算kin社区内部总权重 这里遍历vid的每一个在社区内的邻居 因为边被两点共享后面还会计算 所以权重/2
for k, v in self._G[vid].items():
if k in vertices:
new_vertex._kin += v / 2.0
# 新的社区与节点编号
cid_vertices[cid] = {cid}
vid_vertex[cid] = new_vertex
G = collections.defaultdict(dict)
# 遍历现在不为空的社区编号 求社区之间边的权重
for cid1, vertices1 in self._cid_vertices.items():
if len(vertices1) == 0:
continue
for cid2, vertices2 in self._cid_vertices.items():
# 找到cid后另一个不为空的社区
if cid2 <= cid1 or len(vertices2) == 0:
continue
edge_weight = 0.0
# 遍历 cid1社区中的点
for vid in vertices1:
# 遍历该点在社区2的邻居已经之间边的权重(即两个社区之间边的总权重 将多条边看做一条边)
for k, v in self._G[vid].items():
if k in vertices2:
edge_weight += v
if edge_weight != 0:
G[cid1][cid2] = edge_weight
G[cid2][cid1] = edge_weight
# 更新社区和点 每个社区看做一个点
self._cid_vertices = cid_vertices
self._vid_vertex = vid_vertex
self._G = G
def get_communities(self):
communities = []
for vertices in self._cid_vertices.values():
if len(vertices) != 0:
c = set()
for vid in vertices:
c.update(self._vid_vertex[vid]._nodes)
communities.append(list(c))
return communities
def execute(self):
iter_time = 1
while True:
iter_time += 1
# 反复迭代,直到网络中任何节点的移动都不能再改善总的 modularity 值为止
mod_inc = self.first_stage()
if mod_inc:
self.second_stage()
else:
break
return self.get_communities()
参考结果如下:
社区 1 [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 16, 17, 19, 21]
社区 2 [32, 33, 8, 14, 15, 18, 20, 22, 26, 29, 30]
社区 3 [23, 24, 25, 27, 28, 31]
0.388560157790927
算法执行时间0.002000093460083008
目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非
随着ruby被引入为新的编程救世主,我想知道是否有人基于易用性、运行所需的资源、可用性和易定制性而有偏好。两者有更好的吗? 最佳答案 好吧,任何基于Rails的社交网络应用程序的比较都应该包括insoshi(http://portal.insoshi.com/)。话虽这么说,这三个都非常相似,区别在于实现细节。Lovd和Insoshi都是完整的Rails应用程序;它旨在供您将它们用作入门工具包,并使用您自己的自定义功能对其进行扩展。另一方面,CommunityEngine是一个Rails插件。这意味着您可以更轻松地向现有Rail
1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva
我一直在尝试用Ruby实现Luhn算法。我一直在执行以下步骤:该公式根据其包含的校验位验证数字,该校验位通常附加到部分帐号以生成完整帐号。此帐号必须通过以下测试:从最右边的校验位开始向左移动,每第二个数字的值加倍。将乘积的数字(例如,10=1+0=1、14=1+4=5)与原始数字的未加倍数字相加。如果总模10等于0(如果总和以零结尾),则根据Luhn公式该数字有效;否则无效。http://en.wikipedia.org/wiki/Luhn_algorithm这是我想出的:defvalidCreditCard(cardNumber)sum=0nums=cardNumber.to_s.s
下面是我写的一个计算斐波那契数列中的值的方法:deffib(n)ifn==0return0endifn==1return1endifn>=2returnfib(n-1)+(fib(n-2))endend它工作到n=14,但在那之后我收到一条消息说程序响应时间太长(我正在使用repl.it)。有人知道为什么会这样吗? 最佳答案 Naivefibonacci进行了大量的重复计算-在fib(14)fib(4)中计算了很多次。您可以将内存添加到您的算法中以使其更快:deffib(n,memo={})ifn==0||n==1returnnen
在我们的项目中,我们有一些“被遗忘的”类存在了很长一段时间。那些类已被其他类替代,但我们忘记删除它们。是否有一些自动化的方法/工具可以发现Ruby{onRails}应用程序中没有使用哪些类?谢谢! 最佳答案 这个问题已经被提出了很多次,但是最好的答案都在这里:FindunusedcodeinaRailsapp我个人喜欢日志解析:https://stackoverflow.com/a/14161807但在任何情况下,您都可以创建自己的记录器,扩展ActiveRecord::Base以创建一个观察器,该观察器将最常用的模块存储在数据库中
为了防止在迁移到生产站点期间出现数据库事务错误,我们遵循了https://github.com/LendingHome/zero_downtime_migrations中列出的建议。(具体由https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in概述),但在特别大的表上创建索引期间,即使是索引创建的“并发”方法也会锁定表并导致该表上的任何ActiveRecord创建或更新导致各自的事务失败有PG::InFailedSqlTransaction异常。下面是我们运行Rails4.2(使用Acti
我正在开发一个类似微论坛的项目,其中一个特殊用户发布一条快速(接近推文大小)的主题消息,订阅者可以用他们自己的类似大小的消息来响应。直截了当,没有任何形式的“挖掘”或投票,只是每个主题消息的响应按时间顺序排列。但预计会有很高的流量。我们想根据它们引起的响应嗡嗡声来标记主题消息,使用0到10的等级。在谷歌上搜索了一段时间的趋势算法和开源社区应用示例,到目前为止已经收集到两个有趣的引用资料,但我还没有完全理解它们:Understandingalgorithmsformeasuringtrends,关于使用基线趋势算法比较维基百科页面浏览量的讨论,在SO上。TheBritneySpearsP
我在用Ruby元编程开玩笑,我写了这段代码:classClassdef===(other)other.kind_of?(self)endendclassFakeClassdefinitialize(object)methods.each{|m|eval"undef#{m}"ifm.to_sym!=:methods}define=procdo|m|eval(这会创建一个模拟对象的假类。看:a=FakeClass.new(1)#=>1a.class#=>Fixnuma.methods#=>ReturnallFixnummethodsa+1#=>2(isnotaFakeClass)Fixnu
我收到错误:unsupportedcipheralgorithm(AES-256-GCM)(RuntimeError)但我似乎具备所有要求:ruby版本:$ruby--versionruby2.1.2p95OpenSSL会列出gcm:$opensslenc-help2>&1|grepgcm-aes-128-ecb-aes-128-gcm-aes-128-ofb-aes-192-ecb-aes-192-gcm-aes-192-ofb-aes-256-ecb-aes-256-gcm-aes-256-ofbRuby解释器:$irb2.1.2:001>require'openssl';puts