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

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 内存受限环境下求大文件Top N词频

在大数据时代,处理超大规模数据是算法工程师需要面对的重要问题。本文将以在内存受限环境下,求一个大文件中词频最高的Top N词为例,探讨一种基于堆结构与外部排序的解决方案。
问题描述
给定一个1G大小的文件file.txt,里面每行是一个词,词的大小不超过16字节。内存限制为1M。要求返回文件中词频最高的100个词。
常规方法及不足
最简单的方法是将文件全部读入内存,统计每个词的频数,最后取频数最大的100个词。但文件大小远超内存限制,无法操作。
一种改进是分批读入文件,每次统计一批词频,最后合并结果。这种方法可以控制内存使用,但需要多轮遍历文件,当文件很大时IO成本非常高。且还需要频繁合并中间结果。
再一种方法是使用外部排序算法。将文件逐行读入,并排序,然后统计词频输出Top N结果。此方法依然需要多轮磁盘IO,效率较低。
基于堆结构的解法
基于上述分析,需要一种可以动态维护topk结果的数据结构。堆可以提供这种能力。
具体地,可以使用一个小根堆,堆的大小固定为N(此处为100)。每次从文件中读取一定大小的词,统计词频保存到一个哈希表中。然后遍历这个哈希表,把词频作为值,词语作为键,逐个插入小根堆。如果堆大小超过N,则移除堆顶最小的元素。重复这一过程,直到文件读取完毕,则堆中的N个元素就是全局topk结果。
堆结构保证了每次只需要维护规模为N的中间结果,而不是全量结果,因此可以控制内存占用。
算法实现
基于小根堆,可以设计一个内存受限的词频统计算法:
初始化大小为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 topKFrequent(String file, int k, int batchSize) throws IOException {
PriorityQueue pq = new PriorityQueue<>((a, b) -> Integer.compare(a.freq, b.freq));
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
HashMap freq = new HashMap<>();
while ((line = reader.readLine()) != null) {
// 统计每批词频
freq.put(line, freq.getOrDefault(line, 0) + 1);
if (freq.size() >= batchSize) {
// 加载到堆中
for (Map.Entry entry : freq.entrySet()) {
pq.offer(new WordFreq(entry.getKey(), entry.getValue()));
if (pq.size() > k) {
pq.poll();
}
}
freq.clear(); // 清空当前批次结果
}
}
// 加载最后一个批次
for (Map.Entry entry : freq.entrySet()) {
pq.offer(
new WordFreq(entry.getKey(), entry.getValue()));
}
// 构建结果列表
List 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 topKFrequent(String file, int k, int batchSize) throws IOException {
PriorityQueue pq = new PriorityQueue<>((a, b) -> Integer.compare(a.freq, b.freq));
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
HashMap freq = new HashMap<>();
while ((line = reader.readLine()) != null) {
// 统计每批词频
freq.put(line, freq.getOrDefault(line, 0) + 1);
if (freq.size() >= batchSize) {
// 加载到堆中
for (Map.Entry entry : freq.entrySet()) {
pq.offer(new WordFreq(entry.getKey(), entry.getValue()));
if (pq.size() > k) {
pq.poll();
}
}
freq.clear(); // 清空当前批次结果
}
}
// 加载最后一个批次
for (Map.Entry entry : freq.entrySet()) {
pq.offer( new WordFreq(entry.getKey(), entry.getValue()));
}
// 构建结果列表
List 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词频问题,给出一种基于堆结构与外部排序的解决方案,主要有以下优点:

  1. 可以分批处理文件,控制内存占用;
  2. 堆结构可以动态维护流式数据的topk结果;
  3. 只需要一轮外部排序,效率较高。
    可以分批处理文件,控制内存占用;
    堆结构可以动态维护流式数据的topk结果;
    只需要一轮外部排序,效率较高。
    当然,如果数据量级更大,还可以考虑MapReduce等分布式计算框架。但本文的方法可以覆盖很多常见场景,并可以扩展解决更多类似问题。
    逐批从文件中读取一定行数的词,统计到哈希表F中
    遍历F,将词频作为值,词语作为键,插入小根堆
    堆大小超过N,则移除堆顶最小元素
    重复步骤2-4,直到文件读完
    堆中的N个元素即为全局topk结果
    ```js
    AI绘画资料包
    https://pan.xunlei.com/s/VN_qC7kwpKFgKLto4KgP4Do_A1?pwd=7kbv#

https://yv4kfv1n3j.feishu.cn/docx/MRyxdaqz8ow5RjxyL1ucrvOYnnH

```

目录
相关文章
|
6月前
|
Unix 程序员 Linux
【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域
【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域
182 0
|
2天前
|
存储 缓存 Java
释放C盘空间:释放Windows休眠文件和关闭虚拟内存
在 Windows 11 专业版中,可以通过以下步骤来释放休眠文件(Hibernate File),以释放磁盘空间。休眠文件是系统休眠(Hibernate)功能所需要的文件,它保存了系统的当前状态,以便在休眠状态下恢复。如果你不使用休眠功能,如果因为C盘空间不足,可以考虑释放这个文件来腾出磁盘空间。
4885 0
|
2天前
|
缓存 Linux
linux性能分析之内存分析(free,vmstat,top,ps,pmap等工具使用介绍)
这些工具可以帮助你监视系统的内存使用情况、识别内存泄漏、找到高内存消耗的进程等。根据具体的问题和需求,你可以选择使用其中一个或多个工具来进行内存性能分析。注意,内存分析通常需要综合考虑多个指标和工具的输出,以便更好地理解系统的行为并采取相应的优化措施。
32 6
|
2天前
|
存储 缓存 监控
深入解析linux内存指标:快速定位系统内存问题的有效技巧与实用方法(free、top、ps、vmstat、cachestat、cachetop、sar、swap、动态内存、cgroops、oom)
深入解析linux内存指标:快速定位系统内存问题的有效技巧与实用方法(free、top、ps、vmstat、cachestat、cachetop、sar、swap、动态内存、cgroops、oom)
222 0
|
2天前
|
人工智能 算法 BI
【经典问题】给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
【1月更文挑战第26天】【经典问题】给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
|
2天前
|
存储 编译器 C语言
learn_C_deep_10 extern在多文件下的理解、struct 关键字的理解与柔性数组、union 的内存级布局理解、enum 关键字的基本理解、typedef 的理解与分类、关键字总结
learn_C_deep_10 extern在多文件下的理解、struct 关键字的理解与柔性数组、union 的内存级布局理解、enum 关键字的基本理解、typedef 的理解与分类、关键字总结
|
2天前
|
移动开发 安全 图形学
如何绕过某讯手游保护系统并从内存中获取Unity3D引擎的Dll文件
如何绕过某讯手游保护系统并从内存中获取Unity3D引擎的Dll文件
30 0
|
2天前
|
监控 Linux BI
Linux命令之top命令查看服务器CPU与内存占用
Linux命令之top命令查看服务器CPU与内存占用
251 0
|
5月前
|
移动开发 安全 图形学
如何绕过某讯手游保护系统并从内存中获取Unity3D引擎的Dll文件
通过动态分析了它的保护方法,通过改源码刷机的方法绕过了它的保护方案(也可通过hook libc.so中的execve函数绕过保护),接下来就可以直接使用GameGuardain这个神奇附加上去进行各种骚操作了。这里主要讲一下如何去从内存中获取Assembly-CSharp.dll 和 Assembly-CSharp-fristpass.dll文件。
|
6月前
|
存储 缓存 NoSQL
轻松突破文件IO瓶颈:内存映射mmap技术
轻松突破文件IO瓶颈:内存映射mmap技术