首页 > 系统相关 >内存受限环境下求大文件Top N词频

内存受限环境下求大文件Top N词频

时间:2023-08-14 18:01:43浏览次数:31  
标签:下求 pq int Top 词频 entry new freq

在大数据时代,处理超大规模数据是算法工程师需要面对的重要问题。本文将以在内存受限环境下,求一个大文件中词频最高的Top N词为例,探讨一种基于堆结构与外部排序的解决方案。

问题描述

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

常规方法及不足

最简单的方法是将文件全部读入内存,统计每个词的频数,最后取频数最大的100个词。但文件大小远超内存限制,无法操作。

一种改进是分批读入文件,每次统计一批词频,最后合并结果。这种方法可以控制内存使用,但需要多轮遍历文件,当文件很大时IO成本非常高。且还需要频繁合并中间结果。

再一种方法是使用外部排序算法。将文件逐行读入,并排序,然后统计词频输出Top N结果。此方法依然需要多轮磁盘IO,效率较低。

基于堆结构的解法

基于上述分析,需要一种可以动态维护topk结果的数据结构。堆可以提供这种能力。

具体地,可以使用一个小根堆,堆的大小固定为N(此处为100)。每次从文件中读取一定大小的词,统计词频保存到一个哈希表中。然后遍历这个哈希表,把词频作为值,词语作为键,逐个插入小根堆。如果堆大小超过N,则移除堆顶最小的元素。重复这一过程,直到文件读取完毕,则堆中的N个元素就是全局topk结果。

堆结构保证了每次只需要维护规模为N的中间结果,而不是全量结果,因此可以控制内存占用。

算法实现

