PageRank算法原理及实现

PageRank算法原理介绍


  PageRank算法是google的网页排序算法,在《The Top Ten Algorithms in Data Mining》一书中第6章有介绍。大致原理是用户搜索出的多个网页需要按照一定的重要程度(即后面讲的权重)排序,每个网页的权重由所有链接到它的其他网页的权重的加权和,加权系数为每个网页链出的网页数的倒数,也就是说每个网页的权重会平均分配到其链向的所有网页。

  例如A链接到B和C,B链接到C,C链接到A,P(X)表示X的权重,如下图所示

  则每个节点的权重关系为:

P(A) = P(C)

                                   P(B) = P(A)/2

                                   P(C) = P(A)/2 + P(B)

  一般地,可以写成个线性方程组形式:

P = AP

  如此通过迭代即可求出最终各个网页的权重值。

  但是,当存在某些特殊情况时,如某个网页的链入或链出数为0,则迭代不会收敛。因此在上述算法中增加了个阻尼系数d,d表示用户会继续点击下一网页的概率,同时用户在1-d的概率下会随机访问到任意网页,那么上面的公式会修正为:

P = (1-d)/N * ones(N,1) + d*AP

  其中N为所有网页数,ones(N,1)表示N行1列的全1矩阵。通过上述公式可以迭代计算出最终各网页权重。

  详细介绍可以参考wiki百科

算法实现


  首先声明这个CPageRank类:

 1 typedef unsigned char BYTE;
 2 class CPageRank
 3 {
 4 public:
 5     CPageRank(int nWebNum = 5, bool bLoadFromFile = false);
 6    ~CPageRank();
 7
 8    int Process();
 9    float *GetWeight();
10
11 private:
12    void InitGraph(bool bLoadFromFile = false);
13    void GenerateP();
14    BYTE *m_pu8Relation; //节点关系 i行j列表示j是否指向i
15    float *m_pf32P; //转移矩阵
16
17    //缓存
18    float *m_pf32Weight0;
19    float *m_pf32Weight1;
20    int *m_pl32Out;
21    int m_nNum;
22    float m_f32DampFactor; //阻尼系数
23
24    int m_nMaxIterTime;
25    float m_f32IterErr;
26    float *m_pf32OutWeight;//输出的最终权重
27 };

  下面就是具体各个函数的实现了。

  首先构造函数主要是初始化一些迭代相关变量,分配空间等,这里把生成节点指向图的工作也放在这里,支持直接随机生成和读取二进制文件两种方式。

 1 CPageRank::CPageRank(int nWebNum, bool bLoadFromFile)
 2 {
 3      m_f32DampFactor = 0.85;
 4      m_nMaxIterTime = 1000;
 5      m_f32IterErr = 1e-6;
 6
 7      m_nNum = nWebNum;
 8      m_pu8Relation = new BYTE[m_nNum * m_nNum];
 9      m_pf32P = new float[m_nNum * m_nNum];
10      m_pf32Weight0 = new float[m_nNum];
11      m_pf32Weight1 = new float[m_nNum];
12      m_pl32Out = new int[m_nNum];//每个节点指向的节点数
13
14      InitGraph(bLoadFromFile);
15 }

  析构函数自然就是释放内存了:

