用bitmap解决海量电话号码统计问题

原文:http://blog.csdn.net/chris820313/article/details/6795561

为准备某几个大公司的笔试,看到这样一个问题:

已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。

我所拜读的材料里提到了用bitmap解决这种问题,也就是说:

每一个电话号码被映射到了不同的位。

例如:

61234567 对应位 0x00001000

61234568 对应位 0x0001 0000

这样,每一个电话号码也就有了它自己的唯一标识,而且这个标识只占用1位,如果电话号码被统计过,这个位就标识为1,否则标识为0。

最后,通过统计bitmap中被标记为1的个数,就能统计出来不同号码的个数。

已知电话号码是8位,于是相当于范围是从00000000 ---> 9999 9999,这样无非需要 1亿个bit,也就是95.3M的内存。


对这一方法佩服的五体投地的同时,我思考了这样一个问题,这个映射函数怎么搞?每一个电话号码都要对应一个位,怎么让它们彼此对应起来呢?

这个问题其实不难解决:


电话号码00000000为最低位的标记,也就是0x0000.......000001。

那么电话号码00000001就应该是 0x0000.....0000010。

于是电话号码00000002就是0x0000....0000100。


想到这里,我高呼一声,我了个去!电话号码 不就是1这个数字左移位所应该对应的次数么。


于是,我们现在用一个int型的数组来存bitmap,于是每一个元素就是四个字节,对应32位。

现在,用电话号码记作P。

P / 32 就是应当标记该号码的bitmap数组下标,P mod 32 就是1这个数字对应的左移次数,最后,左移完的结果和当前数组下标的记录按位或,这事就结束了……

来段代码就是:

int const nArraySize = 100000000 / (sizeof(int)*8) + 1;  
int Bitmap[nArraySize]={0};  

void MapPhoneNumberToBit(int nNumber)  
{  
  <strong><span style="color:#ff0000;">  Bitmap[nNumber / (8*sizeof(int))] |= 0x00000001<<(nNumber%(8*sizeof(int)));</span></strong>  
}  
int main()  
{  
	
 
    int nNumber ;  
	cin>>nNumber;
    MapPhoneNumberToBit(nNumber);  
    system("pause");  
}  

用bitset更简单:


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


int main()  
{  
	const int size=100000000;//int大概能表示到22个亿;
	bitset<size> *b=new bitset<size>;//因为1亿/(8*1024*1024)==12M超出了栈的大小(1M),所以这里用堆来存储
	(*b).reset();
	int num;
	cout<<(*b).size()<<endl;
	cout<<"输入8位的电话号码"<<endl;
	for(int i=0;i<5;i++)
	{
		cin>>num;
		(*b)[num]=1;
	}

	//显示电话号码
	for(int i=0;i<size;i++)
	{
		
		if((*b)[i]==1)
			cout<<"电话号码是:"<<i<<endl;
	}
	delete []b;
    system("pause");  
}  


要注意栈大小是1M,不够放时要放在堆里。

总结:

Bitmap擅长对应于寻找

某个数据是否在集合中出现过的问题

那几个数据出现在某个集合而没有出现在另一个集合的问题


难点:

映射函数很难搞,类似于上面这种超级简单的映射函数的情况可不是很多。


因此:

bitmap最好用来处理那些数据具有一定规律的(例如电话号码每次都可以移位,再比如IP地址也是可以这样映射)海量数据。杂乱无章不容易一眼看出规律的数据不太适合bitmap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值