基于小根堆,可以设计一个内存受限的词频统计算法:

  1. 初始化大小为N的小根堆,用于保存topk结果import java.io.*; import java.util.*; public class TopKFrequentWords { private static final int N = 100; // 返回topk结果数 private static final int BATCH_SIZE = 100; // 每批读入行数 public static List<String> topKFrequent(String file, int k, int batchSize) throws IOException { PriorityQueue<WordFreq> pq = new PriorityQueue<>((a, b) -> Integer.compare(a.freq, b.freq)); BufferedReader reader = new BufferedReader(new FileReader(file)); String line; HashMap<String, Integer> freq = new HashMap<>(); while ((line = reader.readLine()) != null) { // 统计每批词频 freq.put(line, freq.getOrDefault(line, 0) + 1); if (freq.size() >= batchSize) { // 加载到堆中 for (Map.Entry<String, Integer> entry : freq.entrySet()) { pq.offer(new WordFreq(entry.getKey(), entry.getValue())); if (pq.size() > k) { pq.poll(); } } freq.clear(); // 清空当前批次结果 } } // 加载最后一个批次 for (Map.Entry<String, Integer> entry : freq.entrySet()) { pq.offer( new WordFreq(entry.getKey(), entry.getValue())); } // 构建结果列表 List<String> topK = new ArrayList<>(); while (!pq.isEmpty()) { topK.add(pq.poll().word); } Collections.reverse(topK); return topK; } public static class WordFreq { String word; int freq; public WordFreq(String word, int freq) { this.word = word; this.freq = freq; } } }这个示例定义了一个小根堆,每次从文件中读取一批数据进行统计,并维护堆中的topk词频结果。最后遍历堆构建结果列表。可以控制每批次处理数据量,保证内存不超限。总结本文针对内存受限环境下的大文件Top N词频问题,给出一种基于堆结构与外部排序的解决方案,主要有以下优点:import java.io.*; import java.util.*; public class TopKFrequentWords { private static final int N = 100; // 返回topk结果数 private static final int BATCH_SIZE = 100; // 每批读入行数 public static List<String> topKFrequent(String file, int k, int batchSize) throws IOException { PriorityQueue<WordFreq> pq = new PriorityQueue<>((a, b) -> Integer.compare(a.freq, b.freq)); BufferedReader reader = new BufferedReader(new FileReader(file)); String line; HashMap<String, Integer> freq = new HashMap<>(); while ((line = reader.readLine()) != null) { // 统计每批词频 freq.put(line, freq.getOrDefault(line, 0) + 1); if (freq.size() >= batchSize) { // 加载到堆中 for (Map.Entry<String, Integer> entry : freq.entrySet()) { pq.offer(new WordFreq(entry.getKey(), entry.getValue())); if (pq.size() > k) { pq.poll(); } } freq.clear(); // 清空当前批次结果 } } // 加载最后一个批次 for (Map.Entry<String, Integer> entry : freq.entrySet()) { pq.offer( new WordFreq(entry.getKey(), entry.getValue())); } // 构建结果列表 List<String> topK = new ArrayList<>(); while (!pq.isEmpty()) { topK.add(pq.poll().word); } Collections.reverse(topK); return topK; } public static class WordFreq { String word; int freq; public WordFreq(String word, int freq) { this.word = word; this.freq = freq; } } }这个示例定义了一个小根堆,每次从文件中读取一批数据进行统计,并维护堆中的topk词频结果。最后遍历堆构建结果列表。可以控制每批次处理数据量,保证内存不超限。总结本文针对内存受限环境下的大文件Top N词频问题,给出一种基于堆结构与外部排序的解决方案,主要有以下优点:
  2. 可以分批处理文件,控制内存占用;
  3. 堆结构可以动态维护流式数据的topk结果;
  4. 只需要一轮外部排序,效率较高。
  5. 可以分批处理文件,控制内存占用;
  6. 堆结构可以动态维护流式数据的topk结果;
  7. 只需要一轮外部排序,效率较高。 当然,如果数据量级更大,还可以考虑MapReduce等分布式计算框架。但本文的方法可以覆盖很多常见场景,并可以扩展解决更多类似问题。
  8. 逐批从文件中读取一定行数的词,统计到哈希表F中
  9. 遍历F,将词频作为值,词语作为键,插入小根堆
  10. 堆大小超过N,则移除堆顶最小元素
  11. 重复步骤2-4,直到文件读完
  12. 堆中的N个元素即为全局topk结果

标签:下求,pq,int,Top,词频,entry,new,freq
From: https://blog.51cto.com/u_16188843/7079674

相关文章

  • 系统和服务通讯(Topshelf+TouchSocket)
    服务不是单独的,总要和其他系统进行信息交互,记录一个解决方案(方便,好用)Topshelf秒建Windows服务推荐一个超轻量级的.NET网络通信框架新建控制台,然后安装Topshelf和TouchSocket,作为服务示例代码:namespaceTestServer{classProgram{staticvoidMain(s......
  • 堆排序(topk 问题)(NB)
    博客地址:https://www.cnblogs.com/zylyehuo/#_*_coding:utf-8_*_#比较排序importrandomdefsift(li,low,high):#堆的向下调整(小根堆)i=lowj=2*i+1tmp=li[low]whilej<=high:ifj+1<=highandli[j+1]<li[j]:......
  • 【linux】命令iftop实时流量监控
    命令iftop实时流量监控iftop是一个命令行系统监控工具用来显示网络连接。默认按照带宽使用排序连接,并且最大带宽消耗排最上方。iftop在命名的网络接口上监听网络流量并显示按照主机对显示当前流量带宽。如果没有指定接口,iftop将监听在外部接口(使用libcap和libncurses)的第一个接......
  • 百望云斩获“2023企业财税服务平台TOP15”奖项
    企业服务业务越来越成为企业发展中不可或缺的一部分。 根据权威数据,企业服务的市场规模在过去五年内年均增长率超过15%。这一点,在投融资领域可能表现得更加迅速也更加明显—— 依据IT桔子、烯牛数据的调研:7月份,企服领域投融资事件增至99件,与6月份相比增加了26起;7月份,投融资事件披......
  • 构建含wkhtmltopdf的jre镜像
    目录官网地址字体下载支持wkhtmlto的镜像Dockerfile构建镜像验证wkhtmltopdf官网地址https://wkhtmltopdf.org/字体下载https://github.com/StellarCN/scp_zh/tree/master/fonts支持wkhtmlto的镜像https://hub.docker.com/r/aantonw/alpine-wkhtmltopdf-patched-qt将......
  • Mac M1基于Docker Desktop部署Gitlab
    一、拉取镜像##这个是gitlab的arm64镜像dockerpullyrzr/gitlab-ce-arm64v8二、配置容器镜像下载完成后,可在DockerDesktop看到镜像点击run,弹出以下界面,配置端口映射和目录挂载后,即可生成一个容器三、启动gitlab容器四、配置Gitlab以下操作需要在Gitlab容器的命令......
  • windows11 docker desktop 安装
      windows11运行docker 下载dockerdesktop https://www.docker.com/ 安装完后会提示要重启电脑 打开dockerdesktop如果报wsl版本软低要更新(docker启动失败) wslkernelversiontoolow打开cmd 运行wsl--update 再次打开dockerdesktop启动成......
  • GitOps 与 DevOps:了解关键差异,为企业做出最佳选择
    在软件开发领域,GitOps和DevOps是加强协作和实现软件交付流程自动化的重要技术。虽然这两种模式都旨在提高软件开发生命周期的效率,但它们的核心原则和实施方式却各不相同。 本篇文章将帮助您了解GitOps和DevOps之间的差异、它们的工作流程,并了解哪种方法更适合您的企业,以......
  • 开发规范TOP10
    1: 单元测试   提高Dao 层方法级别覆盖,包括insert,update,select。    Service 层核心方法覆盖,如对外部环境(网络、服务、中间件等)有依赖,引入Mock实现。   核心业务、核心应用、核心模块的增量代码确保单元测试通过。2.    禁止3B:BigTransaction,B......
  • Docker(.Net6) 环境下使用 Haukcode.WkHtmlToPdfDotNet
     背景: 项目使用的是.Net6+Docker,需要将数据生成PDF保存到第三方文件存储服务器上。引用NuGet:Haukcode.WkHtmlToPdfDotNet 这个插件还是满好用的,支持Windows、Docker.可以直接通过Url转PDF,也可以通过Html字符,生成PDF.官方地址:https://github.com/Hak......