Bitmap 海量数据处理

来源:互联网 发布:pdf sql pan 编辑:程序博客网 时间:2024/05/29 08:50

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 <stdio.h>#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位为0void clr(int i) {//   a[i>>SHIFT] &= ~(1<<(i & MASK));   a[i/BITSPERWORD] &= ~(0x01<<(i%BITSPERWORD));}//test 测试所在的bit为是否为1int 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<N;i+=4)       set(i);   for (i = 0; i < N; i++)       if (test(i))           printf("%d ", i);   return 0;}


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<memory.h>    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

0 0