Louvain 算法原理 及设计实现
模塊度:
Louvain算法是一種基于圖數(shù)據(jù)的社區(qū)發(fā)現(xiàn)算法。原始論文為:《Fast unfolding of communities in large networks》。
算法的優(yōu)化目標(biāo)為最大化整個(gè)數(shù)據(jù)的模塊度,模塊度的計(jì)算如下:
其中m為圖中邊的總數(shù)量,k_i表示所有指向節(jié)點(diǎn)i的連邊權(quán)重之和,k_j同理。A_{i,j} 表示節(jié)點(diǎn)i,j之間的連邊權(quán)重。
有一點(diǎn)要搞清楚,模塊度的概念不是Louvain算法發(fā)明的,而Louvain算法只是一種優(yōu)化關(guān)系圖模塊度目標(biāo)的一種實(shí)現(xiàn)而已。
Louvain算法的兩步迭代設(shè)計(jì):
最開始,每個(gè)原始節(jié)點(diǎn)都看成一個(gè)獨(dú)立的社區(qū),社區(qū)內(nèi)的連邊權(quán)重為0.
算法掃描數(shù)據(jù)中的所有節(jié)點(diǎn),針對(duì)每個(gè)節(jié)點(diǎn)遍歷該節(jié)點(diǎn)的所有鄰居節(jié)點(diǎn),衡量把該節(jié)點(diǎn)加入其鄰居節(jié)點(diǎn)所在的社區(qū)所帶來的模塊度的收益。并選擇對(duì)應(yīng)最大收益的鄰居節(jié)點(diǎn),加入其所在的社區(qū)。這一過程化重復(fù)進(jìn)行指導(dǎo)每一個(gè)節(jié)點(diǎn)的社區(qū)歸屬都不在發(fā)生變化。
對(duì)步驟1中形成的社區(qū)進(jìn)行折疊,把每個(gè)社區(qū)折疊成一個(gè)單點(diǎn),分別計(jì)算這些新生成的“社區(qū)點(diǎn)”之間的連邊權(quán)重,以及社區(qū)內(nèi)的所有點(diǎn)之間的連邊權(quán)重之和。用于下一輪的步驟1。
該算法的最大優(yōu)勢(shì)就是速度很快,步驟1的每次迭代的時(shí)間復(fù)雜度為O(N),N為輸入數(shù)據(jù)中的邊的數(shù)量。步驟2 的時(shí)間復(fù)雜度為O(M + N), M為本輪迭代中點(diǎn)的個(gè)數(shù)。
算法實(shí)現(xiàn):
數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì):
算法數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)主要有兩方面的考慮:
1.??如何高效地存儲(chǔ)圖中的節(jié)點(diǎn)和節(jié)點(diǎn)之間的關(guān)系
2. 如何在設(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu)上高效地掃描數(shù)據(jù)、進(jìn)行算法迭代。
當(dāng)前一些開源的算法實(shí)現(xiàn)主要通過hash表或set的結(jié)構(gòu)來存儲(chǔ)節(jié)點(diǎn)和節(jié)點(diǎn)之間的關(guān)系。
主要有兩個(gè)缺點(diǎn):
1.??維護(hù)hash 或 集合結(jié)構(gòu)本身就需要不少內(nèi)存開銷
2. 遍歷過程中需要不斷地創(chuàng)建、銷毀、清空對(duì)應(yīng)的Hash 或 Set 結(jié)構(gòu),尤其是在遍歷不同的節(jié)點(diǎn)的鄰居節(jié)點(diǎn)以及社區(qū)這點(diǎn)時(shí)。
而且,在遍歷過程中,結(jié)構(gòu)對(duì)元素的訪問也并不是嚴(yán)格O(1)的。
出于以上考慮,我們?cè)O(shè)計(jì)一種更高效的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)圖中的節(jié)點(diǎn)和邊,避開使用復(fù)雜的數(shù)據(jù)結(jié)構(gòu),且在算法迭代過程中不申請(qǐng)多余的空間和空間的銷毀操作,具體如下:
關(guān)于節(jié)點(diǎn)字段的說明:
count,???社區(qū)內(nèi)的節(jié)點(diǎn)個(gè)數(shù)
clsid,?????節(jié)點(diǎn)歸屬社區(qū)的代表節(jié)點(diǎn)ID
next,??????步驟1迭代中下一個(gè)屬于同一個(gè)臨時(shí)社區(qū)的節(jié)點(diǎn)
prev,??????步驟1迭代中上一個(gè)屬于同一個(gè)臨時(shí)社區(qū)的節(jié)點(diǎn)
first,???????屬于同一個(gè)社區(qū)的,除代表節(jié)點(diǎn)外的第一個(gè)節(jié)點(diǎn),該節(jié)點(diǎn)有步驟2 社區(qū)折疊的時(shí)候生成
kin,?????????穩(wěn)定社區(qū)內(nèi)部節(jié)點(diǎn)之間的互相連接權(quán)重之和
kout,???????穩(wěn)定社區(qū)外部,指向自己社區(qū)的權(quán)重之和
clskin,?????臨時(shí)社區(qū)內(nèi)部節(jié)點(diǎn)之間的互相連接權(quán)重之和
clstot,??????穩(wěn)定社區(qū)所有內(nèi)外部指向自己的連接權(quán)重之和
eindex,????節(jié)點(diǎn)鄰居鏈表的第一個(gè)指針,該鏈表下的所有l(wèi)eft,都是本節(jié)點(diǎn)自己
關(guān)于邊數(shù)據(jù)結(jié)構(gòu)的字段就顧名思義即可。
基于上述結(jié)構(gòu)設(shè)計(jì),在給定了一個(gè)M個(gè)節(jié)點(diǎn),N調(diào)邊的圖所需的空間為:60 * M + 24 * N.
例如:給定1000萬給點(diǎn),2000萬邊的數(shù)據(jù),則需要空間約為:10000000 * 60 + 20000000 * 24 = 1080M.且整個(gè)迭代過程中內(nèi)存環(huán)境維持不變。
迭代過程:
1, 假設(shè)我們最開始有5個(gè)點(diǎn),互相之間存在一定的關(guān)系(至于什么關(guān)系,先不管),如下:
2, 假設(shè)在進(jìn)過了步驟1的充分迭代之后發(fā)現(xiàn)節(jié)點(diǎn)2,應(yīng)該加入到節(jié)點(diǎn)1所在的社區(qū)(最開始每個(gè)點(diǎn)都是一個(gè)社區(qū),而自己就是這個(gè)社區(qū)的代表),新的社區(qū)由節(jié)點(diǎn)1代表,如下:
此時(shí)節(jié)點(diǎn)3,4,5之間以及與節(jié)點(diǎn)1,2之間沒有任何歸屬關(guān)系。
3, 此時(shí)應(yīng)該執(zhí)行步驟2,將節(jié)點(diǎn)1,2組合成的新社區(qū)進(jìn)行折疊,折疊之后的社區(qū)看成一個(gè)單點(diǎn),用節(jié)點(diǎn)1來代表,如下:
此時(shí)數(shù)據(jù)中共有4個(gè)節(jié)點(diǎn)(或者說4個(gè)社區(qū)),其中一個(gè)社區(qū)包含了兩個(gè)節(jié)點(diǎn),而社區(qū)3,4,5都只包含一個(gè)節(jié)點(diǎn),即他們自己。
4, 重新執(zhí)行步驟1,對(duì)社區(qū)1,3,4,5進(jìn)行掃描,假設(shè)在充分迭代之后節(jié)點(diǎn)5,4,3分別先后都加入了節(jié)點(diǎn)1所在的社區(qū),如下:
5, 進(jìn)行步驟2,對(duì)新生成的社區(qū)進(jìn)行折疊,新折疊而成的社區(qū)看成一個(gè)單點(diǎn),由節(jié)點(diǎn)1代表,結(jié)構(gòu)如下:
此時(shí)由于整個(gè)數(shù)據(jù)中只剩下1個(gè)社區(qū),即由節(jié)點(diǎn)1代表的社區(qū)。
再進(jìn)行步驟1時(shí)不會(huì)有任何一個(gè)節(jié)點(diǎn)的社區(qū)歸屬發(fā)生變化,此時(shí)也就不需要再執(zhí)行步驟2,至此, 迭代結(jié)束。
代碼實(shí)現(xiàn)及測(cè)試:
一個(gè)基于上述結(jié)構(gòu)設(shè)計(jì)的代碼實(shí)現(xiàn)參見:
https://github.com/liuzhiqiangruc/dml/blob/master/cls/louvain.c
在一個(gè)實(shí)際的圖(70萬點(diǎn),200萬邊)上進(jìn)行測(cè)試,迭代到完全收斂所需時(shí)間為:1.77秒。
實(shí)際中往往不需要迭代到每一個(gè)點(diǎn)都不發(fā)生變化,或者整個(gè)圖中有多少比例的節(jié)點(diǎn)不在發(fā)生變化就退出。
總結(jié)
以上是生活随笔為你收集整理的Louvain 算法原理 及设计实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kubernetes 集群部署
- 下一篇: 长春理工大学第十四届程序设计竞赛(重现赛