1 CPageRank::~CPageRank()
2  {
3      delete []m_pl32Out;
4      delete []m_pf32P;
5      delete []m_pf32Weight0;
6      delete []m_pf32Weight1;
7      delete []m_pu8Relation;
8  }

  下面就是随机生成或读取文件产生节点指向关系,如果随机生成的话,会自动保存当前生成的图,便于遇到问题时可复现调试:

 1 void CPageRank::InitGraph(bool bLoadFromFile)
 2  {
 3      FILE *pf = NULL;
 4      if(bLoadFromFile)
 5      {
 6          pf = fopen("map.dat", "rb");
 7          if(pf)
 8          {
 9              fread(m_pu8Relation, sizeof(BYTE), m_nNum * m_nNum, pf);
10              fclose(pf);
11              return;
12          }
13      }
14
15      //建立随机的节点指向图
16      int i, j;
17      srand((unsigned)time(NULL));
18      for(i = 0; i < m_nNum; i++)
19      {
20          //指向第i个的节点
21          for(j = 0; j < m_nNum; j++)
22          {
23              m_pu8Relation[i * m_nNum + j] = rand() & 1;
24          }
25          //自己不指向自己
26          m_pu8Relation[i * m_nNum + i] = 0;
27      }
28
29      pf = fopen("map.dat", "wb");
30      if(pf)
31      {
32          fwrite(m_pu8Relation, sizeof(BYTE), m_nNum * m_nNum, pf);
33          fclose(pf);
34      }
35  }

  既然已经产生了各个节点的关系了,那PageRank的核心思想就是根据关系,生成出上面的转移矩阵P:

 1 void CPageRank::GenerateP()
 2  {
 3      int i,j;
 4      float *pf32P = NULL;
 5      BYTE *pu8Relation = NULL;
 6
 7      //统计流入流出每个节点数
 8      memset(m_pl32Out, 0, m_nNum * sizeof(int));
 9      pu8Relation = m_pu8Relation;
10      for(i = 0; i < m_nNum; i++)
11      {
12          for(j = 0; j < m_nNum; j++)
13          {
14              m_pl32Out[j] += *pu8Relation;
15              pu8Relation++;
16          }
17      }
18
19      //生成转移矩阵,每个节点的权重平均流出
20      pu8Relation = m_pu8Relation;
21      pf32P = m_pf32P;
22      for(i = 0; i < m_nNum; i++)
23      {
24          for(j = 0; j < m_nNum; j++)
25          {
26              if(m_pl32Out[j] > 0)
27              {
28                  *pf32P = *pu8Relation * 1.0f / m_pl32Out[j];
29              }
30              else
31              {
32                  *pf32P = 0.0f;
33              }
34              pu8Relation++;
35              pf32P++;
36          }
37      }
38
39      //考虑阻尼系数,修正转移矩阵
40      pf32P = m_pf32P;
41      for(i = 0; i < m_nNum; i++)
42      {
43          for(j = 0; j < m_nNum; j++)
44          {
45              *pf32P = *pf32P * m_f32DampFactor;
46              pf32P++;
47          }
48      }
49  }

  接下来就需要求解出各个节点的权重,process函数里先调用GenerateP生成出P矩阵,然后采用迭代法求解,当时为了测试收敛速度,直接返回了迭代次数:

 1 int CPageRank::Process()
 2  {
 3      int i,j,k,t;
 4      float f32MaxErr = 0.0f;
 5      float *pf32Org = m_pf32Weight0;
 6      float *pf32New = m_pf32Weight1;
 7      float f32MinWeight = (1 - m_f32DampFactor) / m_nNum;
 8
 9      //设置初始值,全1
10      for(i = 0; i < m_nNum; i++)
11      {
12          pf32Org[i] = 1.0f / m_nNum;//rand() * 2.0f / RAND_MAX;
13      }
14
15     //生成P矩阵
16      GenerateP();
17
18      //迭代
19      for(t = 0; t < m_nMaxIterTime; t++)
20      {
21          //开始迭代
22          f32MaxErr = 0.0f;
23          for(i = 0; i < m_nNum; i++)
24          {
25              pf32New[i] = f32MinWeight;
26              int l32Off = i * m_nNum;
27              for(j = 0; j < m_nNum; j++)
28              {
29                  pf32New[i] += m_pf32P[l32Off + j] * pf32Org[j];
30              }
31
32              float f32Err = fabs(pf32New[i] - pf32Org[i]);
33              if(f32Err > f32MaxErr)
34              {
35                  f32MaxErr = f32Err;
36              }
37          }
38
39          //迭代误差足够小,停止
40          if(f32MaxErr < m_f32IterErr)
41          {
42              break;
43          }
44
45          //交换2次迭代结果
46          float *pf32Temp = pf32Org;
47          pf32Org = pf32New;
48          pf32New = pf32Temp;
49      }
50
51      //迭代结果存在pf32New中
52      m_pf32OutWeight = pf32New;
53      return t;
54  }

  最后的结果已经存在了m_pf32OutWeight中了,下面函数直接传出结果:

1  float * CPageRank::GetWeight()
2  {
3      return m_pf32OutWeight;
4  }

  这样,整个算法就算完成了,考虑到篇幅,贴上来的代码把opencv显示相关的代码去掉了,完整代码见https://bitbucket.org/jcchen1987/mltest。

  下面是结果图,即便节点数较多时,算法收敛也比较快。

分析总结


  对于上面这个公式,看到网上有人假定P的总能量是1,则可以改写为P=BP的形式来进行迭代,这种方法也实现了一下,问题仍然是当存在网页链入或者链出数为0时,每次迭代后不能保证能量守恒,那么下一次就会导致P=BP这个公式不成立,从而出现迭代不收敛;一种有效的做法是每次迭代后就将P进行能量规一化,这样是可以保证结果的收敛性的。但是这种做法与原始算法的结果会有一点细微的出入。因此建议按照原始的公式进行迭代求解。

时间: 2025-01-06 20:10:12

PageRank算法原理及实现的相关文章

pagerank算法

转自 http://blog.csdn.net/hguisu/article/details/7996185 1. PageRank算法概述 PageRank,即网页排名,又称网页级别.Google左侧排名或佩奇排名. 是Google创始人拉里·佩奇和谢尔盖·布林于1997年构建早期的搜索系统原型时提出的链接分析算法,自从Google在商业上获得空前的成功后,该算法也成为其他搜索引擎和学术界十分关注的计算模型.目前很多重要的链接分析算法都是在PageRank算法基础上衍生出来的.PageRank

