总算有一个周末下午时间,决定亲手把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:
#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
#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
#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等进一步探索,感兴趣的读者按实际需求进一步研究吧。
希望对于不熟练编程实现的人,能起到入门参考作用。