总算有一个周末下午时间,决定亲手把pagerank算法实现一下。 之前只是看过思路,并一直使用同事的实现版本。
关于PageRank的介绍,请看
http://zh.wikipedia.org/wiki/PageRank
http://baike.baidu.com/view/1518.htm
不多废话了,直接进入正题,C++实现。
公式采用:
PR(A) = (1-d) +d(PR(t1)/C(t1) + ... + PR(tn)/C(tn))
样例,采用 http://zh.wikipedia.org/wiki/PageRank 中介绍的 A, B, C, D 四个站点,另外图采用邻接表存储方式(不是邻接矩阵,可根据习惯调整)
详情请看:http://www.webworkshop.net/pagerank.html
代码实现:C++语言: 贴彩色代码可用:http://fayaa.com/code/new/
为了方便构建图,使用了STL的数据结构,希望不会造成阅读障碍。
PageRank.h:
C++语言: 高亮代码由发芽网提供
#include <vector>
#include <set>
#include <string>
#include <iostream>
using namespace std;
// use graph store webpage, weight representlink times
class Node
{
public:
explicit Node(string name,
double pr =
1):name_(name), page_rank_(pr){}
~Node()
{
linkin_nodes_.clear();
}
void InsertLinkdInNode(Node* node)
{
//如果没有链接
if (linkin_nodes_.find(node) == linkin_nodes_.end())
{
linkin_nodes_.insert(node);
}
node->InsertLinkOutNode(
this);
}
void InsertLinkOutNode(Node* node)
{
//如果没有链接
if (linkout_nodes_.find(node) == linkout_nodes_.end())
{
linkout_nodes_.insert(node);
}
}
double GetPageRank()
{
return page_rank_;
}
void SetPageRank(
double pr)
{
page_rank_ = pr;
}
double CalcRank()
{
double pr =
0;
set<Node*>::const_iterator citr = linkin_nodes_.begin();
for (; citr != linkin_nodes_.end(); ++citr)
{
Node * node = *citr;
pr += node->GetPageRank()/node->GetOutBoundNum();
}
return pr;
}
size_t GetOutBoundNum()
{
return linkout_nodes_.size();
}
size_t GetInBoundNum()
{
return linkin_nodes_.size();
}
void PrintNode()
{
cout <<
"Node:" << name_ <<
" 's pagerank is: " << page_rank_ << endl;
}
private:
string name_;
set<Node*> linkin_nodes_;
set<Node*> linkout_nodes_;
double page_rank_;
};
class PageRank
{
public:
PageRank(
double q=
0.85);
~PageRank(
void);
void Calc(vector<Node*> & nodes,
int n);
double Calc(Node* node);
void PrintPageRank(vector<Node*> & nodes);
private:
double q_;
//阻尼系数
};
PageRank.cpp
C++语言: 高亮代码由发芽网提供
#include "PageRank.h"
#include <iostream>
PageRank::PageRank(
double q) : q_(q)
{
// q_ must < 1
}
PageRank::~PageRank(
void)
{
}
// 迭代计算n次
void PageRank::Calc(vector<Node*> & nodes,
int n)
{
for (
int i=
0; i<n; ++i)
{
vector<Node*>::const_iterator citr = nodes.begin();
for (; citr!=nodes.end(); ++citr)
{
Node * node = *citr;
Calc(node);
}
}
}
void PageRank::PrintPageRank(vector<Node*> & nodes)
{
double total_pr =
0;
vector<Node*>::const_iterator citr = nodes.begin();
for (; citr!=nodes.end(); ++citr)
{
Node * node = *citr;
node->PrintNode();
total_pr += node->GetPageRank();
}
cout <<
"Total PR:" << total_pr << endl;
}
double PageRank::Calc(Node * node)
{
double pr = node->CalcRank();
if (pr <
0.00000000000000000000001 && pr > -
0.00000000000000000000001)
//pr == 0
{
pr =
1-q_;
}
else
{
pr = pr * q_ +
1-q_;
}
node->SetPageRank(pr);
return pr;
}
main.cpp
C++语言: 高亮代码由发芽网提供
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include "PageRank.h"
using namespace std;
void InitGraph(vector<Node*> & nodes)
{
// 邻接表存储方式
// example 1
Node * a =
new Node(
"A");
Node * b =
new Node(
"B");
Node * c =
new Node(
"C");
Node * d =
new Node(
"D");
nodes.push_back(a);
nodes.push_back(b);
nodes.push_back(c);
nodes.push_back(d);
// link in node
// a <- b, c, d
a->InsertLinkdInNode(b);
a->InsertLinkdInNode(c);
a->InsertLinkdInNode(d);
// b <- d
b->InsertLinkdInNode(d);
// c <- b, d
c->InsertLinkdInNode(b);
c->InsertLinkdInNode(d);
}
void TestPageRank()
{
// build graph
vector<Node*> nodes;
InitGraph(nodes);
PageRank pr;
// 迭代计算5次 pagerank
pr.Calc(nodes,
40);
pr.PrintPageRank(nodes);
}
int main(
int argc,
const char ** argv)
{
TestPageRank();
return
0;
}
运行结果验证: http://www.webworkshop.net/pagerank_calculator.php?lnks=9,11,17,25,26,27&ilnks=&iblprs=&pgnms=&pgs=4&initpr=1&its=40&type=simple
截图:下面两个结果为初始pr值1,迭代计算40次后的结果对比,完全吻合。
这里只实现了simple mode,对于real mode等进一步探索,感兴趣的读者按实际需求进一步研究吧。
希望对于不熟练编程实现的人,能起到入门参考作用。