MapReduce之可能认识的好友

MapReduce之可能认识的好友

背景

目前有大量的社交网络有一个共同的特性,就是可以推荐联系人,基本思想是:如果A是B的好友,而A又是C的好友(也就是说,A是B和C的共同好友,但B和C彼此并不认识),那么社交网络系统喔咕就会向B推荐C,或者向C推荐B。换句话说,如果两个人有一组共同好友,但这两个人本身不是好友,那么社交网络就会推荐他们相互联络

原理

所有用户之间的好友关系可以表示为一个图,在数学中,图是一个有序对 G = ( V , E ) G=(V,E) G=(V,E),包括顶点集 V V V,以及边集 E E E,边集 E E E V V V的两元素子集(也就是说,边与两个顶点相关,这个关系表示为关于这个特定边的一个无序的顶点对)。这种图可以准确的描述无向图或简单图,在MapReduce解决方案中,假设人们之间的好友关系可以用一个无向图表示(如果A是B的一个好友,那么B也是A的好友),社交网络都使用双向好友关系

在这里,图是一个有序对$ G=(V,E) $,其中:

  • V V V是由人(社交网络的用户)构成的一个有限集
  • E E E V V V上的一个二元关系,称为边集,其中的元素称为一个好友关系。

从图论的角度来看,对于一个特定的社交网络的各个人或成员来说,如果他在某个人A的两个度内,会统计这个人与A之间存在多少条不同的路径(包括两个连接边),然后根据路径数对这个列表中所有人评分,显示出A要联系的人,因此目标是为社交网络中的每一个成员计算推荐好友

输入

社交网络图通常非常稀疏,假设输入记录是一个按照名字排列的邻接表,输入中每一行分别包括一个成员的ID,后面是他的直接好友,如下

1 2,3,4,5,6,7,8
2 1,3,4,5,7
3 1,2
4 1,2,6
5 1,2
6 1,4
7 1,2
8 1

这是一个8人的社交网络,因为1 与所有用户都是好友,所以不向1推荐任何人,另一方面,3与1和2是好友,我们可以向3推荐4,5,6,7和8,因为他们是3和1或2的共同好友

输出

输出格式如下:
&lt; U S E R &gt; &lt; : &gt; &lt; F ( M : [ I 1 , I 2 , I 3 , . . . ] ) , . . . &gt; &lt; USER &gt;&lt; : &gt;&lt; F ( M : [ I_1, I_2, I_3, ...]), ...&gt; <USER><:><F(M:[I1,I2,I3,...]),...>

其中:

  • F是推荐给USER的一个好友
  • M是共同好友数
  • I 1 , I 2 , I 3 , . . . I_1 , I_2 , I_3 , . . . I1,I2,I3,... 是共同好友ID
    输出结果如下:

1
2 6(2: [4, 1]),8(1: [1]),
3 4(2: [1, 2]),5(2: [2, 1]),6(1: [1]),7(2: [1, 2]),8(1: [1]),
4 3(2: [2, 1]),5(2: [1, 2]),7(2: [1, 2]),8(1: [1]),
5 3(2: [2, 1]),4(2: [1, 2]),6(1: [1]),7(2: [1, 2]),8(1: [1]),
6 2(2: [1, 4]),3(1: [1]),5(1: [1]),7(1: [1]),8(1: [1]),
7 3(2: [1, 2]),4(2: [2, 1]),5(2: [2, 1]),6(1: [1]),8(1: [1]),
8 2(1: [1]),3(1: [1]),4(1: [1]),5(1: [1]),6(1: [1]),7(1: [1]),
如图所示
在这里插入图片描述

mapper阶段任务

找出直接好友和将来能认识的好友,使用Tuple2来标记是否是直接好友或者可能认识的好友,其中,Tuple2设计如下:

package com.deng.RecommendFriends;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

public class Tuple2 implements Writable, WritableComparable<Tuple2>{
    private String _1;
    private String _2;
    
    public Tuple2(){
    }
    
    public Tuple2(String _1,String _2){
        set(_1,_2);
    }

    private void set(String String, String String1) {
        _1=String;
        _2=String1;
    }

    public String first() {
        return _1;
    }

    public void setFirst(String _1) {
        this._1 = _1;
    }

    public String second() {
        return _2;
    }

