📄 graphalgorithms.java
字号:
/* * Copyright (c) 2003-2005 The BISON Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ package peersim.graph;import java.util.*;/*** Implements graph algorithms. The current implementation is NOT thread* safe. Some algorithms are not static, many times the result of an* algorithm can be read from non-static fields.*/public class GraphAlgorithms {// =================== public fields ==================================// ====================================================================/** output of some algorithms is passed here */public int[] root = null;private Stack<Integer> stack = new Stack<Integer>();private int counter=0;private Graph g=null;public final static int WHITE=0;public final static int GREY=1;public final static int BLACK=2;/** output of some algorithms is passed here */public int[] color = null;/** output of some algorithms is passed here */public Set<Integer> cluster = null;/** output of some algorithms is passed here */public int[] d = null;// =================== private methods ================================// ====================================================================/*** Collects nodes accessible from node "from" using depth-first search.* Works on the array {@link #color} which must be of the same length as* the size of the graph, and must contain values according to the* following semantics: * WHITE (0): not seen yet, GREY (1): currently worked upon. BLACK* (other than 0 or 1): finished.* If a negative color is met, it is saved in the {@link #cluster} set* and is treated as black. This can be used to check if the currently* visited cluster is weakly connected to another cluster.* On exit no nodes are GREY.* The result is the modified array {@link #color} and the modified set* {@link #cluster}.*/private void dfs( int from ) { color[from]=GREY; for(int j:g.getNeighbours(from)) { if( color[j]==WHITE ) { dfs(j); } else { if( color[j]<0 ) cluster.add(color[j]); } } color[from]=BLACK;}// --------------------------------------------------------------------/*** Collects nodes accessible from node "from" using breadth-first search.* Its parameters and side-effects are identical to those of dfs.* In addition, it stores the shortest distances from "from" in {@link #d},* if it is not null. On return, <code>d[i]</code> contains the length of* the shortest path from "from" to "i", if such a path exists, or it is* unchanged (ie the original value of <code>d[i]</code> is kept,* whatever that was.* <code>d</code> must either be long enough or null.*/private void bfs( int from ) { List<Integer> q = new LinkedList<Integer>(); int u, du; q.add(from); q.add(0); if( d != null ) d[from] = 0; color[from]=GREY; while( ! q.isEmpty() ) { u = q.remove(0).intValue(); du = q.remove(0).intValue(); for(int j:g.getNeighbours(u)) { if( color[j]==WHITE ) { color[j]=GREY; q.add(j); q.add(du+1); if( d != null ) d[j] = du+1; } else { if( color[j]<0 ) cluster.add(color[j]); } } color[u]=BLACK; }}// --------------------------------------------------------------------/** The recursive part of the Tarjan algorithm. */private void tarjanVisit(int i) { color[i]=counter++; root[i]=i; stack.push(i); for(int j:g.getNeighbours(i)) { if( color[j]==WHITE ) { tarjanVisit(j); } if( color[j]>0 && color[root[j]]<color[root[i]] ) // inComponent is false and have to update root { root[i]=root[j]; } } int j; if(root[i]==i) //this node is the root of its cluster { do { j=stack.pop(); color[j]=-color[j]; root[j]=i; } while(j!=i); }}// =================== public methods ================================// ====================================================================/** Returns the weakly connected cluster indexes with size as a value.* Cluster membership can be seen from the content of the array {@link #color};* each node has the cluster index as color. The cluster indexes carry no* information; we guarantee only that different clusters have different indexes.*/public Map weaklyConnectedClusters( Graph g ) { this.g=g; if( cluster == null ) cluster = new HashSet<Integer>(); if( color==null || color.length<g.size() ) color = new int[g.size()]; // cluster numbers are negative integers int i, j, actCluster=0; for(i=0; i<g.size(); ++i) color[i]=WHITE; for(i=0; i<g.size(); ++i) { if( color[i]==WHITE ) { cluster.clear(); bfs(i); // dfs is recursive, for large graphs not ok --actCluster; for(j=0; j<g.size(); ++j) { if( color[j] == BLACK || cluster.contains(color[j]) ) color[j] = actCluster; } } } Hashtable<Integer,Integer> ht = new Hashtable<Integer,Integer>(); for(j=0; j<g.size(); ++j) { Integer num = ht.get(color[j]); if( num == null ) ht.put(color[j],Integer.valueOf(1)); else ht.put(color[j],num+1); } return ht;}// --------------------------------------------------------------------/*** In <code>{@link #d}[j]</code> returns the length of the shortest path between* i and j. The value -1 indicates that j is not accessible from i.*/public void dist( Graph g, int i ) { this.g=g; if( d==null || d.length<g.size() ) d = new int[g.size()]; if( color==null || color.length<g.size() ) color = new int[g.size()]; for(int j=0; j<g.size(); ++j) { color[j]=WHITE; d[j] = -1; } bfs(i);}// --------------------------------------------------------------------/*** Calculates the clustering coefficient for the given node in the given* graph. The clustering coefficient is the number of edges between* the neighbours of i divided by the number of possible edges.* If the graph is directed, an exception is thrown.* If the number of neighbours is 1, returns 1. For zero neighbours* returns NAN.* @throws IllegalArgumentException if g is directed*/public static double clustering( Graph g, int i ) { if( g.directed() ) throw new IllegalArgumentException( "graph is directed"); Object[] n = g.getNeighbours(i).toArray(); if( n.length==1 ) return 1.0; int edges = 0; for(int j=0; j<n.length; ++j) for(int k=j+1; k<n.length; ++k) if( g.isEdge((Integer)n[j],(Integer)n[k]) ) ++edges; return ((edges*2.0)/n.length)/(n.length-1);}// --------------------------------------------------------------------/*** Performs anti-entropy epidemic multicasting from node 0.* As a result the number of nodes that have been reached in cycle i* is put into <code>b[i]</code>.* The number of cycles performed is determined by <code>b.length</code>.* In each cycle each node contacts a random neighbour and exchanges* information. The simulation is generational: when a node contacts a neighbor* in cycle i, it sees their state as in cycle i-1, besides, all nodes update* their state at the same time point, synchronously.*/public static void multicast( Graph g, int[] b, Random r ) { int c1[] = new int[g.size()]; int c2[] = new int[g.size()]; for(int i=0; i<c1.length; ++i) c2[i]=c1[i]=WHITE; c2[0]=c1[0]=BLACK; Collection<Integer> neighbours=null; int black=1; int k=0; for(; k<b.length || black<g.size(); ++k) { for(int i=0; i<c2.length; ++i) { neighbours=g.getNeighbours(i); Iterator<Integer> it=neighbours.iterator(); for(int j=r.nextInt(neighbours.size()); j>0; --j) it.next(); int randn = it.next(); // push pull exchane with random neighbour if( c1[i]==BLACK ) //c2[i] is black too { if(c2[randn]==WHITE) ++black; c2[randn]=BLACK; } else if( c1[randn]==BLACK ) { if(c2[i]==WHITE) ++black; c2[i]=BLACK; } } System.arraycopy(c2,0,c1,0,c1.length); b[k]=black; } for(; k<b.length; ++k) b[k]=g.size();}// --------------------------------------------------------------------/*** Performs flooding from given node.* As a result <code>b[i]</code> contains the number of nodes* reached in exactly i steps, and always <code>b[0]=1</code>.* If the maximal distance from k is lower than <code>b.length</code>,* then the remaining elements of b are zero.*/public void flooding( Graph g, int[] b, int k ) { dist(g, k); for(int i=0; i<b.length; ++i) b[i]=0; for(int i=0; i<d.length; ++i) { if( d[i] >= 0 && d[i] < b.length ) b[d[i]]++; }}// --------------------------------------------------------------------/** Returns the strongly connected cluster roots with size as a value.* Cluster membership can be seen from the content of the array {@link #root};* each node has the root of the strongly connected cluster it belongs to.* A word of caution: for large graphs that have a large diameter and that* are strongly connected (such as large rings) you can get stack overflow* because of the large depth of recursion.*///XXX implement a non-recursive version ASAP!!!public Map tarjan( Graph g ) { this.g=g; stack.clear(); if( root==null || root.length<g.size() ) root = new int[g.size()]; if( color==null || color.length<g.size() ) color = new int[g.size()]; for( int i=0; i<g.size(); ++i) color[i]=WHITE; counter = 1; // color is WHITE (0): not visited // not WHITE, positive (c>1): visited as the c-th node // color is negative (c<1): inComponent true for(int i=0; i<g.size(); ++i) { if( color[i]==WHITE ) tarjanVisit(i); } for( int i=0; i<g.size(); ++i) color[i]=0; for( int i=0; i<g.size(); ++i) color[root[i]]++; Hashtable<Integer,Integer> ht = new Hashtable<Integer,Integer>(); for(int j=0; j<g.size(); ++j) { if(color[j]>0) { ht.put(j,color[j]); } } return ht;}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -