每日一题--找出40亿个整数中不包含的整数(内存限制)

本文探讨了在大型数据集中查找未出现整数的算法,包括BitMap算法及其在有限内存下的应用策略,适用于大数据处理场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

  • 原文:
    Given an input file with four billion integers, provide an algorithm to generate an integer which is not contained in the file. Assume you have 1 GB of memory.
    FOLLOW UP
    What if you have only 10 MB of memory?
  • 译文:
    给你一个文件,里面包含40亿个整数,写一个算法找出该文件中不包含的一个整数, 假设你有1GB内存可用。
    更进一步,
    如果你只有10MB的内存呢?

分析
首先我们看下40亿个整数大概占用多少内存?

40 * 10^8 * 4B = 16GB

这个明显无法一次性装入内存中。但是,如果我们用计算机中的一位来表示某个数出现与否, 就可以减少内存使用量。比如在一块连续的内存区域,19出现,则将第19位置1。 这个就是Bit Map算法。此时所需内存是:

40 * 10^8 b = 5 * 10^8 B = 0.5GB

即使有负数我们也是可以用1M完成的,代码见代码块1。

但是如果我们只有10MB的内存,明显使用Bit Map也无济于事了。 内存这么小,注定只能分块处理。我们可以将这么多的数据分成许多块, 比如每一个块的大小是2000,那么第一块保存的就是0到1999的数,第2块保存的就是2000 到3999的数……实际上我们并不保存这些数,而是给每一个块设置一个计数器。 这样每读入一个数,我们就在它所在的块对应的计数器加1。处理结束之后, 我们找到一个块,它的计数器值小于块大小(2000), 说明了这一段里面一定有数字是文件中所不包含的。然后我们单独处理这个块即可。【注意该方法仅支持没有重复数字出现的情况!】
接下来我们就可以用Bit Map算法了。我们再遍历一遍数据, 把落在这个块的数对应的位置1(我们要先把这个数通过求模,归约到0到blocksize之间)。 最后我们找到这个块中第一个为0的位,其对应的数就是一个没有出现在该文件中的数,代码见代码块2.

代码
代码块1

#include<iostream>
#include<cstdio>
using namespace std;

int main() {
	freopen("C:/Users/Iris/Desktop/12.3.in", "r", stdin);
	
	//int_len is the num of every int can storage bit
	//and bit_len is how many int we need
	int int_len = sizeof(int) * 8;
	int bit_len = 0xFFFFFFFF / int_len; 
	int* bit = new int[bit_len];
	
	int v;
	while(scanf("%d", &v) != EOF) {
		bit[v / int_len] |= 1 << (v % int_len);
	}
	
	bool bfind = false;
	for(int i = 0; i < bit_len; i++) {
		for(int j = 0; j < int_len; j++) {
			if((bit[i] & (1 << j)) == 0) {
				cout << i*int_len+j << endl;
				bfind = true;
				break;
			}
		}
		if(bfind) {
			break;
		}
	}
	
	delete[] bit;
	fclose(stdin);
	return 0;
}

代码库2:

#include<iostream>
#include<cstdio>
using namespace std;

int main() {
	freopen("C:/Users/Iris/Desktop/12.3.in", "r", stdin);
	
	int total_num = 20000;
	int block_size = 2000;
	int block_num = total_num / block_size;
	int block_count[block_num] = {0};
	int int_len = sizeof(int) * 8;
	int block[block_size/int_len] = {0};
	
	int v;
	while(scanf("%d", &v) != EOF) {
		++block_count[v/block_size]; 
	}
	fclose(stdin);
	
	int i;
	for(i = 0; i < block_num; i++) {
		if(block_count[i] != block_size) {
			break;
		}
	}
	
	freopen("C:/Users/Iris/Desktop/12.3.in", "r", stdin);
	while(scanf("%d", &v) != EOF) {
		if(v >= (block_size * i) && v < (block_size * (i+1))) {
			block[(v % block_size)/int_len] |= (1 << (v % block_size)%int_len);
		}
	}
	
	bool bfind = false;
	for(int j = 0; j < block_size/int_len; j++) {
		for(int k = 0; k < int_len; k++) {
			if((block[j] & (1 << k)) == 0) {
				cout << block_size * i + int_len * j +k << endl;
				bfind = true;
				break;
			}
		}
		if(bfind) {
			break;
		}
	}
	fclose(stdin);
	return 0;
}

结果
在这里插入图片描述
注意
这里之所以写成int block_count[block_num] = {0};而没有用int* = new int[]的形式是因为我用的DevC++编译器在new int[]会有时不会自动初始化为0,但是vs没有这个问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值