理解BitMap算法的原理及应用

您所在的位置:网站首页 sql索引实现原理及方法 理解BitMap算法的原理及应用

理解BitMap算法的原理及应用

#理解BitMap算法的原理及应用| 来源: 网络整理| 查看: 265

BitMap简介: 什么是 BitMap 算法

所谓 BitMap 就是用一个 bit 位来标记某个元素对应的 value,而 key 即是这个元素。由于采用bit为单位来存储数据,因此在可以大大的节省存储空间。

算法思想

32位机器上,一个整形,比如 int a; 在内存中占32bit,可以用对应的32个bit位来表示十进制的0-31个数,bitmap算法利用这种思想处理大量数据的排序与查询。

 

优点:

效率高,不许进行比较和移位占用内存少,比如N=10000000;只需占用内存为N/8 = 1250000Bytes = 1.2M,如果采用int数组存储,则需要38M多

缺点:

无法对存在重复的数据进行排序和查找

示例:

 

申请一个int型的内存空间,则有4Byte,32bit。输入 4, 2,  1,  3时:

 

输入4:

 

 

输入2:

 

 

输入1:

 

 

输入3:

 

 

思想比较简单,关键是十进制和二进制bit位需要一个 map 映射表,把10进制映射到bit位上。

map映射表

假设需要排序或者查找的总数N=10000000,那么我们需要申请的内存空间为 int a[N/32 + 1].其中a[0]在内存中占32位,依此类推:

 

bitmap表为:

 

a[0] ------> 0 - 31

 

a[1] ------> 32 - 63

 

a[2] ------> 64 - 95

 

a[3] ------> 96 - 127

 

......

 

下面介绍用位移将十进制数转换为对应的bit位

位移转换

(1) 求十进制数 0-N 对应的在数组 a 中的下标

 

index_loc = N / 32即可,index_loc即为n对应的数组下标。例如n = 76, 则loc = 76 / 32 = 2,因此76在a[2]中。

 

(2)求十进制数0-N对应的bit位

 

bit_loc = N % 32即可,例如 n = 76, bit_loc = 76 % 32 = 12

 

(3)利用移位0-31使得对应的32bit位为1

代码示例(c语言)

复制代码

#include #include #define SHIFT 5 #define MASK 0x1F /** * 设置所在的bit位为1 * * T = O(1) * */ void set(int n, int *arr) { int index_loc, bit_loc; index_loc = n >> SHIFT; // 等价于n / 32 bit_loc = n & MASK; // 等价于n % 32 。 h%2^n = h & (2^n -1) arr[index_loc] |= 1 > SHIFT; arr[index_loc] &= 0; } /** * 测试n所在的bit位是否为1 * * T = O(1) * */ int test(int n, int *arr) { int i, flag; i = 1 > SHIFT] & i; return flag; } int main(void) { int i, num, space, *arr; while (scanf("%d", &num) != EOF) { // 确定大小&&动态申请数组 space = num / 32 + 1; arr = (int *)malloc(sizeof(int) * space); // 初始化bit位为0 for (i = 0; i MB=>KB=>Byte=>Bit。如果存储int类型,能存储多少个?我们算下1024 * 1024 * 1024 / 4 约等于2亿6千万个int类型。

从上面的计算的结果来看,在面对海量数据的时候,如果能够采用bit存储,那么在存储空间方面可以大大节省。

在Java里面,其实已经有对应实现的数据结构类java.util.BitSet了,BitSet的底层使用的是long类型的数组来存储元素。

也就是说,

假设我想排序或者查找的总数N=10000,那么,我申请的数组大小如下:

如果是int类型:int temp[]=new int[1+N/32],也就是312+1=313

如果是long类型:long temp[]=new long[1+N/64],也就是156+1=157

这里注意Java里面的整数除法是向下取整的,所以数组长度还需要加上1.

这里以int为例,生成的bitmap表如下:

其实申请一个int一维数组,那么可以当作为列为32位的二维数组。先通过对32进行相除,得到数组下标,然后将十进制转成二进制之后,进行移位计算,用来代表状态。

下面,我们来看一个排序场景,定义一个元素不重复的数组。

输出:

第一行的64,是代表当前的bit数,因为是long类型,而数组里面的最大值没有超过63,所以其实只用一个long类型就能处理上面的排序。

看到这里,如果熟悉排序算法里面计数排序,那么我们就能发现原理非常类似,不同的是使用bitmap排序占用的存储空间更小,但缺点是不支持重复数字。

来看一下关于BitMap算法一些处理大数据问题的场景:

(1)给定40亿个不重复的 int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中。

解法:遍历40亿数字,映射到BitMap中,然后对于给出的数,直接判断指定的位上存在不存在即可。

(2)使用位图法判断整形数组是否存在重复

解法:遍历一遍,存在之后设置成1,每次放之前先判断是否存在,如果存在,就代表该元素重复。

(3)使用位图法进行元素不重复的整形数组排序

解法:遍历一遍,设置状态1,然后再次遍历,对状态等于1的进行输出,参考计数排序的原理。

(4)在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数

解法1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)。

解法2:采用两个BitMap,即第一个Bitmap存储的是整数是否出现,接着,在之后的遍历先判断第一个BitMap里面是否出现过,如果出现就设置第二个BitMap对应的位置也为1,最后遍历BitMap,仅仅在一个BitMap中出现过的元素,就是不重复的整数。

解法3:分治+Hash取模,拆分成多个小文件,然后一个个文件读取,直到内存装的下,然后采用Hash+Count的方式判断即可。

该类问题的变形问题,如已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。 (可以理解为从0-99 999 999的数字,每个数字对应一个Bit位,所以只需要99M个Bit==12MBytes,这样,就用了小小的12M左右的内存表示了所有的8位数的电话)

BitMap的一些缺点:

(1)数据碰撞。比如将字符串映射到 BitMap 的时候会有碰撞的问题,那就可以考虑用 Bloom Filter 来解决,Bloom Filter 使用多个 Hash 函数来减少冲突的概率。

(2)数据稀疏。又比如要存入(10,8887983,93452134)这三个数据,我们需要建立一个 99999999 长度的 BitMap ,但是实际上只存了3个数据,这时候就有很大的空间浪费,碰到这种问题的话,可以通过引入 Roaring BitMap 来解决。

总结

本文主要介绍了BitMap算法的基本原理和应用案例,其本质上是采用了bit位来表示元素状态,从而在特定场景下能够极大的节省存储空间,非常适合对海量数据的查找,判重,删除等问题的处理。

海量数据处理面试题集锦

https://blog.csdn.net/v_july_v/article/details/6685962

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3