一、问题描述

这个问题在大数据面试中容易出现,问题如下:

有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M,要求返回频数最高的100个词。

二、思路

  1. 此处1G文件远远大于1M内存,分治法,先hash映射把大文件分成很多个小文件,具体操作如下:读文件中,对于每个词x,取hash(x)%5000,然后按照该值存到5000个小文件(记为f0,f1,...,f4999)中,这样每个文件大概是200k左右(每个相同的词一定被映射到了同一文件中);
  2. 对于每个文件fi,都用hash_map做词和出现频率的统计,取出频率大的前100个词(怎么取?topK问题,建立一个100个节点的最小堆),把这100个词和出现频率再单独存入一个文件;
  3. 根据上述处理,我们又得到了5000个文件,归并文件取出top100。

三、解法

1 产生了一个7G大文件,每行一个[0,100000]区间的整数

2 top n 求解:

  1. 大文件分成小文件:把这个7G左右的大文件,按照读入数字的hashcode值分成1024个小文件(每个文件平均最大就7M左右)
  2. 小文件统计:对每个小文件,可以用堆,hash,内部排序等等方法进行处理;
  3. 小文件的统计结果:再做一次统计 求出出现频数最高的那个数

四、代码

本例使用Java的HashMap

import java.io.*;
import java.util.*;class IP implements Serializable {  private static final long serialVersionUID = -8903000680469719698L;  private String ip = "";  private int count;  public IP(String ip2, Integer integer) {  this.ip = ip2;  this.count = integer;  }  public int getCount() {  return count;  }  public String getIp() {  return ip;  }  public void setCount(int count) {  this.count = count;  }  public void setIp(String ip) {  this.ip = ip;  }  } public class Data {static String fileLoc = "D:\\hadoop\\numsout\\nums.txt";  /** * 将打文件hash成1024个小文件 *  * @throws FileNotFoundException * @throws IOException */  private static void hashToSmallFiles() throws FileNotFoundException, IOException {  BufferedReader br = new BufferedReader(new FileReader(fileLoc));  String ip = "";  HashMap<String, FileWriter> fileWriters = new HashMap<String, FileWriter>();  while ((ip = br.readLine()) != null) {  int tmp = Math.abs(ip.hashCode() % 1024);  String fileName = fileLoc + tmp + ".txt";  FileWriter fw = null;  if (fileWriters.containsKey(fileName)) {  fw = fileWriters.get(fileName);  } else {  fw = new FileWriter(fileName, true);  fileWriters.put(fileName, fw);  }  fw.write(ip + "\n");  }  br.close();  for (FileWriter ff : fileWriters.values()) {  ff.close();  }  }  /*** 利用HashMap<> 统计每个小文件频数最高的ip; * @return 所有小文件的结果 组成List 返回* @throws FileNotFoundException* @throws IOException*/private static List<IP> countEverySmallFile() throws FileNotFoundException, IOException {  List<IP> list = new ArrayList<IP>();  for (int i = 0; i < 1024; i++) {  File file = new File(fileLoc + i + ".txt");  if (file.exists()) {  long startTime = System.currentTimeMillis();  BufferedReader br1 = new BufferedReader(new FileReader(file));  String ip1 = "";  HashMap<String, Integer> hm = new HashMap<String, Integer>();  while ((ip1 = br1.readLine()) != null) {  if (!hm.containsKey(ip1)) {  hm.put(ip1, 1);  } else {  hm.put(ip1, hm.get(ip1) + 1);  }  }  IP[] ips = new IP[hm.size()];  int index = 0;  for (String temp : hm.keySet()) {  ips[index] = new IP(temp, hm.get(temp));  index++;  }  int max = 0;  for (int j = 1; j < ips.length; j++) {  if (ips[j].getCount() > ips[max].getCount()) {  max = j;  }  }  list.add(ips[max]);  long endTime = System.currentTimeMillis();  System.out.println("已经统计文件:" + fileLoc + i + ".txt,用时:" + (endTime - startTime) + " 毫秒");  }  }  return list;  }  /** * 从每个文件出现频率最高ip中,计算出所有文件中出现频率最高ip。 *  * @param list */  private static IP calculateResult(List<IP> list) {  IP[] ips = new IP[list.size()];  ips = list.toArray(ips);  int max = 0;  for (int j = 1; j < ips.length; j++) {  if (ips[j].getCount() > ips[max].getCount()) {  max = j;  }  }  return ips[max];  }  public static void findIp() throws IOException, ClassNotFoundException {  long start = System.currentTimeMillis();  //hashToSmallFiles();  long end1 = System.currentTimeMillis();  System.out.println("将大文件映射成小文件,用时:" + (end1 - start) + "毫秒");System.out.println("将大文件映射成小文件,约:" + (end1 - start) / 60000 + "分钟");// 测试时大约28分钟System.out.println("映射到小文件完成,开始统计每个小文件中出现频率最高的ip");long start1 = System.currentTimeMillis();  List<IP> list = countEverySmallFile();  long end2 = System.currentTimeMillis();  System.out.println("统计所有文件共用时:" + (end2 - start1) + " 毫秒");System.out.println("统计所有文件共用时,约:" + (end2 - start1) / 60000 + "分钟");// 测试时大约13分钟        System.out.println("统计完成,开始计算所有ip中出现频率最高的ip");  IP ip = calculateResult(list);  System.out.println("访问次数最多的ip是:" + ip.getIp() + ":" + ip.getCount()); long end = System.currentTimeMillis(); System.out.println("公用时:" + (end - start) + "毫秒");  }// 产生大文件public static void getBigFile() {    try   {  File file = new File(fileLoc);           if(!file.exists())  {   //如果不存在则创建  file.createNewFile();  System.out.println("文件创建完成,开始写入");               }  FileWriter fw = new FileWriter(file);       //创建文件写入  BufferedWriter bw = new BufferedWriter(fw);  //产生随机数据,写入文件  Random random = new Random(); for(int i=0;i<1024*1024*1024;i++)  {     int randint = (int)Math.floor((random.nextDouble()*100000.0));//产生【0,10000】之间随机数          bw.write(String.valueOf(randint));      //写入一个随机数  bw.newLine();       //新的一行  }  bw.close();  fw.close();  System.out.println("文件写入完成");}   catch (Exception e)   {  e.printStackTrace();  }         }public static void main(String[] args) throws Exception {//getBigFile();// 产生文件:有1024*1024*1024行(1G行),每一行一个数字      findIp();System.out.println("Finished");}
}

五、总结

统计词频在大数据领域应用非常广泛,我们在学习大数据技术中的第一个Demo就是WordCount,所以大家必须把这个思想掌握到位,这样在使用Hadoop中的MapReduce进行数据归并处理时才不至于懵逼。

六、在海量日志数据中,找出出现次数最多的IP地址(扩展)

有一个12G的文本文件,每行记录的是一个IP地址,现要找出这个文件中出现次数最多的那个ip。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;  class IP implements Serializable {  private static final long serialVersionUID = -8903000680469719698L;  private String ip = "";  private int count;  public IP(String ip2, Integer integer) {  this.ip = ip2;  this.count = integer;  }  public int getCount() {  return count;  }  public String getIp() {  return ip;  }  public void setCount(int count) {  this.count = count;  }  public void setIp(String ip) {  this.ip = ip;  }  }  /** * 1、海量日志数据,提取出某日访问百度次数最多的那个IP。 *  * 首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法, * 比如模1000 * ,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map进行频率统计,然后再找出频率最大的几个)及相应的频率 * 。然后再在这1000个最大的IP中,找出那个频率最大的IP *  * @author WilsonPeng 846106184@qq.com *  */
public class No2 {  static String fileLoc = "D:\\bigdata_ip.txt";  public static void findIp() throws IOException, ClassNotFoundException {  long start = System.currentTimeMillis();  hashToSmallFiles();  long end1 = System.currentTimeMillis();  System.out.println("将大文件映射成小文件,用时:" + (end1 - start) + "毫秒");  System.out.println("映射到小文件完成,开始统计每个小文件中出现频率最高的ip");  long start1 = System.currentTimeMillis();  List<IP> list = countEverySmallFile();  long end2 = System.currentTimeMillis();  System.out.println("统计所有文件共用时:" + (end2 - start1) + " 毫秒");  System.out.println("统计完成,开始计算所有ip中出现频率最高的ip");  IP ip = calculateResult(list);  System.out.println("访问次数最多的ip是:" + ip.getIp() + ":" + ip.getCount());  long end = System.currentTimeMillis();  System.out.println("公用时:" + (end - start) + "毫秒");  }  /** * 从每个文件出现频率最高ip中,计算出所有文件中出现频率最高ip。 *  * @param list */  private static IP calculateResult(List<IP> list) {  IP[] ips = new IP[list.size()];  ips = list.toArray(ips);  int max = 0;  for (int j = 1; j < ips.length; j++) {  if (ips[j].getCount() > ips[max].getCount()) {  max = j;  }  }  return ips[max];  }  /** * 统计生成的每一个小文件,返回一个List,这个List的每一项就是每个小文件的统计结果,即每个小文件中出现频率最高的ip和出现次数 *  * @return * @throws FileNotFoundException * @throws IOException */  private static List<IP> countEverySmallFile() throws FileNotFoundException, IOException {  List<IP> list = new ArrayList<IP>();  for (int i = 0; i < 1024; i++) {  File file = new File(fileLoc + i + ".txt");  if (file.exists()) {  long startTime = System.currentTimeMillis();  BufferedReader br1 = new BufferedReader(new FileReader(file));  String ip1 = "";  HashMap<String, Integer> hm = new HashMap<String, Integer>();  while ((ip1 = br1.readLine()) != null) {  if (!hm.containsKey(ip1)) {  hm.put(ip1, 1);  } else {  hm.put(ip1, hm.get(ip1) + 1);  }  }  IP[] ips = new IP[hm.size()];  int index = 0;  for (String temp : hm.keySet()) {  ips[index] = new IP(temp, hm.get(temp));  index++;  }  int max = 0;  for (int j = 1; j < ips.length; j++) {  if (ips[j].getCount() > ips[max].getCount()) {  max = j;  }  }  list.add(ips[max]);  long endTime = System.currentTimeMillis();  System.out.println("已经统计文件:" + fileLoc + i + ".txt,用时:" + (endTime - startTime) + " 毫秒");  }  }  return list;  }  /** * 将打文件hash成1024个小文件 *  * @throws FileNotFoundException * @throws IOException */  private static void hashToSmallFiles() throws FileNotFoundException, IOException {  BufferedReader br = new BufferedReader(new FileReader(fileLoc));  String ip = "";  HashMap<String, FileWriter> fileWriters = new HashMap<String, FileWriter>();  while ((ip = br.readLine()) != null) {  int tmp = Math.abs(ip.hashCode() % 1024);  String fileName = fileLoc + tmp + ".txt";  FileWriter fw = null;  if (fileWriters.containsKey(fileName)) {  fw = fileWriters.get(fileName);  } else {  fw = new FileWriter(fileName, true);  fileWriters.put(fileName, fw);  }  fw.write(ip + "\n");  }  br.close();  for (FileWriter ff : fileWriters.values()) {  ff.close();  }  }  /** * 随机生成ip地址,生成大文本文件 *  * @throws IOException */  private static void generateFile() throws IOException {  FileWriter fw = new FileWriter(fileLoc, true);  for (int i = 0; i < 100000000; i++) {  for (int j = 0; j < 100000000; j++) {  fw.write(generateIp() + "\n");  }  }  fw.close();  System.out.println("done");  }  /** * 随机生成ip地址 *  * @return */  private static String generateIp() {  String ip = "";  for (int i = 0; i < 4; i++) {  int temp = (int) (Math.random() * 255);  ip += temp + ".";  }  return ip.substring(0, ip.length() - 1);  }  public static void main(String[] args) {  try {  findIp();  } catch (Exception e) {  // TODO Auto-generated catch block  e.printStackTrace();  }  }  }  

我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!

参考资料:

  1. https://blog.csdn.net/u014532775/article/details/90146877
  2. https://blog.csdn.net/qq_26437925/article/details/78531179
  3. https://blog.csdn.net/dbt666666/article/details/16974415

程序员的算法课(15)-分治法获取文件中出现频次最高100词相关推荐

  1. c++分治法求最大最小值实现_程序员:算法导论,分治法、归并排序,伪代码和Java实现...

    分治法 我们首先先介绍分治法.分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后在合并这些子问题的解来解决原问题的解. 还是拿扑克牌举例子,假设桌上有两堆牌面朝 ...

  2. 视频教程-程序员必备算法课!(揭秘淘宝购物车算法)-机器学习

    程序员必备算法课!(揭秘淘宝购物车算法) CSDN讲师名下集合了诸多业界知名讲师的公开课内容,内容涵盖人工智能.大数据.区块链等诸多热门技术领域的最佳技术实践,聚合美团.滴滴.AWS.科大讯飞等知名企 ...

  3. 程序员的算法课(2)-排序算法

    术语说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面: 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面: 内排序:所有排序操作都在内存中完成: 外排序:由于数 ...

  4. 程序员的算法趣题Q58: 丢手绢游戏中的总移动距离

    1. 问题描述 2. 解题分析 搜索最短距离,图搜索问题中的最短距离问题,可以用广度优先搜索策略来解决. 2.1 搜索树示意图 搜索树示意图如下: 2.2 算法流程 用一维数组表示当前状态,但是要注意 ...

  5. 程序员的算法课(13)-分治法

    一.什么是分治 [百度百科]分治法((Divide and Conquer))可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后 ...

  6. 哈夫曼编码压缩率计算_程序员的算法课(8)-贪心算法:理解霍夫曼编码

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/ ...

  7. 程序员的算法课(8)-贪心算法:理解霍夫曼编码

    一.一种很贪婪的算法定义 贪心是人类自带的能力,贪心算法是在贪心决策上进行统筹规划的统称. [百度百科]贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体 ...

  8. 程序员的算法课(1)-算法概述

    [算法之美]数据结构+算法=程序. 前言 数据结构只是静态的描述了数据元素之间的关系.高效的程序需要在数据结构的基础上设计和选择算法. 高效的程序=恰当的数据结构+合适的算法 算法(Algorithm ...

  9. 程序员的算法课(5)-动态规划算法

    前言 众所周知,递归算法时间复杂度很高为(2^n),而动态规划算法也能够解决此类问题,动态规划的算法的时间复杂度为(n^2).动态规划算法是以空间置换时间的解决方式. 一.什么是动态规划 动态规划(D ...

最新文章

  1. Apache + Tomcat 负载均衡 session复制
  2. 天翼云从业认证(4.10)网络直播场景解决方案(CDN)
  3. LeetCode 20 有效的括号
  4. PHP单元测试使用手册
  5. akb48_AKB48里历史——六年的终结
  6. openjudge 二叉树 2756
  7. Linux用管道移动文件夹,常用的Linux上的文件管理类命令讲解及演示
  8. 自学前端开发,前端进阶阶段需要学习哪些知识?
  9. Zabbix篇四:钉钉机器人报警
  10. 创建uni-app 微信小程序项目
  11. 步态分析——信度以及效度
  12. 苹果设备(iPhone、iPad、iPod)尺寸及数据 iOS
  13. Java控制流程-for 天朝有一个乞丐姓洪,去天桥要钱 第一天要了1块钱 第二天要了2块钱 第三天要了4块钱 第四天要了8块钱 洪乞丐干10天,收入是多少?
  14. jQuery学习基础理论(二)
  15. OPPO三星苹果影像“硬碰硬”,没想到Reno6 Pro+夜拍竟略胜一筹
  16. 7-40 奥运排行榜
  17. 哥:我要嫁给你!(让人巨感动)
  18. Ritzy Aspen酒店联手Indiegogo使用区块链出售房产
  19. 【万能搜索】万能DFS之全排列(一)——普通算法
  20. 浅谈Jmockit使用

热门文章

  1. HT for Web的HTML5树组件延迟加载技术实现
  2. HTML table 和 div
  3. Kubernetes学习笔记之kube-proxy service实现原理
  4. ZooKeeper(二) idea中使用Java操作zookeeper
  5. spark BlockManager如何实现Broadcast广播
  6. python中如何统计元组中元素的个数_python-无论元素顺序如何,获取列表中的元组数...
  7. 一图解惑SQL JOINS
  8. 区分 Protobuf 中缺失值和默认值
  9. redis的事务不是原子性
  10. 配置配置DruidDataSource