1、概述
本文将讲述Bit-Map算法的相关原理,Bit-Map算法的一些利用场景,例如BitMap解决海量数据寻找重复、判断个别元素是否在海量数据当中等问题.最后说说BitMap的特色已经在各个场景的使用性。
2、Bit-Map算法
先看看这样的一个场景:给一台普通PC,2G内存,要求处理一个包含40亿个不重复而且没有排过序的无符号的int整数,给出一个整数,问若是快速地判断这个整数是否在文件40亿个数据当中?
问题思考:
40亿个int占(40亿*4)/1024/1024/1024 大概为14.9G左右,很明显内存只有2G,放不下,所以不可能将这40亿数据放到内存中计算。要快速的解决这个问题最好的方案就是将数据搁内存了,因此如今的问题就在如何在2G内存空间之内存储着40亿整数。一个int整数在java中是占4个字节的即要32bit位,若是可以用一个bit位来标识一个int整数那么存储空间将大大减小,算一下40亿个int须要的内存空间为40亿/8/1024/1024大概为476.83 mb,这样的话咱们彻底能够将这40亿个int数放到内存中进行处理。
具体思路:
1个int占4字节即4*8=32位,那么咱们只须要申请一个int数组长度为 int tmp[1+N/32]便可存储完这些数据,其中N表明要进行查找的总数,tmp中的每一个元素在内存在占32位能够对应表示十进制数0~31,因此可获得BitMap表:
tmp[0]:可表示0~31
tmp[1]:可表示32~63
tmp[2]可表示64~95
.......
那么接下来就看看十进制数如何转换为对应的bit位:
假设这40亿int数据为:6,3,8,32,36,......,那么具体的BitMap表示为:html
那么怎么快速定位它的索引呢。若是找到它的索引号,又怎么定位它的位置呢。Index(N)表明N的索引号,Position(N)表明N的所在的位置号。java
Index(N) = N/8 = N >> 3;
Position(N) = N%8 = N & 0x07;
add方法:git
public void add(int num){ // num/8获得byte[]的index int arrayIndex = num >> 3; // num%8获得在byte[index]的位置 int position = num & 0x07; //将1左移position后,那个位置天然就是1,而后和之前的数据作|,这样,那个位置就替换成1了。 bits[arrayIndex] |= 1 << position; }
code:算法
public class BitMap { //保存数据的 private byte[] bits; //可以存储多少数据 private int capacity; public BitMap(int capacity){ this.capacity = capacity; //1bit能存储8个数据,那么capacity数据须要多少个bit呢,capacity/8+1,右移3位至关于除以8 bits = new byte[(capacity >>3 )+1]; } public void add(int num){ // num/8获得byte[]的index int arrayIndex = num >> 3; // num%8获得在byte[index]的位置 int position = num & 0x07; //将1左移position后,那个位置天然就是1,而后和之前的数据作|,这样,那个位置就替换成1了。 bits[arrayIndex] |= 1 << position; } public boolean contain(int num){ // num/8获得byte[]的index int arrayIndex = num >> 3; // num%8获得在byte[index]的位置 int position = num & 0x07; //将1左移position后,那个位置天然就是1,而后和之前的数据作&,判断是否为0便可 return (bits[arrayIndex] & (1 << position)) !=0; } public void clear(int num){ // num/8获得byte[]的index int arrayIndex = num >> 3; // num%8获得在byte[index]的位置 int position = num & 0x07; //将1左移position后,那个位置天然就是1,而后对取反,再与当前值作&,便可清除当前的位置了. bits[arrayIndex] &= ~(1 << position); } public static void main(String[] args) { BitMap bitmap = new BitMap(100); bitmap.add(7); System.out.println("插入7成功"); boolean isexsit = bitmap.contain(7); System.out.println("7是否存在:"+isexsit); bitmap.clear(7); isexsit = bitmap.contain(7); System.out.println("7是否存在:"+isexsit); } }
每一个byte存8个数字,相对于int类型来讲,节省了32倍的存储空间数组
存储数据范围,就上面的例子来讲,上面bits数组的长度为13,那么总共能够存储(13*8)104个数字,分别是(0~103)优化
上面的代码并无扩容方法,超出范围会报错,this
使用bit数组来表示某些元素是否存在,好比8位电话号码.spa
缺点:.net
若是是比较特殊的数字,好比[1,100000000],那么就会浪费存储空间,好比这个就必需要(100000000>>3)个字节code
针对上面的缺点,谷歌所实现的EWAHCompressedBitmap对bitmap存储空间作了必定的优化
相信的讲解:http://www.sohu.com/a/166661005_479559
问题实例
一、在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数
解法一:采用2-Bitmap(每一个数分配2bit,00表示不存在,01表示出现一次,10表示屡次,11无心义)进行,共需内存2^32 * 2 bit=1 GB内存,还能够接受。而后扫描这2.5亿个整数,查看Bitmap中相对应位,若是是00变01,01变10,10保持不变。所描完过后,查看bitmap,把对应位是01的整数输出便可。
解法二:也可采用与第1题相似的方法,进行划分小文件的方法。而后在小文件中找出不重复的整数,并排序。而后再进行归并,注意去除重复的元素。”
二、给40亿个不重复的unsigned int的整数,没排过序的,而后再给一个数,如何快速判断这个数是否在那40亿个数当中?
解法一:能够用位图/Bitmap的方法,申请512M的内存,一个bit位表明一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。
https://www.jianshu.com/p/6082a2f7df8e
https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/06.07.html
http://blog.51cto.com/zengzhaozheng/1404108
http://blog.csdn.net/h348592532/article/details/45362661