一篇文章搞懂bitmap算法(待更新)

BitMap算法

  • 热点
  • 什么是BitMap算法
    • 问题
    • 解法
    • BitMap算法
  • BitMap算法举例
    • 简单的排序问题
    • 海量数据的查找问题
  • 针对第二个问题

热点

BitMap算法,一直是面试的热点问题,当然它也是在海量数据进行快速查找,判重,删除的基本方法。

什么是BitMap算法

问题

在介绍bitmap算法之前,先来看下面几个与bitmap有关的问题
1. 给40亿个不重复的int的整数,没排过序的,然后在给一个数,如何快速判断这个数是否在哪40亿个数当中。
2. 在2.5亿个整数中找出不重复的整数(内存不足以容纳这2.5亿个整数)

解法

下面针对第一个问题分别给出不同的解法:

  1. 使用数据结构存储数据,然后进行查找
    对于在无序的数据中查找某个数这个问题,刚入门的同学最先想到的可能就是将这些数据都存入内存中,然后进行查找。查找的方法无非就是遍历或者是使用Hash映射。选用的数据结构差不多就是list、map或者set。
    当然在机器内存足够大的情况下,这些方法都是可以正确运行的。可以计算下将40亿个unsigned int装入内存中需要耗费的空间大小。假设在一个32位的平台上,一个整数也就占用4字节的空间,即40亿个int整数要耗费160亿个字节,大约需要16GB的空间。而且进行数据查找是的时间复杂度和设计合理的hash映射函数也将成为一个难点。
    当然有聪明的同学可能会说,可以使用外存查找的算法啊。完全不用将数据都存入内存中。可以当然可以,但是磁盘IO时间的消费将远高于内存计算所用的时间。所以这样产生的时间成本是无法接受的。
  2. 使用分布式的方法
    对于上面的问题,一些学过大数据或者分布式相关知识的同学可能会想到使用分布式的方法。假设每台机器的单个可用于这个计算任务的内存是2.5个G可以使用7台机器同时对不同部分的数据进行查找,然后将查找的结果进行汇总就可以啦。
    当然,这是一个不错的设计思想,可以在秒级别上处理问题,但它不是一个聪明的方法,单个机器在解决问题时,仍然很暴力。

BitMap算法

通过上面对海量整数查找问题解决方法的讨论可以看出:这些方法都想尽可能的将数据全部装入内存中,然后进行查找。当装不下时,便使用多个机器或者是多次装入。
下面介绍一种新的算法,使用这种算法可以轻松的将40亿个整数装入内存中,且不用耗费太多的内存空间。同时它可以实现快速查找。
bitmap,即用一个bit位来标记某个元素对应的Value,而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面可以大大节省。

BitMap算法举例

简单的排序问题

假设要对0-7内的5个元素排序(4,7,2,5,3)。要表示8个数,我们只需要8个bit(1Bytes),可以表示(0-7)。可以先开辟一个8bit空间,并将所有的位置设置为0。如图1所示:
图1
然后可以遍历待排序的5个元素,首先第一个元素是4,那么可以把4对应的位置设置为1(p+(i/8)|(0x01<<(i%8)))。这里有一个需要注意的问题,即大端和小端问题,这个在网络编程中会有讨论。因为是从0开始的,所以会将第五位置为1,如图2所示:
图2
重复上面步骤处理完所有的元素后,内存中bit位的状态如图3所示:
图3
之后,仅需要遍历一边Bit区域,将改位是1的位的编号输出(2,3,4,5,7),这样就达到了排序的目的。
talk is cheap,show me the code

#include
#define BYTESIZE 8 //定义每个Byte中有8个bit位
void SetBit(char *p,int posi)
{
	for(int i=0;i<(posi/BYTESIZE);i++)
	{
		p++;
	}
	*p=*p|(0x01<<(posi%BYTESIZE));	 //将该bit位赋值为1
	return}
void BitMapSortDemo(){
	//为了简单起见,暂时不考虑负数
	int num[]={3,5,2,10,6,12,8,14,9};
	//bufferLen这个值时根据待排序的数据中最大值确定的
	//待排序中的最大值是14,因此只需要2个Bytes(16个Bit)
	const int BufferLen=2;
	char *pBuffer=new char[BufferLen];
	//将所有的Bit位置为0
	memset(pBuffer,0,BufferLen);
	for(int i=0;i<9;i++)
	{
		//将相应Bit位上置为1
		SetBit(pBuffer,num[i]);
	}
	//输出排序结果
	for(int i=0;i<BufferLen;i++){  //每次处理一个字节
		for(int j=0;j<BYTESIZE;j++){  //每次处理字节中的每个Bit位
			//判断该位置上是否为1,进行输出
			//首先得到该第j位的掩码(0x01<
			//和此掩码作与操作,最后判断掩码是否和处理后的结果相同
			if((*pBuffer&(0x01<<j))==(0x01<<j))
			{
				printf("%d ",i*BYTESIZE+j);
			}
		}
		pBuffer++;
	}
}
int _tmain(int argc,_TCHAR* argv[])
{
	BitMapSortDemo();
	return 0;
}

程序的运行结果如图2所示:
图4

海量数据的查找问题

通过上面简单排序的小栗子,可以对bitmap算法有一个初步的认识。下面将进一步说明bitmap是如何解决在40亿个整数中快速查找的问题。其实也不是很难嘛。
由于存储的是int整数,一般情况下需要关注32位int的范围,总共是2[^32],大概是42亿多点。所以可以申请2的32次方个位,这样就可以覆盖整个整数的范围。从0开始往后每一个Bit位都对应唯一的一个整数。当这个整数存在时,相应的bit位就为1,否则便是0。
当要查看一个整数是否存在于这40亿个数中时,只需要将找到这个数在内存中对应的Bit位,然后检查这个Bit位是否为1即可,可以实现快速查找。
当然在存储方面这种存储方式也大大减少了存储的空间。2的32次方个位,相当于2的29次方个字节,,需要500MB的存储空间,极大的减少了存储的消耗。

针对第二个问题

如何在2.5亿个整数中找出不重复的整数(内存不足容纳2.5亿个整数)

  1. 采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示出现多次,11无意义)进行,共需内存2[^32] *2bit=1GB.然后扫描着2.5亿个整数,查看Bitmap中相对应位,如果00则将其变为01,如果是01则将其变成10,如果是10则保持不变。扫描结束后,遍历bitmap,将对应位是01的整数输出即可。
  2. 也可采用与第1题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。

你可能感兴趣的:(算法,大数据)