一、在新浪微博、人人网等社交网站上,为了使用户在网络上认识更多的朋友,社交网站往往提供类似“你可能感兴趣的人”、“间接关注推荐”等好友推荐的功能。一直很好奇这个功能是怎么实现的。

其实,社交网站上的各个用户以及用户之间的相互关注可以抽象为一个图。以下图为例:

顶点A、B、C到I分别是社交网站的用户,两顶点之间的边表示两顶点代表的用户之间相互关注。那么如何根据用户之间相互关注所构成的图,来向每个用户推荐好友呢?可能大家都听说过六度人脉的说法,所谓六度人脉是指:地球上所有的人都可以通过五层以内的熟人链和任何其他人联系起来。通俗地讲:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过六个人你就能够认识任何一个陌生人。”这个理论在社交网络中同样成立。

现在我们以上图为例,介绍下如何利用用户之间相互关注所构成的图,来向每个用户推荐好友。首先我们不得不假设的是如果两用户之间相互关注,那么我们认为他们认识或者说是现实中的好友,至少应该认识。假设我们现在需要向用户I推荐好友,我们发现用户I的好友有H、G、C。其中H的好友还有A,G的好友还有 F,C的好友还有B、F。那么用户I、H、G、C、A、B、F极有可能是同一个圈子里的人。我们应该把用户A、B、F推荐给用户I认识。进一步的想,用户 F跟两位I的好友C、G是好友,而用户A、B都分别只跟一位I的好友是好友,那么相对于A、B来说,F当然更应该推荐给用户I认识。

可能你会发现,在上面的分析中,我们使用了用户I的二度人脉作为他的推荐好友,而且我们对用户I的每个二度人脉进行了投票处理,选举出最优推荐。其实,我觉得,二度人脉的结果只能看看某个用户的在社交网站上的人际关系链,而基于投票选举产生的二度人脉才是好友推荐功能中所需要的好友。

另外你也可能已经认识到所谓的N度人脉,其实就是图算法里面的宽度优先搜索。宽度优先搜索的主要思想是From Center To Outer,我们以用户I为起点,在相互关注所构成的图上往外不退回地走N步所能到的顶点,就是用户I的N度好友。

下面是Python写的N度人脉的算法,可以输出某个用户的N度好友,代码详见这里。

下面几点是其与宽度优先搜索的不同之处:

1. 宽度优先搜索搜索的是起始顶点可达的所有顶点,N度人脉不需要,它只需要向外走N步,走到N步的顶点处便停止,不需要再往外走了。

2. 走过N步之后,结果中包含起始顶点往外走1、2……N-1步所能到达的所有顶点,返回结果之前需将这些点删除。

3. 变量pathLenFromStart记录这N步具体的走法。

上诉的算法看似可行,其实在实际中并不适用。社交网站上的用户量至少是千万级别的,不可能把所有用户之间相互关注的关系图放进内存中,这个时候就可以依赖 Hadoop了。下面的实例中,我们的输入是deg2friend.txt,保存用户之间相互关注的信息。每行有两个用户ID,以逗号分割,表示这两个用户之间相互关注即认识。

二度好友的计算需要两轮的MapReduce。第一轮MapReduce的Map中,如果输入是“H,I”,我们的输出是 key=H,value=“H,I”跟key=I,value=“H,I”两条结果。前者表示I可以通过H去发现他的二度好友,后者表示H可以通过I去发现他的二度好友。

根据第一轮MapReduce的Map,第一轮MapReduce的Reduce 的输入是例如key =I,value={“H,I”、“C,I”、“G,I”} 。其实Reduce 的输入是所有与Key代表的结点相互关注的人。如果H、C、G是与I相互关注的好友,那么H、C、G就可能是二度好友的关系,如果他们之间不是相互关注的。对应最上面的图,H与C是二度好友,G与C是二度好友,但G与H不是二度好友,因为他们是相互关注的。第一轮MapReduce的Reduce的处理就是把相互关注的好友对标记为一度好友(“deg1friend”)并输出,把有可能是二度好友的好友对标记为二度好友(“deg2friend”)并输出。

第二轮MapReduce则需要根据第一轮MapReduce的输出,即每个好友对之间是否是一度好友(“deg1friend”),是否有可能是二度好友(“deg2friend”)的关系,确认他们之间是不是真正的二度好友关系。如果他们有deg1friend的标签,那么不可能是二度好友的关系;如果有deg2friend的标签、没有deg1friend的标签,那么他们就是二度好友的关系。另外,特别可以利用的是,某好友对deg2friend标签的个数就是他们成为二度好友的支持数,即他们之间可以通过多少个都相互关注的好友认识。

两轮MapReduce的代码,详见这里。

根据上述两轮的MapReduce的方法,我以部分微博的数据进行了测试,测试的部分结果如下:

通过与我(@Intergret)相互关注的138位好友,两轮的MapReduce向我推荐的二度好友前三位是:2010963993(@可乐要改变),2022127621(@琥珀露珠)和2572979357(@赵鸿泽),他们都是我本科的同学,有很多共同的好友,但我跟他们三目前尚未相互关注,所以推荐结果还算靠谱。

