加权GN算法的Java实现

本文为加权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();
		}
	}



  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值