在本次实验中,我是参考了PageRank算法的MapReduce实现
输入文本格式:网页+\t+该网页链接到的网页的集合(相互之间用英文逗号分开)
实验要求&输出文本格式:
PageRank算法思想:
如果网页T存在一个指向网页A的连接,则表明T的所有者认为A比较重要,从而把T的一部分重要性得分赋予A。这个重要性得分值为:PR(T)/L(T)
其中PR(T)为T的PageRank值,L(T)为T的出链数。
则A的PageRank值为一系列类似于T的页面重要性得分值的累加。
即一个页面的得票数由所有链向它的页面的重要性来决定,到一个页面的超链接相当于对该页投一票。一个页面的PageRank是由所有链向它的页面(链入页面)的重要性经过递归算法得到的。一个有较多链入的页面会有较高的等级,相反如果一个页面没有任何链入页面,那么它没有等级。
解决思路:
可以得到公式
PR(A)即A的PageRank值;d为阻尼因子,实验中给出为0.85;L(B)即B网站所有的出链数量(即B网站内的所有链接的数量)。
所以公式的意义是:A的PageRank值=(1-d)+d*(链接到A的所有网站的PR值/该网站的所有出链数量之和)。这里首次计算时为每个链接附上一个初始PR值,实验中给出为1.0。
下面举个例子,这样能更清楚地知道map和reduce的输入输出对应该怎么写。
1 | A B,C,D |
为了计算PR(A),就要知道所有链接到A的网站,PR值及其出链数。在例子中,这些网站就是B,C,D,
所以要得到类似 B,PR(B),2 这种值,再加上记号表明这是B到A的,可以形成<A, B,PR(B),2 >。MapReduce中会将同一个key的分到一个reduce中,因此在处理key为A时,会有如下输入对:
1 | <A, B,PR(B),2> |
就可以利用公式,计算出PR(A)。因为要进行迭代,所以还必须要有 A B,C,D 。
为了区分 ,将 B,PR(B),2 改为用 ; 分隔:B;PR(B);2。
初始值是1.0可以直接赋给网站,但是迭代中,要怎么获取网站当前的PR值呢?
在reduce中已经计算出了PR(A),我们可以将它加到key中输出作为下一次map的输入key:A,PR(A)
TIPS:
(1)根据分析,map第一次输入为A B,C,D,迭代中的输入为<A,PR(A) , B,C,D>,是否需要写2个map函数?
答:可以只写一个map函数,在第一次中,可以利用KeyValueTextInputFormat,它将\t之前的作为key读入,为A;在迭代中读入的key为A,PR(A)。利用分隔符 , 可以得到相应的值。
(2)在实现中,所有与计算PR值有关的必须用double类型,因为要保留10位小数。
1 | private static final double d = 0.85;//阻尼系数 |
main函数中的迭代:
1 | Configuration conf = new Configuration(); |
到此,输出的结果为:
可以看到输出结果为:网站,PR 网站中的链接
与标准输出还有距离,因此,我添加了一对map和reduce用来标准化输出。
为了按照PR排列,结合reduce是按照key排列的,且顺序是从小到大,因此先将PR作为key,网站作为value。
又因为,在标准输出中,是按照PR从大到小进行排列。所以可以在PR前加个负号 - 。
1 | //10次迭代后,按格式输出 要按pr值排序,保留10位小数 |
相应的,在main函数中添加:
1 | String[] otherArgs = new String[]{"/user/pr/out"+10, "/user/pr/out"+11}; |
结果:
✿✿ヽ(°▽°)ノ✿