基於最短路徑演算法的社群發現演算法-Gewman and Girvan演算法)
阿新 • • 發佈:2018-11-30
基於最短路徑演算法的社群發現演算法-Gewman and Girvan演算法)
重要概念
邊介數(betweenness):網路中任意兩個節點通過此邊的最短路徑的數目。
GN演算法的思想:在一個網路之中,通過社群內部的邊的最短路徑相對較少,而通過社群之間的邊的最短路徑的數目則相對較多。下圖中展示了變得強度以及邊介數在現實網路中的分佈情況。GN演算法是一個基於刪除邊的演算法,本質是基於聚類中的分裂思想,在原理上是使用邊介數作為相似度的度量方法。在GN演算法中,每次都會選擇邊介數高的邊刪除,進而網路分裂速度遠快於隨機刪除邊時的網路分裂。
GN演算法的步驟如下:
(1)計算每一條邊的邊介數;
(2)刪除邊界數最大的邊;
(3)重新計算網路中剩下的邊的邊階數;
(4)重複(3)和(4)步驟,直到網路中的任一頂點作為一個社群為止。
GN演算法示例:
實現想法:
(1)用最短路徑演算法求出任意兩點間的最短路徑
(2)借用最短路徑算出每條邊的邊介數,也就是建立一個節點數*節點數的二維矩陣,然後只要有最短路徑通過某條邊,此邊的二維矩陣中的邊介數加1
(3)遍歷整個邊介數矩陣,找出最大的邊介數的邊的值
(4)將所有等於此邊介數的邊的值賦值為無窮大(也就是斷開這條邊)
(5)找出所有的社團,並計算模組係數Q
(6)如果模組係數Q大於某個q值(一般在0.3到0.7之間),結束演算法,輸出所有的社團,否則從(1)再次執行
程式碼:
最短路徑演算法人盡皆知,不再展示,算出的最短路徑儲存在一個二維的AarryList或者二維的Stack中均可。
//求出各邊的邊介數,Stack中儲存的是最短路徑
while (stack.size() > 1) {
int q = stack.pop();
int b = stack.peek();
edge[q][b]++;
edge[b][q]++;
}
stack.pop ();
//找出最大邊介數
for (int q = 0; q < edge.length; q++) {
for (int b = 0; b < edge.length; b++) {
if (temp < edge[q][b]) temp = edge[q][b];
}
}
//將邊介數最大的邊的權值設為無窮大
for (int q = 0; q < edge.length; q++) {
for (int b = 0; b < edge.length; b++) {
if (temp == edge[q][b]) {
graph.edges[q][b] = INF;
graph.edges[q][b] = INF;
}
}
}//endfor
找出社團比較麻煩,用廣度優先搜尋或者深度優先搜尋找均可
//深度優先遍歷
public static void DFS(int[][] graph, boolean[] visitied, int start, ArrayList<Integer> arrayList){
for(int j = 0; j<graph.length; j++){
if(graph[start][j] == 1 && !visitied[j]){
visitied[j] = true;
arrayList.add(j);
DFS(graph, visitied, j, arrayList);
}
if(j == graph.length-1) return;
}
}
//廣度優先演算法遍歷整個圖
public class BFSfisrt {
private Queue q;
private Queue<Integer> visited;
public BFSfisrt() {
// TODO Auto-generated constructor stub
q=new LinkedList();
visited=new LinkedList<>();
}
private int getIndex(char v,char[] str)
{
for(int i=0;i<str.length;i++)
{
if(v==str[i])
return i;
}
return -1;
}
public Stack<Integer> bfs(int[][] matrix, char v, char[] str, int vd[])
{
Stack<Integer> stack = new Stack<>();
int i=getIndex(v,str);
if(i==-1){
System.out.println("error");
return stack;
}
q.add(i);
visited.add(i);
System.out.print(str[i]+" ");
stack.push(i);
while(!q.isEmpty())
{
int u=(int)q.remove();
for(int j=0;j<str.length;j++)
{
if(matrix[u][j]==1 && (!visited.contains(j)))
{
q.add(j);
visited.add(j);
stack.push(j);
}
}
}
return stack;
}
}
再找出強連通分量,也就是分組
//計算強連通分量
public ArrayList<ArrayList<Integer>> SCCCount(){
group = new ArrayList<>();
boolean[] visited = new boolean[graph.length]; //判斷結點是否遍歷過
for(int i = 0; i<visited.length; i++) visited[i] = false;
//進行深度優先遍歷
for(int i = 0; i< graph.length; i++){
if(!visited[i]) {
ArrayList<Integer> arrayList = new ArrayList<>();
group.add(arrayList);
DFS(graph, visited, i, arrayList);
}
}
for(int i = 0; i<visited.length; i++) visited[i] = false;
for(ArrayList<Integer> a: group){
for(int i: a){
visited[i] = true;
}
}
//所有剩下沒加入的點都是單個結點,將其作為一個獨立的組加入
for(int i = 0; i<visited.length; i++){
if(visited[i] == false){
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(i);
group.add(arrayList);
}
}
return group;
}
最後計算模組係數Q
public double modelRateQ(double edgeCount, int[][] graph){
group = SCCCount();
double groupEdge = 0; //組內邊介數
double rateQ = 0; //模組係數Q
for(ArrayList<Integer> al: group){
double nodeRate = 0;
for(int node: al){
//只有一個點的組的組內邊的和為0
if(al.size() == 1){
for(int i = 0; i<graph.length; i++){
//計算所有與node點相連線的邊的數量
if(graph[node][i] > 0) nodeRate++;
}
continue;
}
for(int i = 0; i<graph.length; i++){
//計算所有與node點相連線的邊的數量
if(graph[node][i] > 0) nodeRate++;
//計算組內邊數
if(graph[node][i] > 0 && al.indexOf(i) != -1)
groupEdge++;
}//endfor
}//endfor
rateQ -= (nodeRate/(edgeCount * 2)) * (nodeRate/(edgeCount * 2));
}
groupEdge /= 2;
rateQ += groupEdge/edgeCount;
System.out.println(rateQ);
return rateQ;
}