pagerank算法c语言,在hadoop的map-reduce框架下实现经典的pagerank算法

编程环境:

Ubuntu16.4 uklin

Hadoop3.2.0

openjdk version "1.8.0_191"

spark 2.3.0 ( 集群环境 )

完整代码已经更新至GitHub,欢迎fork~GitHub链接

声明:创作不易,未经授权不得复制转载

statement:No reprinting without authorization

一、算法思想介绍:

PageRank的核心思想其实十分简单,概括如下:

1、如果一个网页被很多其它网页链接到,说明这个网页很重要,它的PageRank值也会相应较高;

2、如果一个PageRank值很高的网页链接到另外某个网页,那么那个网页的PageRank值也会相应地提高。

使用随机浏览模型的PageRank公式:

8f6df13c459e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image.png

也可将(1-d)换为(1-d/N)N为总网页数

二、hadoop上利用mapreduce框架实现pagerank算法:

1、设计思路:

源数据格式为起始网页+\t+用逗号,分割的起始网页所连接指向的其它网页列表,eg:A B,D,F......

所以考虑首先读入一遍全体数据,先初始化每个网页的权重为1.0,并在往后的每一次迭代中更新这个记录每个网页权重pr值,每次将上一轮更新后的pr值表传给下一次迭代计算新的pr值。

具体在map时将每一行源数据拆分,首先读取到A的pr值,而后统计A后一共的出链数目num,将A指向的每个网页名作为一个key,value设为pr/num,表示A对该网页的贡献量,如上例中的一行数据经 过mapper后将得到的是:、、......

而后在reducer端接受到的将是同一个key为网页,value是其它所有网页对它pr值贡献量的值列表,eg:>,所以reducer要做的便是利用公式将列表中值相加得到sum值,而后将sum乘以权重d再加上(1-d)便得到更新后的网页B的pr值:

8f6df13c459e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image.png

2、难点

如何设计一个共享的变量来保存所有网页的pr值:

查询后发现hadoop对数据传递和共享变量没有什么特别好的接口,主要是因为hadoop是基于分布式的节点计算,要想实现数据共享传递主要是依赖对文件的操作,以及自带的Configuration中设置属性值,考虑到频繁的文件io操作带来的低效性,我采用了后一种方法:

//初始化configuration后,使用

conf.set("propertyName“,properyValue);

//在mapper或reducer中,

Configuration conf = context.getConfiguration();

String g = conf.get("propertyName");

补充其它方式:

8f6df13c459e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image.png

而后在每一次迭代前,先读取上一次迭代的输出文件(part-r-00000), 将网页的pr值封装进一个String的字符串中,用“,”进行分割,用lastWeights保存,用在mapper中读取到后进行计算即可,不需要考虑到在每个reducer节点进行遍历更新(会很慢),转换为一次的文件io操作即可,极大增加了运行效率。

三、具体迭代实现的mapper和reducer的代码:

Mapper:

public static class pageMapper extends Mapper{

private static Text keyInfo = new Text();

private static DoubleWritable valueInfo = new DoubleWritable();

private static String[] lastWeightsInfo;

@Override

protected void setup(Context context)

throws IOException, InterruptedException {

try {

//从全局配置获取配置参数

Configuration conf = context.getConfiguration();

lastWeightsInfo = conf.get("lastWeights").split(","); //这样就拿到了

} catch (Exception e) {

e.printStackTrace();

}

}

@Override

protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

//分离邻接链表

String[] line = value.toString().split("\t");

if(line.length==2){

String curPageName = line[0];

String[] outPages = line[1].split(",");

double outNum = outPages.length;

double ww = 0.0;

for(int i = 0;i

if(curPageName.equals(lastWeightsInfo[i])){

ww = Double.valueOf(lastWeightsInfo[i+1].toString())/outNum;

break;

}

}

for (int i=0;i

keyInfo.set(outPages[i]);

valueInfo.set(ww);

context.write(keyInfo,valueInfo);

}

}

}

}

Reducer:

public static class pageReducer extends Reducer{

private static DoubleWritable info = new DoubleWritable();

//private static int totalNum;

/* @Override

protected void setup(Context context)

throws IOException, InterruptedException {

try {

//从全局配置获取配置参数

Configuration conf = context.getConfiguration();

//totalNum = conf.getInt("totalNum", 0);

} catch (Exception e) {

e.printStackTrace();

}

} */

@Override

protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException {

double sum = 0.0;// 统计PR

for (DoubleWritable value : values) {

sum += value.get();

}

sum = 0.85*sum + 0.15;

info.set(sum);

context.write(key, info);

}

}

四、实现标准输出:

标准输出如下:

(recovered_factory,1.3220451459)

(AbboT,1.3123762223)

(Abbess,1.2992692277)

(Martin_Scorsese,1.2820462602)

(Asteropaeus,1.2775443371)

(Best_Adapted_Screenplay,1.2755708599)

(For_Your_Consideration,1.2743701080)

(Whittaker_Chambers,1.2633760889)

(capitalism,1.2620055989)

方案:

额外增加设置一次jop,单独设计mapper和reducer,将每个网页的pr值设定为key,按照降序传递到reducer端,并保留十位小数进行输出,主要利用的是重写比较器WritableComparator:

public static class IntWritableDecreasingComparator extends IntWritable.Comparator {

public int compare(WritableComparable a, WritableComparable b) {

return -super.compare(a, b);

}

public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {

return -super.compare(b1, s1, l1, b2, s2, l2);

}

}

新的Mapper和Reduder:

public static class pageOutMapper extends Mapper{

private static DoubleWritable keyInfo = new DoubleWritable();

private static Text valueInfo = new Text();

@Override

protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

//super.map(key, value, context);

//若A能连接到B,则说明B是A的一条出链

if(!value.toString().equals("")){

String[] line = value.toString().split("\t");

String curPageName = line[0];

double pr = Double.valueOf(line[1]);

DecimalFormat df = new DecimalFormat("0.0000000000");

String Prvalue = df.format(pr);

String vout = "("+curPageName+","+Prvalue+")";

valueInfo.set(vout);

keyInfo.set(pr);

context.write(keyInfo, valueInfo);

}

}

}

public static class pageOutReducer extends Reducer{

private static Text info = new Text();

@Override

protected void reduce(DoubleWritable key, Iterable values, Context context) throws IOException, InterruptedException {

for(Text value:values){

info.set(value.toString());

context.write(NullWritable.get(),info);

}

}

}

五、在spark上完成pagerank

spark下的scala编程简单很多,直接在集群上的spark-shell上几行代码就可以搞定:

8f6df13c459e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image.png

六、实验代码设计漏洞:

1、:在邻接链表右边未出现过的,左边出现,会出现异常,输出少一个page

2、:没有进行检查链接到自己的网页筛选

还可以进一步优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值