Bitmap 海量数据处理

Bitmap:

说明:采用一个bit来标记某个value,可以大大节省存储空间。

优点是占用内存少,比如N为一亿(10000 0000),只需要N/8=1250 0000个byte,约等于12Mb。缺点为不能重复数据进行排序和查找

 

思想:利用一个byte中的8个bit来表示8个数。某数出现,利用映射将对应bit位置1。

比如元素3,在8bit的映射为


再来个元素5,在8bit的映射为


映射表为:

A[0]->0~7;

A[1]->8~15;

A[2]->16~23;

…….

 

具体实现:(例子为32位)

#include 
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
//#define N 10000000
#define N 100
 
int a[1 + N/BITSPERWORD];//申请内存的大小
 
void set(int i) {
//   a[i>>SHIFT] |=  (1<<(i& MASK));
   a[i/BITSPERWORD] |= (0x01<<(i%BITSPERWORD));
}
//clr 初始化所有的bit位为0
void clr(int i) {
//   a[i>>SHIFT] &= ~(1<<(i & MASK));
   a[i/BITSPERWORD] &= ~(0x01<<(i%BITSPERWORD));
}
//test 测试所在的bit为是否为1
int test(int i){
   return a[i>>SHIFT] & (0x01<<(i & MASK));
}
 
int main()
{  int i;
   for (i = 0; i < N; i++)
       clr(i);
   for(i=0;i


a[i/BITSPERWORD]或a[i>>SHIFT],实现i/32,即求出操作bit所在数的下标。

0x01<<(i%BITSPERWORD)或(1<<(i& MASK)),取得该操作bit的具体位置。

 

实例

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

思路:8位数,0000 0000~9999 9999,范围为一亿。需要10^8个bit,12M左右字节。

实现:

int const nArraySize = 100000000 /(sizeof(int)*8) + 1;
int Bitmap[nArraySize];
void MapPhoneNumberToBit(int nNumber){
         Bitmap[nNumber/ (8*sizeof(int))] |= 0x00000001<<(nNumber%(8*sizeof(int)));
}


 

2、2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

思路:用两个bit来表示一个数。0表示整数未出现,1表示出现一次,2表示出现多次。在遍历这些数的时候,如果对应位置的值是0,则将其置为1;如果是1,将其置为2;如果是2,则保持不变。注意控制与置位的分离。

 

实现

#include    
unsigned char flags[1000]; //数组大小自定义     
unsigned get_val(int idx)  {   
//  |    8 bit  |  
//  |00 00 00 00|  //映射3 2 1 0  
//  |00 00 00 00|  //表示7 6 5 4  
//  ……  
//  |00 00 00 00|  
  
    int i = idx/4;  //一个char 表示4个数,  
    int j = idx%4;    
    unsigned ret = (flags[i]&(0x3<<(2*j)))>>(2*j);    
    //0x3是0011 j的范围为0-3,因此0x3<<(2*j)范围为00000011到11000000 如idx=7 i=1 ,j=3 那么flags[1]&11000000, 得到的是|00 00 00 00|  
    //表示7 6 5 4  
   return ret;    
}    
        
unsigned set_val(int idx, unsigned int val)  {    
    int i = idx/4;    
    int j = idx%4;    
    unsigned tmp = (flags[i]&~((0x3<<(2*j))&0xff)) | (((val%4)<<(2*j))&0xff);    
    flags[i] = tmp;    
    return 0;    
}    
unsigned add_one(int idx)    
{    
    if (get_val(idx)>=2) {  //这一位置上已经出现过了??  
        return 1;    
    }  else  {    
        set_val(idx, get_val(idx)+1);    
        return 0;    
    }    
}    
//只测试非负数的情况;    
//假如考虑负数的话,需增加一个2-Bitmap数组.    
int a[]={1, 3, 5, 7, 9, 1, 3, 5, 7, 1, 3, 5,1, 3, 1,10,2,4,6,8,0};    
        
int main()   {    
    int i;    
    memset(flags, 0, sizeof(flags));    
            
    printf("原数组为:");    
    for(i=0;i < sizeof(a)/sizeof(int); ++i)  {    
        printf("%d  ", a[i]);    
        add_one(a[i]);    
    }    
    printf("\r\n");    
        
    printf("只出现过一次的数:");    
    for(i=0;i < 100; ++i)  {    
        if(get_val(i) == 1)    
            printf("%d  ", i);    
        }    
    printf("\r\n");    
      
    return 0;    
}  


 

3、给你一个文件,里面包含40亿个整数,写一个算法找出该文件中不包含的一个整数, 假设你有1GB内存可用。如果你只有10MB的内存呢?

思路:40亿个整数(40*10^8*4byte~16GB)。

A、 内存为1G

显然无法将16G的数据直接放入1G内存,采用bitmap算法,40*10^8bit= 0.5G(大约),满足1G的内存。

B、 内存为10M

如果我们只有10MB的内存,明显使用BitMap也无济于事了。 内存这么小,注定只能分块处理。比如分块的大小是1000,那么第1块的范围就是0到999,第2块的范围就是1000 到1999……

实际上并不保存这些数,而是给每一个块设置一个计数器。 这样每读入一个数,就在它所在的块对应的计数器加1。处理结束之后,我们找到一个块,它的计数器值小于块大小(1000),说明了这一段里面一定有数字是文件中所不包含的。然后我们单独处理这个块即可。

 

参考:

http://blog.csdn.net/hguisu/article/details/7880288

http://hawstein.com/posts/12.3.html

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

你可能感兴趣的:(其他)