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

2023-11-13

一、问题描述

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

有一个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

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • 【vue】websocket封装&使用

    本文章向大家介绍使用websocket 主要包括使用websocket使用实例 应用技巧 基本知识点总结和需要注意事项 具有一定的参考价值 需要的朋友可以参考一下 小程序封装sokect传送门 创建一个websocket js utils
  • C++基础——匿名对象介绍、拷贝对象时的一些编译器优化

    目录 创建对象的几种方式 匿名对象的创建格式 二 编译器对于拷贝对象做出的优化 场景一 检测 优化 检测 场景二 检测 优化 场景三 检测 优化 场景四 优化 检测 整体优化总结 通过C 长时间的学习 我们已经学会了好多通过类定义对象的方法
  • 深入探究Selenium定位技巧及最佳实践

    在使用Selenium进行Web自动化测试时 准确地定位元素是非常重要的一步 Selenium提供了多种元素定位方法 本文将深入探究这八大元素定位方法 帮助读者更好地理解和应用Selenium的定位技巧 1 ID定位 ID是元素在HTML中
  • JAVA的类加载机制

    类加载机制 参考 https blog csdn net zhangliangzi article details 51319033 https www jianshu com p 3556a6cca7e5 https blog csdn
  • 以promise方式调用微信小程序的api

    仅仅适用于部分API需要用到promise方式调用 封装一个方法如下 export const promisify method options gt return new Promise resolve reject gt 将option
  • 用c语言编写lr(1)文法,C语言编写源程序建立LR(1)分析器.pdf

    目 录 前 言2 用C语言编写源程序建立LR 1 分析器3 一 设计目的 要求 算法与设计思想3 1 设计内容3 2 设计要求3 3 设计的基本原理3 1 CLOSURE I 的构造3 2 GO I X 的构造3 3 FIRST集合的构造4
  • Android屏幕适配全攻略(最权威的官方适配指导)

    转载请注明出处 http blog csdn net zhaokaiqiang1992 Android的屏幕适配一直以来都在折磨着我们这些开发者 本篇文章以Google的官方文档为基础 全面而深入的讲解了Android屏幕适配的原因 重要概
  • Java for Web学习笔记(五九):Controller替代Servlet(1)请求匹配

    URL匹配 书写方式 是对DispatcherServlet所匹配的URL进行二次匹配 本例DispatcherServelt的servlet mapping中
  • Echarts隐藏坐标轴

    xAxis show false 不显示坐标轴线 坐标轴刻度线和坐标轴上的文字 axisTick show false 不显示坐标轴刻度线 axisLine show false 不显示坐标轴线 axisLabel show false 不
  • GNU许可证常见问题

    最新在学习开源软件 开源软件的组成最重要的一个就是license 及许可证 开源License在法律上赋予用户相关权利和义务 任何开源应用行为都必须围绕此 游戏规则 进行 其中重点学习了GUN GPL的许可证 本地记录下一个重要的网站 方便
  • 数据库出错提示Duplicate entry * for key *的解决方法

    错误编号 1062 错误提示 查询语句错误 1062 ERR Duplicate entry 16777215 for key PRIMARY SQL INSERT INTO forum attachment SET tid 0 pid 0
  • 揭秘Kaggle神器xgboost

    在 Kaggle 的很多比赛中 我们可以看到很多 winner 喜欢用 xgboost 而且获得非常好的表现 今天就来看看 xgboost 到底是什么以及如何应用 本文结构 什么是 xgboost 为什么要用它 怎么应用 学习资源 什么是
  • CROSS使用说明书 发行和拍卖NFT完整攻略

    鉴于目前去中心化NFT发行和拍卖平台CROSS是英文版本 对部分中国区用户存在操作困难 为了方便投资者和NFT爱好者能及时了解CROSS的相关信息和使用流程 现在CyberVein推出了更加详细的CROSS完整版教程 若还存有疑问 可添加中
  • windows7虚拟拔号服务器,ADSL采用虚拟拨号上网,使用Windows 7如何设置PPPoE宽带连接...

    今天介绍ADSL采用虚拟拨号上网 使用Windows 7操作系统如何设置PPPoE宽带连接 连接网络的方式有很多 现在小伙伴们上网使用的连接方式主要有以下几种 ADSL宽带上网 小区宽带上网 无线局域上网和无线移动上网 其中ADSL宽带上网
  • 使用Python实现二分查找算法及其应用场景详解

    引言 二分查找是一种常用的搜索算法 它可以在有序数组中高效地查找指定元素 本文将详细介绍二分查找算法的原理 实现方法 并探讨其在实际应用场景中的使用 通过深入了解二分查找算法 你将能够更好地理解它的工作原理并灵活应用于各种问题中 目录 引言
  • 像打王者荣耀一样的学习/工作?(转)

    https blog csdn net dataiyangu article details 97544551 depth 1 utm source distribute pc feed none task blog alirecmd 2
  • GET和POST的区别,java模拟postman发post

    题解 空心正方形图案 include
  • MFC对话框中屏蔽Enter键与ESC键

    文章内容无意义 存档用 MFC对话框应用程序中 按下回车键或者ESC键 对话框会自动关闭 原因在于 当用户按下Enter键时 Windows就会自动去查找 输入焦点 落在了哪一个按钮上 获得焦点的按钮的四周将被点线矩形框所包围 如果所有按钮
  • 关于hexo的笔记 以及 常见问题

    在 Hexo 中有两份主要的配置文件 其名称都是 config yml 其中 一份位于站点根目录下 blog config yml 主要包含 Hexo 本身的配置 另一份位于主题目录下 blog themes next config yml
  • 程序员的算法课(15)-分治法获取文件中出现频次最高100词

    一 问题描述 这个问题在大数据面试中容易出现 问题如下 有一个1G大小的一个文件 里面每一行是一个词 词的大小不超过16字节 内存限制大小是1M 要求返回频数最高的100个词 二 思路 此处1G文件远远大于1M内存 分治法 先hash映射把