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类:
 typedef unsigned char BYTE;
class CPageRank
{
public:
CPageRank(int nWebNum = , bool bLoadFromFile = false);
~CPageRank(); int Process();
float *GetWeight(); private:
void InitGraph(bool bLoadFromFile = false);
void GenerateP();
BYTE *m_pu8Relation; //节点关系 i行j列表示j是否指向i
float *m_pf32P; //转移矩阵 //缓存
float *m_pf32Weight0;
float *m_pf32Weight1;
int *m_pl32Out;
int m_nNum;
float m_f32DampFactor; //阻尼系数 int m_nMaxIterTime;
float m_f32IterErr;
float *m_pf32OutWeight;//输出的最终权重
};

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

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

 CPageRank::CPageRank(int nWebNum, bool bLoadFromFile)
{
m_f32DampFactor = 0.85;
m_nMaxIterTime = ;
m_f32IterErr = 1e-; m_nNum = nWebNum;
m_pu8Relation = new BYTE[m_nNum * m_nNum];
m_pf32P = new float[m_nNum * m_nNum];
m_pf32Weight0 = new float[m_nNum];
m_pf32Weight1 = new float[m_nNum];
m_pl32Out = new int[m_nNum];//每个节点指向的节点数 InitGraph(bLoadFromFile);
}

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

 CPageRank::~CPageRank()
{
delete []m_pl32Out;
delete []m_pf32P;
delete []m_pf32Weight0;
delete []m_pf32Weight1;
delete []m_pu8Relation;
}

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

 void CPageRank::InitGraph(bool bLoadFromFile)
{
FILE *pf = NULL;
if(bLoadFromFile)
{
pf = fopen("map.dat", "rb");
if(pf)
{
fread(m_pu8Relation, sizeof(BYTE), m_nNum * m_nNum, pf);
fclose(pf);
return;
}
} //建立随机的节点指向图
int i, j;
srand((unsigned)time(NULL));
for(i = ; i < m_nNum; i++)
{
//指向第i个的节点
for(j = ; j < m_nNum; j++)
{
m_pu8Relation[i * m_nNum + j] = rand() & ;
}
//自己不指向自己
m_pu8Relation[i * m_nNum + i] = ;
} pf = fopen("map.dat", "wb");
if(pf)
{
fwrite(m_pu8Relation, sizeof(BYTE), m_nNum * m_nNum, pf);
fclose(pf);
}
}

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

 void CPageRank::GenerateP()
{
int i,j;
float *pf32P = NULL;
BYTE *pu8Relation = NULL; //统计流入流出每个节点数
memset(m_pl32Out, , m_nNum * sizeof(int));
pu8Relation = m_pu8Relation;
for(i = ; i < m_nNum; i++)
{
for(j = ; j < m_nNum; j++)
{
m_pl32Out[j] += *pu8Relation;
pu8Relation++;
}
} //生成转移矩阵,每个节点的权重平均流出
pu8Relation = m_pu8Relation;
pf32P = m_pf32P;
for(i = ; i < m_nNum; i++)
{
for(j = ; j < m_nNum; j++)
{
if(m_pl32Out[j] > )
{
*pf32P = *pu8Relation * 1.0f / m_pl32Out[j];
}
else
{
*pf32P = 0.0f;
}
pu8Relation++;
pf32P++;
}
} //考虑阻尼系数,修正转移矩阵
pf32P = m_pf32P;
for(i = ; i < m_nNum; i++)
{
for(j = ; j < m_nNum; j++)
{
*pf32P = *pf32P * m_f32DampFactor;
pf32P++;
}
}
}

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

 int CPageRank::Process()
{
int i,j,k,t;
float f32MaxErr = 0.0f;
float *pf32Org = m_pf32Weight0;
float *pf32New = m_pf32Weight1;
float f32MinWeight = ( - m_f32DampFactor) / m_nNum; //设置初始值,全1
for(i = ; i < m_nNum; i++)
{
pf32Org[i] = 1.0f / m_nNum;//rand() * 2.0f / RAND_MAX;
} //生成P矩阵
GenerateP(); //迭代
for(t = ; t < m_nMaxIterTime; t++)
{
//开始迭代
f32MaxErr = 0.0f;
for(i = ; i < m_nNum; i++)
{
pf32New[i] = f32MinWeight;
int l32Off = i * m_nNum;
for(j = ; j < m_nNum; j++)
{
pf32New[i] += m_pf32P[l32Off + j] * pf32Org[j];
} float f32Err = fabs(pf32New[i] - pf32Org[i]);
if(f32Err > f32MaxErr)
{
f32MaxErr = f32Err;
}
} //迭代误差足够小,停止
if(f32MaxErr < m_f32IterErr)
{
break;
} //交换2次迭代结果
float *pf32Temp = pf32Org;
pf32Org = pf32New;
pf32New = pf32Temp;
} //迭代结果存在pf32New中
m_pf32OutWeight = pf32New;
return t;
}

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

  float * CPageRank::GetWeight()
{
return m_pf32OutWeight;
}

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

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

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

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

  1. PageRank算法原理与Python实现

    一.什么是pagerank PageRank的Page可是认为是网页,表示网页排名,也可以认为是Larry Page(google 产品经理),因为他是这个算法的发明者之一,还是google CEO( ...

  2. 【原创】机器学习之PageRank算法应用与C#实现(1)算法介绍

    考虑到知识的复杂性,连续性,将本算法及应用分为3篇文章,请关注,将在本月逐步发表. 1.机器学习之PageRank算法应用与C#实现(1)算法介绍 2.机器学习之PageRank算法应用与C#实现(2 ...

  3. [转]PageRank算法

    原文引自: 原文引自: http://blog.csdn.net/hguisu/article/details/7996185 感谢 1. PageRank算法概述 PageRank,即网页排名,又称 ...

  4. PageRank算法实现

    基本原理 在互联网上,如果一个网页被很多其他网页所链接,说明它受到普遍的承认和信赖,那么它的排名就高.这就是PageRank的核心思想. 引用来自<数学之美>的简单例子: 网页Y的排名应该 ...

  5. Machine Learning:PageRank算法

    1. PageRank算法概述 PageRank,即网页排名,又称网页级别.Google左側排名或佩奇排名.         在谷歌主导互联网搜索之前, 多数搜索引擎採用的排序方法, 是以被搜索词语在 ...

  6. PageRank算法R语言实现

    PageRank算法R语言实现 Google搜索,早已成为我每天必用的工具,无数次惊叹它搜索结果的准确性.同时,我也在做Google的SEO,推广自己的博客.经过几个月尝试,我的博客PR到2了,外链也 ...

  7. PageRank 算法-Google 如何给网页排名

    公号:码农充电站pro 主页:https://codeshellme.github.io 在互联网早期,随着网络上的网页逐渐增多,如何从海量网页中检索出我们想要的页面,变得非常的重要. 当时著名的雅虎 ...

  8. PageRank算法--从原理到实现

    本文将介绍PageRank算法的相关内容,具体如下: 1.算法来源 2.算法原理 3.算法证明 4.PR值计算方法 4.1 幂迭代法 4.2 特征值法 4.3 代数法 5.算法实现 5.1 基于迭代法 ...

  9. 【原创】机器学习之PageRank算法应用与C#实现(2)球队排名应用与C#代码

    在上一篇文章:机器学习之PageRank算法应用与C#实现(1)算法介绍 中,对PageRank算法的原理和过程进行了详细的介绍,并通过一个很简单的例子对过程进行了讲解.从上一篇文章可以很快的了解Pa ...

随机推荐

  1. Python之模块和包(Day21)

    一.Python模块 Python模块(module),是一个Python文件,以.py结尾,包含了Python对象定义和Python语句. 模块让你能够有逻辑的组织你的Python代码段 把相关的代 ...

  2. Polar 投影c#版本移植

    from:http://hi.baidu.com/sungaoyong/item/0c4584d25873f131e3108f05 ///刘泽军java版本的极坐标投影c#版本的移植 using Sy ...

  3. eclipse连接SqlServer2008(被它搞得惨兮兮)

    建民大叔告诉我要考试做一个系统要求连接SqlServer2008,于是我便开始了“炼狱”,人家连接起来一路绿灯,我却一路红灯所以决定把它记录下来,给后来人提供方便. 第一个红灯: 启动服务后利用cmd ...

  4. 023_数量类型练习——Hadoop MapReduce手机流量统计

    1) 分析业务需求:用户使用手机上网,存在流量的消耗.流量包括两部分:其一是上行流量(发送消息流量),其二是下行流量(接收消息的流量).每种流量在网络传输过程中,有两种形式说明:包的大小,流量的大小. ...

  5. VMWare中安装windowsXP遇到的问题

    XP系统安装 1.安装Windows和安装linux不一样,创建虚拟机完成后Linux自动根据硬盘进行系统安装,不需要提前分区.而windows必须进行提前分区,这个分区是在虚拟磁盘上完成的,就是你创 ...

  6. flex 实现图片播放 方案一 图片全部预加载放内存

    这种方案,对于web的应用有局限性,在图片量比较多,比较大的时候,就会爆浏览器异常.一般建议轻量级的采用这种方案. <?xml version="1.0" encoding= ...

  7. 【转载】iptables、tc和ip命令

    2.3 CommandListener中的命令 CL一共定义了11个命令,这些命令充分反映了Netd在Android系统中网络管理和控制方面的职责.本节首先介绍Linux系统中常用的三个网络管理工具, ...

  8. codeforces上某题

    一道codeforces上的题目. 题目大意: 定义有k个不同的字符的字符串为好字符串.现在给出一个字符串,求解对该字符串的每个前缀Si至少是多少个好字符串的连接,若不能由好字符串连接而成则输出-1. ...

  9. 机器学习相关知识整理系列之二:Bagging及随机森林

    1. Bagging的策略 从样本集中重采样(有放回)选出\(n\)个样本,定义子样本集为\(D\): 基于子样本集\(D\),所有属性上建立分类器,(ID3,C4.5,CART,SVM等): 重复以 ...

  10. JDBC的一些代码

    import java.sql.*; public class jdbc { //JDBC 驱动名及数据库名 URL static final String JDBC_DRIVE = "co ...