原文链接:http://www.datalab.sinaapp.com/?p=192

二、代码如下:

<pre name="code" class="java">import java.io.IOException;
import java.util.Random;
import java.util.Vector;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
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.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;public class deg2friend {public static class job1Mapper extends Mapper<Object, Text, Text, Text>{private Text job1map_key = new Text();private Text job1map_value = new Text();public void map(Object key, Text value, Context context) throws IOException, InterruptedException {String eachterm[] = value.toString().split(",");if(eachterm[0].compareTo(eachterm[1])<0){job1map_value.set(eachterm[0]+"\t"+eachterm[1]);}else if(eachterm[0].compareTo(eachterm[1])>0){job1map_value.set(eachterm[1]+"\t"+eachterm[0]);}job1map_key.set(eachterm[0]);context.write(job1map_key, job1map_value);job1map_key.set(eachterm[1]);context.write(job1map_key, job1map_value);}} public static class job1Reducer extends Reducer<Text,Text,Text,Text> {private Text job1reduce_key = new Text();private Text job1reduce_value = new Text();public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {String someperson = key.toString();Vector<String> hisfriends = new Vector<String>();for (Text val : values) {String eachterm[] = val.toString().split("\t");if(eachterm[0].equals(someperson)){hisfriends.add(eachterm[1]);job1reduce_value.set("deg1friend");context.write(val, job1reduce_value);}else if(eachterm[1].equals(someperson)){hisfriends.add(eachterm[0]);job1reduce_value.set("deg1friend");context.write(val, job1reduce_value);}}for(int i = 0; i<hisfriends.size(); i++){for(int j = 0; j<hisfriends.size(); j++){if (hisfriends.elementAt(i).compareTo(hisfriends.elementAt(j))<0){job1reduce_key.set(hisfriends.elementAt(i)+"\t"+hisfriends.elementAt(j));job1reduce_value.set("deg2friend");context.write(job1reduce_key, job1reduce_value);}
//                  else if(hisfriends.elementAt(i).compareTo(hisfriends.elementAt(j))>0){
//                      job1reduce_key.set(hisfriends.elementAt(j)+"\t"+hisfriends.elementAt(i));
//                  }   }}} }public static class job2Mapper extends Mapper<Object, Text, Text, Text>{private Text job2map_key = new Text();private Text job2map_value = new Text();public void map(Object key, Text value, Context context) throws IOException, InterruptedException {String lineterms[] = value.toString().split("\t");if(lineterms.length == 3){job2map_key.set(lineterms[0]+"\t"+lineterms[1]);job2map_value.set(lineterms[2]);context.write(job2map_key,job2map_value);}}} public static class job2Reducer extends Reducer<Text,Text,Text,Text> {private Text job2reducer_key = new Text();private Text job2reducer_value = new Text();public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {Vector<String> relationtags = new Vector<String>();String deg2friendpair = key.toString();for (Text val : values) {relationtags.add(val.toString());}boolean isadeg1friendpair = false;boolean isadeg2friendpair = false;int surport = 0;for(int i = 0; i<relationtags.size(); i++){if(relationtags.elementAt(i).equals("deg1friend")){isadeg1friendpair = true;}else if(relationtags.elementAt(i).equals("deg2friend")){isadeg2friendpair = true;surport += 1;}    }if ((!isadeg1friendpair) && isadeg2friendpair){job2reducer_key.set(String.valueOf(surport));job2reducer_value.set(deg2friendpair);context.write(job2reducer_key,job2reducer_value);}}  }public static void main(String[] args) throws Exception {Configuration conf = new Configuration();String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();if (otherArgs.length != 2) {System.err.println("Usage: deg2friend <in> <out>");System.exit(2);}Job job1 = new Job(conf, "deg2friend");job1.setJarByClass(deg2friend.class);job1.setMapperClass(job1Mapper.class);job1.setReducerClass(job1Reducer.class);job1.setOutputKeyClass(Text.class);job1.setOutputValueClass(Text.class);//定义一个临时目录,先将任务的输出结果写到临时目录中, 下一个排序任务以临时目录为输入目录。FileInputFormat.addInputPath(job1, new Path(otherArgs[0]));Path tempDir = new Path("deg2friend-temp-" + Integer.toString(new Random().nextInt(Integer.MAX_VALUE))); FileOutputFormat.setOutputPath(job1, tempDir);if(job1.waitForCompletion(true)){Job job2 = new Job(conf, "deg2friend");job2.setJarByClass(deg2friend.class);FileInputFormat.addInputPath(job2, tempDir);job2.setMapperClass(job2Mapper.class);job2.setReducerClass(job2Reducer.class);FileOutputFormat.setOutputPath(job2, new Path(otherArgs[1]));job2.setOutputKeyClass(Text.class);job2.setOutputValueClass(Text.class);FileSystem.get(conf).deleteOnExit(tempDir);System.exit(job2.waitForCompletion(true) ? 0 : 1);}System.exit(job1.waitForCompletion(true) ? 0 : 1);}}

Hadoop实例:二度人脉与好友推荐相关推荐

  1. 【算法题】MapReduce编程,寻找二度人脉

    这是某资讯APP公司的面试题,考察MapReduce的编程思想. 给定一个人脉关系的文件,从中找到二度人脉.比如给定如下的人脉关系, A B C D E B E F C G G H I J 应输出 A ...

  2. 图数据库Neo4j实现人脉推荐——二度人脉

    "吾尝终日而思矣,不如须臾之所学也:吾尝跂而望矣,不如登高之博见也.登高而招,臂非加长也,而见者远:顺风而呼,声非加疾也,而闻者彰.假舆马者,非利足也,而致千里:假舟楫者,非能水也,而绝江河 ...

  3. PHP实现二度人脉算法

    <?php /**  * 数据库中得到的关注列表  * $str字符串中,每个逗号分隔的记录表示前者关注后者  */ $str = "A-B,A-C,A-D,A-E,A-F,A-G,B ...

  4. 二度人脉推荐(案例)

    - 基于:hadoop2.x集群:HDFS + MapReduce JobFriends Mao01 Resource01 Map02 Resource02 JobFriends package co ...

  5. SDNU1129.多度人脉

    Description 艾斯蒂恩优公司最近打算推出一款社交网站,为了让更多用户之间互加好友,项目经理ZZK准备推出多度人脉功能.我们知道,人脉是我们直接的好友,那么多度人脉就是包括好友的好友以及好友的 ...

  6. LinkedIn领英人脉显示1度、2度、3度、领英会员的意思和区别是什么?

    对于新注册使用LinkedIn领英的人来说,LinkedIn领英人脉的规则一定是最让人头痛的问题之一,因为我刚开始用LinkedIn领英的时候,就是被这个折磨到差点放弃. 但LinkedIn领英又是全 ...

  7. 大家一起讨论一下朋友网的人脉关系算法是怎么实现的

    大家一起讨论一下啊!最短路径? 1.一度人脉:双方直接是好友 2.二度人脉:双方有一个以上共同的好友,这时朋友网可以计算出你们有几个共同的好友并且呈现数字给你.你们的关系是: 你->朋友-> ...

  8. 如何打造领英朋友圈_有哪些领英快速扩充人脉的技巧?

    为什么在LinkedIn领英上搜到的客户都是显示领英会员(Linkedin Member)?无法向对方发送添加好友邀请?也无法向访问对方的LinkedIn领英个人主页?这是很多人都遇到的难题,也是使用 ...

  9. 让靠谱的人推荐靠谱的人:基于关系链计算的职场社交应用“脉脉”,上线“好友推荐候选人”功能

    发个悬赏.让众人转发一下职位信息.等待猎头推荐候选人上门.达成交易再给猎头一笔报酬--这就是"社会化招聘"了么?总觉得还欠点什么. 至少个人看来,这样的招聘模式只是单纯的" ...

最新文章

  1. Spring Boot 2.x基础教程:使用Spring Data JPA访问MySQL
  2. ASP.NETserver控件使用之Reportviewer 报表
  3. python中的loop_django学习笔记之forloop
  4. C/C++语言传参、返回参数知识点讲解
  5. 好风凭借力,送我上青云!
  6. 计算机文化基础的重点,计算机文化基础重点知识(1)
  7. LATEX参考文献添加文章doi号并嵌入超链接+IEEE期刊缩写查询
  8. Java视频特效处理(超全)/PC版美颜相机
  9. java 反射 field.set,java 反射之Field
  10. Linux VPS 免费管理面板推荐
  11. 喜忧参半的电子烟 究竟是不是一个靠谱的创业机会?
  12. Linux------进程概念、进程控制
  13. 怎么追学计算机的女生,怎样去追比较文静,不爱说话的女生!!!!!
  14. 2020最新开发及环境搭建类经典面试题
  15. 解决(CRON) info (No MTA installed, discarding output)
  16. Binary Search Tree(二叉搜索树、二叉查找树、二叉排序树)
  17. 给iOS App减肥
  18. 离职了半年了,大家觉得我为啥离职呢?
  19. OpenCV学习笔记(三)——图像像素(图像的最大(小)值、均值、标准差、比较运算、逻辑运算、图像二值化)
  20. OpenCV如何叠加大小不同的图片

热门文章

  1. 中北信商2019年计算机考试题,中北信商高数习题答案.doc
  2. Go-关键字defer、panic、recover详解
  3. A40i-linux 扩展文件系统分区
  4. vue + Three.js实现3d动画效果
  5. PyQt5快速开发与实战 10.1 获取城市天气预报
  6. 光大证券毁在一个笨蛋程序员手里 2013-08-31
  7. 阿里云linux好用不了,十个你可能不曾用过的Linux命令!巨好用!
  8. D1. Great Vova Wall (Version 1)-匹配栈
  9. cad图纸无法复制到剪贴板解决方法
  10. 帆软如何把两个表格分页显示