本文为加权GN算法的Java实现,具体算法原理请参考前一篇文章GN算法的简介,整个代码可从http://download.csdn.net/detail/u012483487/9447115下载,如有不对,敬请指正。
加权GN 算法求解的具体实现过程为:
(1)忽略边的权重,以无权网络计算网络中所有连接边的边介数;
(2)将边介数除以对应边的权重得到边权比;
(3)找到边权比最高的边将它移除,并计算网络的模块性 Q 函数,在计算中当边权比最高的边有多条时,同时移除这些边,并将此时移除的边和Q值进行存储;
(4)重复步骤(1),(2),直到网络中所有的边均被移除。
(5)GN算法划分结束后,取出Q值最大时的序号,在原始矩阵中依次去除截止到该次划分的边。得出最终连通矩阵,矩阵的值为权值。
现在使用JAVA对上述算法进行简单的实现,声明全局变量:
private double[][] linkMatrix;//原始数据,在算法中不断删除边
private double[][] originMatrix;//用于存储原始数据
public List<Double> Qlist;//Q的集合
public List<edgeInfo> deleteEgdes;//删除的边
public ArrayList<nodeInfo> previousNodes;//Gn算法中一次已经成功入队的节点
public ArrayList<nodeInfo> currentNodes;//Gn算法中的当前节点
public ArrayList<ArrayList<nodeInfo>> resultList;
public ArrayList<nodeInfo> record;//Gn算法中上一次访问过节点
public double[] disArray;//Gn算法中一次的各个边的距离
public double[] weightArray;//Gn算法中一次的各边的权重
public double[][] disMatrix;//用于记录源节点与其他节点的距离
public double[][] sumDisMatix;//记录边介数
private int MAX_NUM;//最大的数目
private int coperationNum;//统计社团的数目
private int[] belong;//用于区别节点所述社团
以及节点对象和边对象
public class nodeInfo {
public List<Integer> parentNodes;//父节点
public List<Integer> childrenNodes; //子节点
public int name;
//public double weightOfNode;//点权,
public nodeInfo(int n) {
parentNodes=new ArrayList<Integer>();
childrenNodes=new ArrayList<Integer>();
name=n;
//weightOfNode=1;
}
}
//边对象
private class edgeInfo
{
public int start;//开始的节点
public int end;//结束的节点
public double weightOfEdge;//边权
public void setInfo(int a,int b,double c) {
this.start=a;
this.end=b;
this.weightOfEdge=c;
}
}
初始化变量,申请内存空间。
private void init()
{
MAX_NUM=100;
linkMatrix=new double[MAX_NUM][MAX_NUM];
originMatrix=new double[MAX_NUM][MAX_NUM];
Qlist=new ArrayList<Double>();
deleteEgdes=new ArrayList<edgeInfo>();
resultList=new ArrayList<ArrayList<nodeInfo>>();
}
现在简单说明算法的实现,主要实现如下,不断对每一个在图中,不孤立的点使用GN算法。去除边介数最大的点,直到所有边都删除为止。结束后求出最大的Q值,依次按照删除边的顺序对originMartrix矩阵进行删除,得出最后的划分图。
public void calGn()
{
while (true) {
sumDisMatix=new double[MAX_NUM][MAX_NUM];
coperationNum=0;
belong=new int[MAX_NUM];
//使用GN算法计算每一个点,从而得出边介数
for(int i=0;i<MAX_NUM;i++)
if(isNodeOut(i)==false)
{
calOneNodeGn(i);
}
double max=0;
int x=0,y=0;
//删除边权比最高的边
for(int i=0;i<MAX_NUM;i++)
for(int j=0;j<MAX_NUM;j++)
{
if(linkMatrix[i][j]>0)
sumDisMatix[i][j]=sumDisMatix[i][j]/linkMatrix[i][j];
else {
sumDisMatix[i][j]=0;
}
}
for(int i=0;i<MAX_NUM;i++)
for(int j=0;j<MAX_NUM;j++)
if(max<sumDisMatix[i][j])
{
max=sumDisMatix[i][j];
x=i;
y=j;
}
if(max==0)
break;
calQ();
deleEdge(x,y);
}
//查找最大的Q值,此时为最佳划分
double maxq=0;
int index=0;
for(int i=0;i<Qlist.size();i++)
{
if(maxq<Qlist.get(i))
{
maxq=Qlist.get(i);
index=i;
}
}
System.out.print("maxQ:"+maxq+" "+"index:"+index);
}
对单独节点使用GN算法,这里先忽略掉边的权值,使用无权GN算法。首先需要对源节点使用一次广度优先遍历,得出访问过的节点列表,之后对这一列表中各个节点依次使用广度优先遍历,直到没有其他相互连接的节点为止。在广度优先遍历的过程中,建立了以源节点为根节点的树状网络。在遍历的过程中,对每一个节点建立了父节点列表和子节点列表,用于确定该节点的父节点(可能有多个)和子节点(可能有多个)。其次,对节点的社团进行标记;最后,对该树状网络进行权值的计算,计算过程请看简介,计算出边介数之后一次算法的目的达到。
public void calOneNodeGn(int n)
{
if(isNodeOut(n)==true)
return ;
disMatrix=new double[MAX_NUM][MAX_NUM];
previousNodes=new ArrayList<dataPreprocessing.nodeInfo>();
disArray=new double[MAX_NUM];
weightArray=new double[MAX_NUM];
Boolean isStop=false;
int deep1=2;
currentNodes=new ArrayList<dataPreprocessing.nodeInfo>();
singleNodeGNFirst(new nodeInfo(n), 1);
//不断往下寻找,建立以节点n为源节点的树
while(isStop==false)
{
isStop=true;
record=currentNodes;
currentNodes=new ArrayList<dataPreprocessing.nodeInfo>();
for(int i=0;i<record.size();i++)
{
boolean ldl=singleNodeGN(record.get(i),deep1);//如果还有子节点,继续循环
if(ldl==true)
isStop=false;
}
deep1++;
// System.out.print(previousNodes.size()+"\n");
}
for(int i=0;i<previousNodes.size();i++)
{
nodeInfo ne=previousNodes.get(i);
if(ne.childrenNodes.size()==0)
for(int j=0;j<ne.parentNodes.size();j++)
{
int x=ne.parentNodes.get(j);
disMatrix[i][x]=weightArray[x]/weightArray[i];
disMatrix[x][i]=disMatrix[i][x];
}
}//对叶子节点进行赋初值
double max=0;
int num=0;
for(int i=0;i<MAX_NUM;i++)
if(max<disArray[i])
{
max=disArray[i];
num=i;
}
//此时把previousNodes中与最后一个相同的节点归为同一社团,并且标记
int first=previousNodes.size()-1;
if(belong[first]==0)
{
coperationNum++;
belong[first]=coperationNum;
}
for(int i=previousNodes.size()-1;i>=0;i--)
{
int x=previousNodes.get(i).name;
if(belong[x]==0)
belong[x]=coperationNum;
}
//进行第二步,计算各边的权重
for(int i=previousNodes.size()-1;i>=0;i--)
singleSencondStep(previousNodes.get(i).name,n);
//计算边介数
for(int i=0;i<MAX_NUM;i++)
for(int j=0;j<MAX_NUM;j++)
{
sumDisMatix[i][j]+=disMatrix[i][j];
}
}
public Boolean singleNodeGNFirst(nodeInfo nn,int deep)
{
//System.out.println(deep+"\n");
boolean res=true;
int n=nn.name;
previousNodes.add(nn);
for(int i=0;i<MAX_NUM;i++)
{
if(linkMatrix[n][i]>0)
{
nodeInfo ne=new nodeInfo(i);
nodeInfo parentInfo=findNodeInfoById(n, previousNodes);
parentInfo.childrenNodes.add(i);//为父子关系
ne.parentNodes.add(n);
currentNodes.add(ne);
disArray[i]=deep;
weightArray[i]=1;
}
}
for(int j=0;j<MAX_NUM;j++)
{
if(linkMatrix[n][j]>0)
{
if(disArray[j]==0)
res=false;
}
}
return res;
}
//广度优先遍历节点
public Boolean singleNodeGN(nodeInfo nn,int deep)
{
//System.out.println(deep+"\n");
boolean res=false;
int n=nn.name;
if(findNodeInfoById(n, previousNodes)==null)
previousNodes.add(nn);
for(int i=0;i<MAX_NUM;i++)
{
if(linkMatrix[n][i]>0)
{
nodeInfo ne=new nodeInfo(i);
if(findNodeInfoById(n, record)!=null&&(findNodeInfoById(i, record)!=null))
break;
if((findNodeInfoById(i, previousNodes)==null))
{
nodeInfo parentInfo=findNodeInfoById(n, previousNodes);
if(findNodeInfoById(i, currentNodes)==null)
{
parentInfo.childrenNodes.add(i);//为父子关系
ne.parentNodes.add(n);//
currentNodes.add(ne);//如果在当前访问节点中为空并且从未被访问过
res=true;
}
else {
{
parentInfo.childrenNodes.add(i);//为父子关系
findNodeInfoById(i, currentNodes).parentNodes.add(n);
res=true;
}
}
if(disArray[i]==0)
{
disArray[i]=deep;
weightArray[i]=weightArray[parentInfo.name];
}
else if(disArray[i]==deep)
{
weightArray[i]=weightArray[n]+weightArray[i];
}
else if(disArray[i]<deep)
;
else {
;
}
}
}
}
/* for(int j=0;j<MAX_NUM;j++)
{
if(linkMatrix[n][j]>0)
{
if(disArray[j]==0)
res=false;
}
}*/
return res;
}
//计算各边的权重
public void singleSencondStep(int n,int source)
{
nodeInfo neInfo=findNodeInfoById(n, previousNodes);
for(int j=0;j<neInfo.parentNodes.size();j++)
{
nodeInfo pa=findNodeInfoById(neInfo.parentNodes.get(j), previousNodes);
double q=1;
for(int k=0;k<neInfo.childrenNodes.size();k++)
q+=disMatrix[n][neInfo.childrenNodes.get(k)];
int x=neInfo.parentNodes.get(j);
if(pa.name!=source)
{
if(weightArray[n]!=0)
{
disMatrix[n][x]=weightArray[x]*q/weightArray[n];
disMatrix[x][n]=disMatrix[n][x];
}
}
else {
disMatrix[n][x]=q;
disMatrix[x][n]=disMatrix[n][x];
}
}
}
对边的删除,主要是对linkMatrix矩阵进行删除操作,并将删除的边记录在列表中,也把当时计算的Q值记录,便于查找。对Q值的计算公式详见简介
public void deleEdge(int start,int end)
{
edgeInfo deInfo=new edgeInfo();
deInfo.setInfo(start, end, linkMatrix[start][end]);
deleteEgdes.add(deInfo);
linkMatrix[start][end]=0;
linkMatrix[end][start]=0;
}
<span style="font-family: Arial, Helvetica, sans-serif;">public double calQ()</span><span style="font-family: Arial, Helvetica, sans-serif;">{</span>
double Q=0;
double M=0;
double sum=0;
double []k=new double[MAX_NUM];
for(int i=0;i<MAX_NUM;i++)
for(int j=0;j<MAX_NUM;j++)
{
M+=linkMatrix[i][j];
}
M=M/2;
for(int i=0;i<MAX_NUM;i++)
for(int j=0;j<MAX_NUM;j++)
if(linkMatrix[i][j]>0)
{
k[i]+=linkMatrix[i][j];
}
for(int i=0;i<MAX_NUM;i++)
for(int j=0;j<MAX_NUM;j++)
{
if(belong[i]==belong[j])
sum+=(linkMatrix[i][j]-k[i]*k[j]/(2*M));
}
Q=sum/(2*M);
Qlist.add(Q);
// System.out.print(Q+"\n");
return Q;
}
计算出结果后,可以选择将结果进行打印,调用函数printResult即可,此时打印的矩阵为GN分裂后的社团结构,以二维数组的形式呈现,打印横纵坐标和权值。
public void printResult()
{
double max=0;
int index=0;
for(int i=0;i<Qlist.size();i++)
{
if(max<Qlist.get(i))
{
max=Qlist.get(i);
index=i;
}
}
for(int i=0;i<index;i++)
{
edgeInfo eInfo=deleteEgdes.get(i);
int ks=eInfo.start;
int ke=eInfo.end;
originMatrix[ks][ke]=0;
originMatrix[ke][ks]=0;
}
File file = new File("d:/temp", "res.txt");
try {
file.createNewFile(); // 创建文件
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
FileOutputStream in = new FileOutputStream(file);
for(int i=0;i<MAX_NUM;i++)
for(int j=0;j<MAX_NUM;j++)
{
String str =""+i+" "+j+" "+originMatrix[i][j]+"\n";
byte bt[] = new byte[1024];
bt = str.getBytes();
try {
in.write(bt, 0, bt.length);
//in.close();
// boolean success=true;
// System.out.println("写入文件成功");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}