    public void setSecond(String _2) {
        this._2 = _2;
    }

    @Override
    public int compareTo(Tuple2 o) {
        return 0;
    }

    @Override
    public void write(DataOutput dataOutput) throws IOException {
        Text.writeString(dataOutput,_1);
        Text.writeString(dataOutput,_2);
    }

    @Override
    public void readFields(DataInput dataInput) throws IOException {
        _1=Text.readString(dataInput);
        _2=Text.readString(dataInput);
    }

}

mapper阶段编码

package com.deng.RecommendFriends;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

public class recommendFriendsMapper extends Mapper<LongWritable, Text,Text, Tuple2> {
    public Tuple2 directFriend;
    public Tuple2 possibleFriend1,possibleFriend2;
    public String[] People; // 所有人ID
    public void map(LongWritable key,Text value,Context context) throws IOException,InterruptedException{
        String line=value.toString();
        People=line.split(" ");
        // 使用切割字符将用户和他的好友分割出来
        String person=People[0],friendTotal=People[1];
        String[] friends=friendTotal.split(",");
        // 将所有的直接好友标记为-1
        for(String friend:friends){
            directFriend= new Tuple2(friend,"-1");
            try {
                context.write(new Text(person),directFriend);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        for(int i=0;i<friends.length;i++){
            for(int j=i+1;j<friends.length;j++){
                possibleFriend1=new Tuple2(friends[j],person);
                try {
                    context.write(new Text(friends[i]),possibleFriend1);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                possibleFriend2=new Tuple2(friends[i],person);
                try{
                    context.write(new Text(friends[j]),possibleFriend2);
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}


reducer阶段任务

这个阶段会找出特定的人与值Tuple2列表的共同好友,如果某个value有一个共同好友,则不做推荐,最后进行格式化输出

reducer阶段编码

package com.deng.RecommendFriends;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;
import java.util.*;

public class recommendFriendsReducer extends Reducer<Text,Tuple2,Text,Text> {
    public void reduce(Text key, Iterable<Tuple2> values, Context context)  {
        Map<String,List> mutualFriends= new HashMap<String, List>();
        for(Tuple2 t2:values){
            String toUser=t2.first();
            String mutualFriend=t2.second();
            // 判断是否是直接好友
            int alreadyFriend=(mutualFriend.compareTo("-1"));
			//如果存在这个用户,则将可能认识的好友添加到链表中,如果不存在,新建链表并将可能认识的好友添加到链表中
            if(mutualFriends.containsKey(toUser)){
                if(alreadyFriend==0){
                    mutualFriends.put(toUser,null);
                }else if(mutualFriends.get(toUser)!=null){
                    mutualFriends.get(toUser).add(mutualFriend);
                }
            }else{
                if(alreadyFriend==0){
                    mutualFriends.put(toUser,null);
                }else{
                    mutualFriends.put(toUser,new ArrayList<String>());
                    mutualFriends.get(toUser).add(mutualFriend);
                }
            }
        }
        String reducerOutput=buildOutput(mutualFriends);
        try{
            context.write(key,new Text(reducerOutput));
        }catch (InterruptedException e){
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   // 格式化输出
    public String buildOutput(Map<String,List> map){
        String output="";
        for(Map.Entry<String, List> entry:map.entrySet()){
            String  k= entry.getKey();
            List v=entry.getValue();
            if(v==null) continue;
            output+=k+"("+v.size()+": "+v+"),";
        }
        return output;
    }
}

驱动程序如下

package com.deng.RecommendFriends;

import com.deng.FileUtil;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class recommendFriendDriver {
    public static void main(String[] args) throws Exception{
        FileUtil.deleteDirs("output");
        Configuration conf=new Configuration();
        String[] otherArgs=new String[]{"input/RecommendFriends.txt","output"};
        Job job=new Job(conf,"recommendFriendDriver");
        job.setJarByClass(recommendFriendDriver.class);
        job.setMapperClass(recommendFriendsMapper.class);
        job.setReducerClass(recommendFriendsReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Tuple2.class);
        FileInputFormat.addInputPath(job,new Path(otherArgs[0]));
        FileOutputFormat.setOutputPath(job,new Path(otherArgs[1]));
        System.exit(job.waitForCompletion(true)?0:1);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值