BitMap算法的说明以及验证

来源:互联网 发布:国美集团美信网络诈骗 编辑:程序博客网 时间:2024/06/05 16:44

核心
1、什么是BitMap算法
2、BitMap的优缺点
3、BitMap的实现
4、BitMap 的比对

1、什么是BitMap算法
在大数据时代,我们如果将一个上亿的整数文件来做排序或者查询使用,这个就将面临一个问题,内存不够用,例如1亿个整数的文件放入内存将会占用380MB(100000000*4/1024/1024)空间.如果是10亿或者更多呢,这个将对内存是一个巨大的挑战。为了解决这个问题,就出现了BitMap算法,那么什么是BitMap算法呢?
32位机器上,一个整形,比如int a; 在内存中占32bit位,可以用对应的32bit位对应十进制的0-31个数,bitmap算法利用这种思想处理大量数据的排序与查询.
说完上面这段话估计还有人不是很懂,那么接下来我们用一个简单的说明来说明一下什么是BitMap。
一个整数在计算中是存储32位的,就是4个字节,每个字节是8位,所以就是32位。
例如
1在计算机里面存储就是0000 0000 0000 0000 0000 0000 0000 0001
32在计算里面存储就是0000 0000 0000 0000 0000 0000 0010 0000
OK如果大家知道上面的存储了,接下来我们做一个思维的转换,就是大家将这个32位看成一个数组,也就是这个数组的长度是32.如果我们定义一个长度是32数组默认情况下每位都是0.现在我们做一个这样的设计,就是在0到31之间,只要出现了哪位数值,我们将这个数组对应的下标的值修改了。例如如果出现了8,那么a[7]=1、如果出现了15那么a[14]=1.那么按照这样的操作,一位整形的数组其实是可以标记0到31的数值的。
假设需要排序或者查找的总数N=100000000,那么我们需要申请内存空间的大小为int a[1 + N/32],其中:a[0]在内存中占32为可以对应十进制数0-31,依次类推:
bitmap表为:
a[0]———>0-31
a[1]———>32-63
a[2]———>64-95
a[3]———>96-127
……….

那么这么下来,一个100000000的整数,实际存储的空间是12MB(100000000/8/1024/1024)

2、BitMap的优缺点
优点:1.运算效率高,不许进行比较和移位;2.占用内存少,比如N=100000000;只需占用内存为N/8=12,500,000Byte=12M。
缺点:所有的数据不能重复。即不可对重复的数据进行排序和查找。

3、BitMap的实现
只需要关注 BitMap_data 这个方法就可以了

package com.xlucas.bank;import java.util.Random;public class BitMap {    public static void main(String[] args) {        BitMap bm=new BitMap();        int ser=1000;        int num=100000000;        System.out.println("序号|"+"类型|"+"消耗内存MB|"+"检索数据|"+"数组下标|"+"范围|"+"耗时|"+"程序耗时|"+"是否存在");        for (int j=1;j<=ser;j++){            Runtime.getRuntime().gc();        bm.serach_data(num,j);        Runtime.getRuntime().gc();        bm.BitMap_data(num, j);         }    }public void serach_data(int num,int ser){    long begin_time=System.currentTimeMillis();    int mem,index=0,time;    float per=0;    Random r=new Random();     int a [] =new int[num+2];     for (int i=0;i<=num;i++)     {         a[i]=r.nextInt(num);     }     mem=(int) ((Runtime.getRuntime().totalMemory()-           Runtime.getRuntime().freeMemory())/1024.0/1024.0);     long start_time=System.currentTimeMillis();     int serach=r.nextInt(num);     boolean flag=false;     for (int j=0;j<num;j++){         if (a[j]==serach)         {              index=j;             per=j/(float)num;             flag=true;             break;         }      }      long end_time=System.currentTimeMillis();     time=(int) (end_time-start_time);     int totaltime=(int) (end_time-begin_time);     System.out.println(ser+"|"+"数组|"+mem+"|"+serach+"|"+index+"|"+per*100+"|"+time+"|"+totaltime+"|"+flag);}public void BitMap_data(int num,int ser){    long begin_time=System.currentTimeMillis();     int[] bits = null;  //定义一个整形数组     bits = new int[(int) (num/32+((num%32)>0?1:0))];//数组的长度等于整数个数除以32向上取整     long length;       final  int[] bitValue = { //定义一个数组,这个数组是存放32位中每位下标为1的值对应的16进制             0x80000000,  //1000 0000 0000 0000 0000 0000 0000 0000            0x40000000,  //0100 0000 0000 0000 0000 0000 0000 0000            0x20000000,              0x10000000,              0x08000000,              0x04000000,              0x02000000,              0x01000000,              0x00800000,              0x00400000,              0x00200000,              0x00100000,              0x00080000,              0x00040000,              0x00020000,              0x00010000,              0x00008000,              0x00004000,              0x00002000,              0x00001000,              0x00000800,              0x00000400,              0x00000200,              0x00000100,              0x00000080,              0x00000040,              0x00000020,              0x00000010,              0x00000008,              0x00000004,              0x00000002,              0x00000001      };     int index=0,time,raddata,intData;    float mem,per=0;    Random r=new Random();     for (int i=0;i<=num;i++)     {         raddata=r.nextInt(num);         //intData = (int) bits[(int) (raddata/32)];         // bits[(int) (raddata/32)] = intData | bitValue[(int) (raddata%32)];    //生成一个随机数,这个随机数存入数组的思想是    //1、先知道这个应该存在那位整形里面 就是这个数除以32(按位操作就是无符号右移5位 2^5=32)    //2、要知道这个应该修改32个下标位对应的值,只需要这个值与31进行与运算就可以了    //3、取出BitValue中对应需要修改下标的16进制的值    //4、在与原数组里面的值进行或 运算就完成了转换    bits[(int) (raddata>>>5)] = (int) bits[(int) (raddata>>>5)] | bitValue[(int) (raddata&31)];     }     //消耗内存大小     mem=(float) ((Runtime.getRuntime().totalMemory()-           Runtime.getRuntime().freeMemory())/1024.0/1024.0);     long start_time=System.currentTimeMillis();     int serach=r.nextInt(num);     //System.out.println(bits[0]);     intData=(int) bits[(int) (serach/32)];       boolean flag=false;     //任意生成一个随机,判断在不在这个里面,其实很简单,只需判断这个数据对应的下标所在的值是不是1就可以了     //取出这个值和bitvalue里面的值进行一个与运算,在无符号移动在到最后。就只剩下标记位了     flag=((intData & bitValue[(int) (serach%32)])>>>(32-serach%32-1))==1?true:false;     long end_time=System.currentTimeMillis();     time=(int) (end_time-start_time);     int totaltime=(int) (end_time-begin_time);     System.out.println(ser+"|"+"Bitmap|"+mem+"|"+serach+"|"+index+"|"+per*100+"|"+time+"|"+totaltime+"|"+flag);}}

4、BitMap 的比对
利用这个1亿数据进行了3000次的运行看能不能得到什么结论。
数值这种模式
运行3000次其中只有1901次中了,也就是java这个random函数在一亿范围内存命中率只有63%

随着检索到下标的增大程序运行的时间将增加
这里写图片描述

而Bitmap
不管在什么时间都是一样的时间0
这里写图片描述

虽然到这里我们了解了这个Bitmap算法,但是这里有一个问题。
在将一亿数值构建BitMap的时候花了6秒,但是在直接赋值给数组的是只花了不到2秒。这个不知道为啥。时间复杂度都是O(n),希望我清楚的大牛帮忙解答一下,谢谢

原创粉丝点击