经过一段时间的学习,对于Hadoop有了一些了解,于是决定用MapReduce实现PageRank算法,以下简称PR

先简单介绍一下PR算法(摘自百度百科:https://baike.baidu.com/item/google%20pagerank/2465380?fr=aladdin&fromid=111004&fromtitle=pagerank):

PageRank让链接来"投票"
一个页面的“得票数”由所有链向它的页面的重要性来决定,到一个页面的超链接相当于对该页投一票。一个页面的PageRank是由所有链向它的页面(“链入页面”)的重要性经过递归算法得到的。一个有较多链入的页面会有较高的等级,相反如果一个页面没有任何链入页面,那么它没有等级。
  2005年初,Google为网页链接推出一项新属性nofollow,使得网站管理员和网站作者可以做出一些Google不计票的链接,也就是说这些链接不算作"投票"。nofollow的设置可以抵制评论垃圾。
假设一个由4个页面组成的小团体:A,B,C和D。如果所有页面都链向A,那么A的PR(PageRank)值将是B,C及D的Pagerank总和。
继续假设B也有链接到C,并且D也有链接到包括A的3个页面。一个页面不能投票2次。所以B给每个页面半票。以同样的逻辑,D投出的票只有三分之一算到了A的PageRank上。
换句话说,根据链出总数平分一个页面的PR值。
最后,所有这些被换算为一个百分比再乘上一个系数。由于“没有向外链接的页面”传递出去的PageRank会是0,所以,Google通过数学系统给了每个页面一个最小值:
说明:在Sergey Brin和Lawrence Page的1998年原文中给每一个页面设定的最小值是1-d,而不是这里的
(1-d)/N。 所以一个页面的PageRank是由其他页面的PageRank计算得到。Google不断的重复计算每个页面的PageRank。如果给每个页面一个随机PageRank值(非0),那么经过不断的重复计算,这些页面的PR值会趋向于稳定,也就是收敛的状态。这就是搜索引擎使用它的原因。
 
通过以上文字,可以总结出以下几点:
1.PR中每个页面都需要需要一个初始值
2.PR算法是一个趋于收敛的无限循环,因此需要一个条件来确定收敛完毕
一般而言收敛条件有以下三种情况:

1、每个页面的PR值和上一次计算的PR相等

2、设定一个差值指标(0.0001)。当所有页面和上一次计算的PR差值平均小于该标准时,则收敛。

3、设定一个百分比(99%),当99%的页面和上一次计算的PR相等

本文将采用第二种方式来实现该算法:

首先定义一个初始互联网环境,如下图所示:
转化为文件则内容如下:

A B D
B C
C A B
D B C

其中每一行的后面的页面为第一个页面的出链(A可以链到B和C)

由于需要统计每个页面的入链页面和出链数,因此需要两个MapReduce,第一个用于统计入链和出链,第二个用于循环统计PR值,代码如下:

package com.tyx.mapreduce.PageRank;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* Created by tyx on 2017/11/29.
*/
public class RunPageRankJob { // 统计所有链接的入链
private static Map<String,String > allInLine = new HashMap<>();
// 统计所有链接的出链
private static Map<String,Integer> allOutLine = new HashMap<>();
// 统计所有链接的现有pagerank
private static Map<String,Double> allPageRank = new HashMap<>();
// 统计所有链接计算后的pagerank
private static Map<String ,Double> allNextPageRank = new HashMap<>(); public static void main(String[] args) {
Configuration configuration = new Configuration();
// configuration.set(KeyValueLineRecordReader.KEY_VALUE_SEPERATOR," ");
configuration.set("fs.defaultFS", "hdfs://node1:8020");
configuration.set("yarn.resourcemanager.hostname", "node1");
// 第一个MapReduce为了统计出每个页面的入链,和每个页面的出链数
if (run1(configuration)){
run2(configuration);
}
} /*
输入数据:
A B D
B C
C A B
D B C*/ static class AcountOutMapper extends Mapper<Text,Text,Text,Text>{
@Override
protected void map(Text key, Text value, Context context) throws IOException, InterruptedException {
// super.map(key, value, context);
int num = 0;
// 若A能连接到B,则说明B是A的一条出链
String[] outLines = value.toString().split("\t");
for (int i=0;i<outLines.length;i++){
context.write(new Text(outLines[i]),key);
}
num = outLines.length;
// 统计出链
context.write(key,new Text("--"+num));
}
} static class AcountOutReducer extends Reducer<Text,Text,Text,Text>{
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
// super.reduce(key, values, context);
// 统计该页面的入链页面
String outStr = "";
int sum = 0;
for (Text text : values){
// 统计出链数目
if (text.toString().contains("--")){
sum += Integer.parseInt(text.toString().replaceAll("--",""));
}else {
outStr += text+"\t";
}
}
context.write(key,new Text(outStr+sum));
allOutLine.put(key.toString(),sum);
allInLine.put(key.toString(),outStr);
allPageRank.put(key.toString(),1.0);
}
} public static boolean run1(Configuration configuration){
try {
Job job = Job.getInstance(configuration);
FileSystem fileSystem = FileSystem.get(configuration); job.setJobName("acountline"); job.setJarByClass(RunPageRankJob.class); job.setMapperClass(AcountOutMapper.class);
job.setReducerClass(AcountOutReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class); job.setInputFormatClass(KeyValueTextInputFormat.class); Path intPath = new Path("/usr/output/pagerank.txt");
FileInputFormat.addInputPath(job,intPath); Path outPath = new Path("/usr/output/acoutline");
if (fileSystem.exists(outPath)){
fileSystem.delete(outPath,true);
}
FileOutputFormat.setOutputPath(job,outPath); boolean f = job.waitForCompletion(true);
return f;
} catch (Exception e) {
e.printStackTrace();
}
return false;
} /*第一次MapReduce输出数据:
A C
B A C D
C B D
D A*/ static class PageRankMapper extends Mapper<Text,Text,Text,Text>{
@Override
protected void map(Text key, Text value, Context context) throws IOException, InterruptedException {
// super.map(key, value, context);
String myUrl = key.toString();
// 取出该页面所有的入链页面
String inLines = allInLine.get(myUrl);
context.write(key,new Text(inLines));
}
} static class PageRankReducer extends Reducer<Text,Text,Text,Text>{
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
// super.reduce(key, values, context);
// 后半段求和公式的和 (PR(1)/L(1)…………PR(i)/L(i)
double sum = 0.0;
String outStr = "";
for (Text text : values){
String[] arr = text.toString().split("\t");
for (int i=0;i<arr.length;i++){
outStr += arr[i]+"\t";
sum += allPageRank.get(arr[i])/allOutLine.get(arr[i]);
}
}
// 算出该页面本次的PR结果
double nowPr = (1-0.85)/allPageRank.size()+0.85*sum; allNextPageRank.put(key.toString(),nowPr);
context.write(key,new Text(outStr));
}
} public static void run2(Configuration configuration){
double d = 0.001;
int i=1;
// 迭代循环趋于收敛
while (true){
try {
configuration.setInt("count",i);
i++;
Job job = Job.getInstance(configuration);
FileSystem fileSystem = FileSystem.get(configuration); job.setJobName("pagerank");
job.setJarByClass(RunPageRankJob.class);
job.setJobName("Pr"+i); job.setMapperClass(PageRankMapper.class);
job.setReducerClass(PageRankReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class); job.setInputFormatClass(KeyValueTextInputFormat.class); Path intPath = new Path("/usr/output/pagerank.txt");
if (i>2){
intPath = new Path("/usr/output/Pr"+(i-1));
}
FileInputFormat.addInputPath(job,intPath); Path outPath = new Path("/usr/output/Pr"+i);
if (fileSystem.exists(outPath)){
fileSystem.delete(outPath,true);
}
FileOutputFormat.setOutputPath(job,outPath); boolean f = job.waitForCompletion(true);
if (f){
System.out.println("job执行完毕");
double sum = 0.0;
// 提取本轮所有页面的PR值和上一轮作比较,
for (String key : allPageRank.keySet()){
System.out.println(key+"--------------------------"+allPageRank.get(key));
sum += Math.abs(allNextPageRank.get(key)-allPageRank.get(key));
allPageRank.put(key,allNextPageRank.get(key));
}
System.out.println(sum);
// 若平均差小于d则表示收敛完毕
if (sum/allPageRank.size()<d){
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

最终结果输入如下:

由图可知在这四个页面组成的互联网集群中,页面C的重要性是最高的

本次操作一共经过了30次循环:

若有不对之处请不吝指教,谢谢

转载请注明出处http://www.cnblogs.com/liuxiaopang/p/7930508.html

Hadoop实战训练————MapReduce实现PageRank算法的更多相关文章

  1. MapReduce实现PageRank算法(邻接矩阵法)

    前言 之前写过稀疏图的实现方法,这次写用矩阵存储数据的算法实现,只要会矩阵相乘的话,实现这个就很简单了.如果有不懂的可以先看一下下面两篇随笔. MapReduce实现PageRank算法(稀疏图法) ...

  2. MapReduce实现PageRank算法(稀疏图法)

    前言 本文用Python编写代码,并通过hadoop streaming框架运行. 算法思想 下图是一个网络: 考虑转移矩阵是一个很多的稀疏矩阵,我们可以用稀疏矩阵的形式表示,我们把web图中的每一个 ...

  3. 吴裕雄--天生自然HADOOP操作实验学习笔记:pagerank算法

    实验目的 了解PageRank算法 学会用mapreduce解决实际的复杂计算问题 实验原理 1.pagerank算法简介 PageRank,即网页排名,又称网页级别.Google左侧排名或佩奇排名. ...

  4. Hadoop实战5:MapReduce编程-WordCount统计单词个数-eclipse-java-windows环境

    Hadoop研发在java环境的拓展 一 背景 由于一直使用hadoop streaming形式编写mapreduce程序,所以目前的hadoop程序局限于python语言.下面为了拓展java语言研 ...

  5. Hadoop实战3:MapReduce编程-WordCount统计单词个数-eclipse-java-ubuntu环境

    之前习惯用hadoop streaming环境编写python程序,下面总结编辑java的eclipse环境配置总结,及一个WordCount例子运行. 一 下载eclipse安装包及hadoop插件 ...

  6. Hadoop实战2:MapReduce编程-WordCount实例-streaming-python环境

    这是搭建hadoop环境后的第一个MapReduce程序: 基于hadoop streaming的python的脚本: 1 map.py文件,把文本的内容划分成单词: #!/usr/bin/pytho ...

  7. 升级版:深入浅出Hadoop实战开发(云存储、MapReduce、HBase实战微博、Hive应用、Storm应用)

          Hadoop是一个分布式系统基础架构,由Apache基金会开发.用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力高速运算和存储.Hadoop实现了一个分布式文件系 ...

  8. Hadoop应用开发实战(flume应用开发、搜索引擎算法、Pipes、集群、PageRank算法)

    Hadoop是2013年最热门的技术之一,通过北风网robby老师<深入浅出Hadoop实战开发>.<Hadoop应用开发实战>两套课程的学习,普通Java开发人员可以在最快的 ...

  9. 王家林的“云计算分布式大数据Hadoop实战高手之路---从零开始”的第十一讲Hadoop图文训练课程:MapReduce的原理机制和流程图剖析

    这一讲我们主要剖析MapReduce的原理机制和流程. “云计算分布式大数据Hadoop实战高手之路”之完整发布目录 云计算分布式大数据实战技术Hadoop交流群:312494188,每天都会在群中发 ...

随机推荐

  1. Nand Flash基础知识与坏块管理机制的研究

    概述 Flash名称的由来,Flash的擦除操作是以block块为单位的,与此相对应的是其他很多存储设备,是以bit位为最小读取/写入的单位,Flash是一次性地擦除整个块:在发送一个擦除命令后,一次 ...

  2. LINUX 笔记-文件属性相关命令

    chgrp:该命令用于改变文件所属用户组 chown:该命令用于改变文件的所有者 chmod:该命令用于改变文件的权限 -R:进行递归的持续更改,即连同子目录下的所有文件都会更改  

  3. 【ASP.NET MVC 学习笔记】- 11 Controller和Action(2)

    本文参考:http://www.cnblogs.com/willick/p/3331513.html 1.MVC一个请求的发出至action返回结果的流程图如下: 重点是Controller Fact ...

  4. excel中添加拼接行

    Sub 万途标签()Dim iFor i = 1 To Sheets.Count    If Sheets(i).Name = "数据表" Then        If MsgBo ...

  5. Jquery的树插件jqxTreeGrid的使用小结

    一.引入相应的js <link rel="stylesheet" href="../../jqwidgets/styles/jqx.base.css" t ...

  6. Yii2之AR类的坑与总结

    本文主要记录本人从学习yii2到使用yii2开发项目过程中遇到的一些问题以及解决方法和知识点总结. 1. AR类关联哪个数据表? 默认情况下,AR类会根据类名称关联和类名相关的数据表,如:类名为Cou ...

  7. Linux系列教程(五)——Linux链接命令和权限管理命令

    前一篇博客我们讲解了Linux文件和目录处理命令,还是老生常淡,对于新手而言,我们不需要完全记住命令的详细语法,记住该命令能完成什么功能,然后需要的时候去查就好了,用的多了我们就自然记住了.这篇博客我 ...

  8. js解析器的执行原理

    首先看一段代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> < ...

  9. 关联查询一张小表。对性能有影响吗(mysql)

     具体语句  SELECT dfm.id, dfm.member_code, dfm.member_name, dfm.recommend_code, dfm.member_rank, dfm.cre ...

  10. 一个简易内存池(C++)

    做这个内存池主要是为了完成一道面试题,题目在代码中. 代码 #include <iostream> #include<string> #include <list> ...