一個網絡(有向帶權圖)中節點u的PageRank的計算公式:
PR(u)表示節點u的PageRank值,d為衰減因子(damping factor)或阻尼系數,一般取d=0.85,N為網絡中的節點總數,nb(u)表示節點u的所有鄰居節點的集合,d(v)表示節點v的出度(如果是無向圖,就是度),w(u,v)表示節點v的邊<u,v>所占的權重(如果對於無權圖或者認為每條邊的權重都一樣,那么w(u,v)=1),PR(v)表示節點v的PageRank值。
由此可以看出要算出節點u的PR值需要先知道它的每個鄰居節點的PR值,似乎是個遞歸的過程。其實初始狀態下,可以給每個節點的PR值都賦值為一個任意正數,例如1,然后通過上述公式不斷迭代計算更新每個節點的PR值,數學證明,最終每個節點的PR值都會收斂到一個穩定的PR值(初值PR不同,最終的PR值也不同,但最后各節點之間PR的大小排名不因初值而改變)。編程時如何確定某個節點u的PR值已經收斂?如果這次的PR值與上次的PR值相差很小的時候就可以認為收斂了。很小是多小?越小越好,但不要太小,免得迭代次數太多浪費時間,可取10的-4或-5次方。
PageRank的數學原理的詳細說明,可參考:
PageRank on undirected and weighted graph
《集體智慧編程》上的例子:
Java實現代碼:
Program.java:
1 package dd.lt; 2 3 import dd.lt.entity.Node; 4 5 public class Program 6 { 7 //計算每個節點的PageRank值 8 public static void CalcPageRank(ArrayList<Node> graph) 9 { 10 double distance = 0.00001; 11 double d = 0.85;// damping factor 12 double common = (1 - d) / graph.size(); 13 while (true) 14 { 15 for (Node n : graph) 16 { 17 double sum = 0.0; 18 for (int nodeId : n.getNeighbors()) 19 { 20 Node nb = getNodeById(nodeId,graph); 21 sum += nb.getPR() / nb.getDegree(); 22 } 23 double newPR = common + d * sum; 24 //如果尚未收斂,賦新值,否則結束迭代 25 if (Math.abs(n.getPR() - newPR) > distance) 26 n.setPR(newPR); 27 else 28 return; 29 } 30 } 31 } 32 33 public static Node getNodeById(int nodeId,ArrayList<Node> graph) 34 { 35 for(Node n:graph) 36 { 37 if (n.nodeId==nodeId) 38 return n; 39 } 40 return null; 41 } 42 43 44 public static ArrayList<Node> buildGraph() 45 { 46 ArrayList<Node> graph = new ArrayList<Node>();//圖以節點集合形式來表示 47 //加載數據,組裝好圖結構 48 .... 49 return graph; 50 } 51 52 public static void main(String[] args) 53 { 54 ArrayList<Node> graph = buildGraph(); 55 CalcPageRank(graph); 56 for(Node n:graph) 57 { 58 System.out.println("PageRank of %d is %.2f",n.nodeId,n.getPR()); 59 } 60 } 61 }
Node.java:
1 package dd.lt.entity; 2 3 import java.util.ArrayList; 4 5 public class Node implements Comparable<Node> 6 { 7 public int nodeId; 8 private ArrayList<Integer> neighbors = new ArrayList<Integer>();//以鄰接表的形式表示圖結構【無向圖】 9 private double pr=1; 10 public Node(int nodeId) 11 { 12 this.nodeId = nodeId; 13 } 14 15 public int getDegree() 16 { 17 return this.neighbors.size(); 18 } 19 20 public ArrayList<Integer> getNeighbors() 21 { 22 return this.neighbors; 23 } 24 public void setNeighbors(ArrayList<Integer> neighbors) 25 { 26 this.neighbors=neighbors; 27 } 28 29 public double getPR() 30 { 31 return pr; 32 } 33 public void setPR(double val) 34 { 35 this.pr=val; 36 } 37 38 // 按PageRank值排序 39 public int compareTo(Node anotherNode) 40 { 41 if (this.neighbors != null && anotherNode.neighbors != null) 42 { 43 // 降序排列 44 if (anotherNode.getPR() >this.getPR()) 45 return 1; 46 else if (anotherNode.getPR() <this.getPR()) 47 return -1; 48 else 49 return 0; 50 // 升序排列 51 // return this.getPR()-anotherNode.getPR(); 52 } 53 return 0; 54 } 55 }