Machine Learning:PageRank算法

1. PageRank算法概述 PageRank,即网页排名,又称网页级别.Google左側排名或佩奇排名.         在谷歌主导互联网搜索之前, 多数搜索引擎採用的排序方法, 是以被搜索词语在网页中的出现次数来决定排序--出现次数越多的网页排在越前面. 这个判据不能说毫无道理, 由于用户搜索一个词语. 通常表明对该词语感兴趣. 既然如此, 那该词语在网页中的出现次数越多, 就越有可能表示该网页是用户所须要的. 可惜的是, 这个貌似合理的方法实际上却行不大通. 由于依照这样的方法, 不论什

[转]PageRank算法

原文引自: 原文引自: http://blog.csdn.net/hguisu/article/details/7996185 感谢 1. PageRank算法概述 PageRank,即网页排名,又称网页级别.Google左侧排名或佩奇排名. 是Google创始人拉里·佩奇和谢尔盖·布林于1997年构建早期的搜索系统原型时提出的链接分析算法,自从Google在商业上获得空前的成功后,该算法也成为其他搜索引擎和学术界十分关注的计算模型.目前很多重要的链接分析算法都是在PageRank算法基础上衍生

MapReduce原理——PageRank算法Java版

Page Rank就是MapReduce的来源,下文是一个简单的计算PageRank的示例. import java.text.DecimalFormat; /**  * Created by jinsong.sun on 2014/7/15.  */ public class PageRankCaculator {     public static void main(String[] args) {         double[][] g = calcG(genS(), 0.85);  

浅析PageRank算法

很早就对Google的PageRank算法很感兴趣,但一直没有深究,只有个轮廓性的概念.前几天趁团队outing的机会,在动车上看了一些相关的资料(PS:在动车上看看书真是一种享受),趁热打铁,将所看的东西整理成此文. 本文首先会讨论搜索引擎的核心难题,同时讨论早期搜索引擎关于结果页面重要性评价算法的困境,借此引出PageRank产生的背景.第二部分会详细 讨论PageRank的思想来源.基础框架,并结合互联网页面拓扑结构讨论PageRank处理Dead Ends及平滑化的方法.第三部分讨论To

谷歌PageRank算法

1. 从Google网页排序到PageRank算法 (1)谷歌网页怎么排序? 先对搜索关键词进行分词,如"技术社区"分词为"技术"和"社区": 根据建立的倒排索引返回同时包含分词后结果的网页: 将返回的网页相关性(类似上篇文章所讲的文本相似度)网页,相关性越高排名越靠前 (2)怎么处理垃圾网页?那么问题来了,假如有某个垃圾网页中虽然也包含大量的查询词,但却并非满足用户需要的文档,因此,页面本身的重要性在网页排序中也起着很重要的作用.(3)如何度量

张洋:浅析PageRank算法

本文引自http://blog.jobbole.com/23286/ 很早就对Google的PageRank算法很感兴趣,但一直没有深究,只有个轮廓性的概念.前几天趁团队outing的机会,在动车上看了一些相关的资料(PS:在动车上看看书真是一种享受),趁热打铁,将所看的东西整理成此文. 本文首先会讨论搜索引擎的核心难题,同时讨论早期搜索引擎关于结果页面重要性评价算法的困境,借此引出PageRank产生的背景.第二部分会详细讨论PageRank的思想来源.基础框架,并结合互联网页面拓扑结构讨论P

PageRank算法初探

1. PageRank的由来和发展历史 0x1:源自搜索引擎的需求 Google早已成为全球最成功的互联网搜索引擎,在Google出现之前,曾出现过许多通用或专业领域搜索引擎.Google最终能击败所有竞争对手,很大程度上是因为它解决了困扰前辈们的最大难题:对搜索结果按重要性排序.而解决这个问题的算法就是PageRank.毫不夸张的说,是PageRank算法成就了Google今天的地位. 1. 搜索引擎的核心框架 从本质上说,搜索引擎是一个资料检索系统,搜索引擎拥有一个资料库(具体到这里就是互联

Adaboost算法原理分析和实例+代码(简明易懂)

Adaboost算法原理分析和实例+代码(简明易懂) [尊重原创,转载请注明出处] http://blog.csdn.net/guyuealian/article/details/70995333     本人最初了解AdaBoost算法着实是花了几天时间,才明白他的基本原理.也许是自己能力有限吧,很多资料也是看得懵懵懂懂.网上找了一下关于Adaboost算法原理分析,大都是你复制我,我摘抄你,反正我也搞不清谁是原创.有些资料给出的Adaboost实例,要么是没有代码,要么省略很多步骤,让初学者