数据挖掘算法之 PageRank

来源:互联网 发布:今年网络教育报名时间 编辑:程序博客网 时间:2024/05/02 01:56

一、 什么是PageRank

PageRank 是Sergy Brin 和 Larry page 在1998年4月上第一次提出的,PageRank 利用网页之间的链接形成一个强有力的排名算法。PageRank 算法生成的web网页排序是静态的,这是指每个网页的排序值是通过离线计算得到的,并且该值和查询无关(基于web上的现有的链接而不考虑用户的查询)

二、PageRank 算法思想

把Web抽象成为一个有向图 G=(V,E) ,其中V是图中节点(网页)的集合,E是图的有向边(超链接)的集合,Web 上的总网页数目为n, 网页i的pagerank 值P(i)是:

        

其中 Oj 是网页j中出链的数量。用矩阵P表示PageRank 值的n维向量,用矩阵A表示有向图的邻接矩阵,并按照如下规则为每条有向边赋值:

            

基于这两个矩阵,我们可以得到一个n维的方程组


由线性代数的知识可以知道:Pagerank 的向量P就是对应矩阵A的最大特征值1的主特征向量,我们可以用幂迭代的方法求P。

如果A是一个随机矩阵而且是不可约和非周期的.

随机性: 矩阵的每个元素都是非负的实数而且每行的和为1;

可约行:矩阵强连通时可约,强连通指针对图中的每一个节点对(u,v)存在从u到v的一条路径。

周期性:状态i的是周期的并且具有周期k>1,存在一个最小的正整数k,使得所有的从状态I出发又回到状态i的路径长度都是k的整数倍。如果一个马尔科夫链的所有状态都是非周期的那么这个马尔科夫链是非周期的。

我们可以将整个Web图用马尔科夫链进行建模,每个结点看成是马尔科夫链的一个状态,有向边表示状态的转移,在迭代之前我们需要对悬挂网页(没有任何出边的网页)进行处理,例如如下的一个超链接图,网页5只有入边没有出边,即为悬挂网页:

                                                                               (图1)

针对图1 的DAG,我们可以得到状态转移矩阵A:

              

解决随机性: 我们可以看到第五行全是0,此时A不是随机矩阵,不可以用马尔科夫链的结论。为了解决这个问题我们从悬挂网页向其他的每个网页引出一条连接,即转换概率都为1/n, 此时的矩阵变为A1.

解决可约性和周期性:从任一个页面出发,到每个页面都加上一个链接,并且分配给这个链接一个由参数d控制的微小的转换概率,经过修正,状态转移矩阵A就是不可约和非周期的。改进的PageRank模型为:(E是一个n*n的元素全为1的向量,可以看为是,e是一个元素全为1的列向量)



此时任何一个网页i的PageRank 公式变为:其中d成为阻尼因子,在0-1之间,一般设置为d =0.85.



算法流程



三、代码

3.1 PageRank:

package dataMining;import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.HashSet;import java.util.List;import java.util.Set;/** * @author 作者 : xcy * @version 创建时间:2016年11月15日 下午9:43:18 *          类说明 */public class PageRank {    private int n;    private double[][] A;    private double d = 0.85;    private double[] P;    public PageRank(String path) throws IOException {        BufferedReader bfr = new BufferedReader(new FileReader(path));        List<String> listTemp = new ArrayList<String>();        HashMap<String, Integer> pageNum = new HashMap<String, Integer>();        Set<String> pageSet = new HashSet<String>();        String tmp = "";        while ((tmp = bfr.readLine()) != null) {            listTemp.add(tmp);            String[] t = tmp.split(" ");            pageSet.add(t[0]);            pageSet.add(t[1]);            if (pageNum.containsKey(t[0])) {                pageNum.put(t[0], pageNum.get(t[0]) + 1);            } else {                pageNum.put(t[0], 1);            }        }        bfr.close();        int num = pageSet.size();        double[][] At = new double[num][num];        for (int i = 0; i < num; i++) {            At[i] = new double[num];        }        // get A:        for (String t : listTemp) {            int t1 = Integer.parseInt(t.split(" ")[0]);            int t2 = Integer.parseInt(t.split(" ")[1]);            At[t1 - 1][t2 - 1]++;        }        for (int i = 0; i < num; i++) {            if (pageNum.containsKey((i + 1) + "")) {                for (int j = 0; j < num; j++) {                    At[i][j] /= (1.0 * pageNum.get((i + 1) + ""));                }            } else {                for (int j = 0; j < num; j++) {                    At[i][j] = 1.0 / num;                }            }        }        this.n = num;        // AT        this.A = new double[num][num];        for (int i = 0; i < num; i++) {            this.A[i] = new double[num];            for (int j = 0; j < num; j++) {                this.A[i][j] = At[j][i];            }        }        this.P = new double[num];    }    public void init() {        //p        for (int i = 0; i < n; i++) {            P[i] = 1.0 / n;        }    }    public double[] calculateMatrix(double[][] a, double[] p) {        double[] re = new double[p.length];        for (int i = 0; i < p.length; i++) {            re[i] = 0;            for (int j = 0; j < p.length; j++) {                re[i] += a[i][j] * p[j];            }        }        return re;    }    public double calculateError(double[] a, double[] b) {        double re = 0.0;        for (int i = 0; i < b.length; i++) {            re += Math.abs(a[i] - b[i]);        }        return re;    }    //    public void run(int iter) {    //        init();    //        for (int it = 0; it < iter; it++) {    //            long startTime = System.currentTimeMillis();    //            double[] Apk_1 = calculateMatrix(A, P);    //            for (int i = 0; i < n; i++) {    //                P[i] = (1 - d) + d * Apk_1[i];    //            }    //            long endTime = System.currentTimeMillis();    //    //            System.out.println("第" + it + "次迭代耗时" + (endTime - startTime) + "ms");    //        }    //        for (double tmp : P) {    //            System.out.println(tmp);    //        }    //    }    public void run(double error) {        init();        int iter = 0;        double[] pTmp = new double[n];        do {            for (int i = 0; i < n; i++) {                pTmp[i] = P[i];            }            iter++;            double[] Apk_1 = calculateMatrix(A, P);            for (int i = 0; i < n; i++) {                P[i] = (1 - d) + d * Apk_1[i];            }        } while (calculateError(pTmp, P) > error);        System.out.println("the sum iter is " + iter);        System.out.println("-------the result is -------");        for (double tmp : P) {            System.out.println(tmp);        }    }}
3.2 小测试程序:

package dataMining;import java.io.IOException;/** * @author 作者 : xcy * @version 创建时间:2016年11月15日 下午10:38:02 *          类说明 */public class TestPr {    public static void main(String[] args) throws IOException {        // TODO Auto-generated method stub        PageRank pr = new PageRank("G:/eclipse/pr.txt");        pr.run(0.001);    }}

注: pr.txt 中的格式如下(对应图1,第一列表示边的出结点,第二列表示边的入结点):

1 2

1 3

2 1

2 3

3 2

4 3

4 5

4 6

6 5

6 4

       



1 0
原创粉丝点击