一、实验内容
- 采用基于“抽税”法的PageRank算法分析图1的网页排名;
图1 Web链接实例图
- 图1中,若节点①和节点③是主题节点,采用面向主题的PageRank算法重新计算所有节点的PageRank值。
二、实验设计(原理分析及流程)
首先是需要建立一个初始化概率分布向量V0,因为共有6个网页,因此V0的每个分量都是1/6,且V0是一个6维列向量。之后,根据上图的网页链接关系,构造一个网页转移矩阵。
具体是先使用vector来存储初始概率分布向量V0,同时创建结果特征矩阵V2。然后使用一个选择函数Choice来根据用户输入选择合适的向量计算方法(抽税法或面向主题法)。最后,显示surplus以及最终的特征向量。
其中CalResTax 计算特征矩阵左乘概率分布向量,抽税法,参数:上一次左乘结果,这次左乘结果,转移矩阵,抽税常数。CalResThe 计算特征矩阵左乘概率分布向量,面向主题方法,参数:上一次左乘结果,这次左乘结果,主题矩阵,转移矩阵,主题常数。另外,surplus定义为两个向量对应元素差的绝对值之和,使用SurCal函数来实现。
三、代码:
// pagerank.cpp model the google pagerank algorithm
// using taxation or theme oriented
// surplus定义为两个向量对应元素差的绝对值之和
#include <iostream>
#include <vector>
#include <cmath> // 使用abs()函数 求绝对值
#define PAGENUM 6 // page个数
#define THENUM 2
#define EPS 0.001 // 阈值
#define BETA 0.85 // beta
// 初始化的概率向量v0
std::vector<double> V0 (PAGENUM, (double)1/6);
int ThemeArr[PAGENUM] = { 0.5, 0, 0.5, 0, 0, 0};
std::vector<double> VS0 (ThemeArr, ThemeArr + PAGENUM);
// 手动建立的转移矩阵
double TrMat[PAGENUM][PAGENUM] =
{
0.0, 0.5, 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1.0, 0.5, 0.0, 0.0, 0.5, 0.0,
0.0, 0.0, 0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.0, 0.5, 0.0, 0.0
};
// 网页主题 es
int TheMat[PAGENUM] =
{
1, 0, 1, 0, 0, 0
};
// 根据用户输入选择抽税还是面向主题的计算pagerank方法
// 输入'T'返回true表明选择抽税方法
char Choice(void)
{
char UsCho;
std::cout << "Now choose the calculate function: " << std::endl;
std::cout << "Enter 'T' for tax, enter 'S' for Theme, enter 'Q' to quit:" << std::endl;
std::cin >> UsCho;
return tolower(UsCho);
}
// 计算特征矩阵左乘概率分布向量,抽税法
// 参数:上一次左乘结果, 这次左乘结果, 转移矩阵, 抽税常数
int CalResTax(const std::vector<double> &V1, std::vector<double> &V2,
const double TrMat[][PAGENUM], const double TAX)
{
double TemSum = 0.0;
for(int i = 0; i < PAGENUM; i++) // 矩阵行变化
{
for (int j = 0; j < PAGENUM; j++) // 矩阵列变化
{
TemSum += V1[j] * TrMat[i][j]; // 矩阵列乘以概率列向量
}
V2[i] = BETA * TemSum + TAX; // 存放左乘后结果 + 税
TemSum = 0.0; // 一行乘以一列的结果重置为0
}
return 0;
}
// 计算特征矩阵左乘概率分布向量,面向主题方法
// 参数:上一次左乘结果, 这次左乘结果,主题矩阵, 转移矩阵,主题常数
int CalResThe(const std::vector<double> &V1, std::vector<double> &V2,
const std::vector<int> &S, const double TrMat[][PAGENUM],const double THEME)
{
double TemSum = 0.0;
for(int i = 0; i < PAGENUM; i++) // 矩阵行变化
{
for (int j = 0; j < PAGENUM; j++) // 矩阵列变化
{
TemSum += V1[j] * TrMat[i][j]; // 矩阵列乘以概率列向量
}
V2[i] = BETA * TemSum + THEME * S[i]; // 存放左乘后结果 + 主题
TemSum = 0.0; // 一行乘以一列的结果重置为0
}
return 0;
}
// 计算surplus,定义见开头注释
// 参数:第一个概率分布向量,第二个概率分布向量
double SurCal(const std::vector<double> &V1, std::vector<double> &V2)
{
double Sur = 0.0;
for (int i = 0; i < PAGENUM; i++)
Sur += fabs(V2[i] - V1[i]); // 加括号更清晰
return Sur;
}
int main(void)
{
// result property matrix
std::vector<double> V2 (PAGENUM); // 最新的特征矩阵
std::vector<int> S(TheMat, TheMat + PAGENUM); // 主题矩阵
using std::cout;
using std::endl;
double surplus = 1.0;
const double TAX = (double)(1.0 - BETA) / PAGENUM; // 循环不变量外提,小优化
const double THEME = (double)(1.0 - BETA) / THENUM; // 主题矩阵es
char cho;
while (( cho = Choice()) != 'q')
{
cout << "cho = " << cho << endl;
if (cho == 't')
{
std::vector<double> V1 (V0); // 上一个特征矩阵
while (surplus > EPS)
{
CalResTax(V1, V2, TrMat, TAX);
surplus = SurCal(V1, V2);
V1.assign(V2.begin(), V2.end()); // 更新V1的内容,注意使用的方法
}
}
else if (cho == 's')
{
std::vector<double> V1 (VS0); // 上一个特征矩阵
while (surplus > EPS)
{
CalResThe(V1, V2, S, TrMat, THEME);
surplus = SurCal(V1, V2);
V1.assign(V2.begin(), V2.end()); // 更新V1的内容,注意使用的方法
}
}
else
continue;
cout << "surplus: " << surplus << endl;
cout << "The result vector: " << endl;
for(auto p : V2) // 输出最后的特征矩阵
cout << p << endl;
surplus = 1.0; // 重置surplus以便其他方法调用
}
cout << "Done." << endl;
return 0;
}