首页 > 代码库 > 海量数据处理第二谈-----位图BitMap

海量数据处理第二谈-----位图BitMap


位图的概念:


    在C++中,位图是以位来表示整数的结构,普通的整数一个数需要用4个字节来表示,我们可以换种思想,在整个整数的集合范围内,某个整数存在与否,只有两种情况,在或者不在,那么,我们可以考虑只用一个bit位,来表示该整数存在的状态,从而达到节省内存的目的。


位图实例分析:


    给一个实际的例子,给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中

    我们可以简单计算一下,40亿个整数全部放到内存,需要160亿个字节,粗略计算,大致需要16G的内存,如果我们把每个整数是否出现,转换成用一位来表示它存在的状态,需要5亿个字节,也就是大约500M的内存,对于计算机而言,对内存的节省亦常地重要,这就是位图的一个重要应用。


位图模拟实现:


    首先我们考虑位图的结构,实际上也是对数组的封装,只不过我们这里需要的是以bit位为单位进行存放,每一位的状态只有0和1两种,这里用来表示该整数是否在位图内。

    我们以一个整形为例,在一个整形的空间,存储【1 6 9 4 12 10】这些数,存储结果应该如下:

技术分享

    这个只给出了两个字节,全可以表示上面的6个整数。

    关于位图的底层,这里我们使用vector来模拟实现。

#include <vector>
#include<iostream>
using namespace std;
class BitMap
{
public:
    BitMap(const size_t& range)
    {
        int sz = (range >> 5) + 1;
        _vec.resize(sz);
    }
    void BitSet(const size_t& x)
    {
        int index = x >> 5; // index是x对应位所在的下标
        int num = x % 32; // num是x对应该整形的第多少位
        _vec[index] |= 1 << num;
    }
    void BitReSet(const size_t& x)
    {
        int index = x >> 5; // index是x对应位所在的下标
        int num = x % 32; // num是x对应该整形的第多少位
        _vec[index] &= (~(1 << num));
    }
    bool BitTest(const size_t& x)
    {
        int index = x >> 5; // index是x对应位所在的下标
        int num = x % 32; // num是x对应该整形的第多少位
        return _vec[index] & (1 << num);
    }
protected:
    vector<int> _vec;
};

    位图的实现实际上就是进行一系列的位操作,通过位操作找到该整形对应的位,下面给出一组简单的测试用例
    

void TestBitMap()
{
    BitMap  mp(100);
    mp.BitSet(1);
    mp.BitSet(2);
    mp.BitSet(11);
    mp.BitSet(22);
    cout << "test --<1>" << mp.BitTest(1) << endl;
    cout << "test --<2>" << mp.BitTest(2) << endl;
    cout << "test --<11>" << mp.BitTest(11) << endl;
    cout << "test --<22>" << mp.BitTest(22) << endl<<endl;
    mp.BitReSet(2);
    cout << "test --<1>" << mp.BitTest(1) << endl;
    cout << "test --<2>" << mp.BitTest(2) << endl;
    cout << "test --<11>" << mp.BitTest(11) << endl;
    cout << "test --<22>" << mp.BitTest(22) << endl << endl;
}

    

源码库中的位图:


    在源码库中,有这样一个容器 bitset,和我们这里的bitmap性质基本是一样的,当然,功能要比上面实现的位图大得多。


A bitset is a special container class that is designed to store bits (elements with only two possible values: 0 or 1, true or false, ...).

The class is very similar to a regular array, but optimizing for space allocation: each element occupies only one bit (which is eight times less than the smallest elemental type in C++: char).

Each element (each bit) can be accessed individually: for example, for a given bitset named mybitset, the expression mybitset[3] accesses its fourth bit, just like a regular array accesses its elements.

Because no such small elemental type exists in most C++ environments, the individual elements are accessed as special references which mimic bool elements:


    库中提供了一系列的函数操作,除了set、reset、test之外,常用的还有filp<取反操作>,count<统计位为1的个数>。关于bitset的操作,都包含在

#include <bitset>

的头文件中。

    

位图的分析与扩展:


    位图的确用起来会很方便,但并不是任何情况下都需要使用到位图的,位图通常是为了处理大量数据,内存中不足以存放所有的数字才使用的一种数据结构,因为位图也有着一定的缺陷:

    1> 它的可读性差

    2> 位图在视图节约空间的时候,也伴随着一定的消耗,它要求给最大值和最小值之间的所有数都要占用一个bit位,当数据过于分散而数据量又不至太大的情况,位图其实是一种比较浪费空间的做法。如果最小值为10000,位图开辟出来的前10000个bit位其实就空了出来,没有利用到,之前我们举得例子,40亿个整数,因为无符号整数的最大值就到42.9亿左右,大部分的整数值确定都已经被取到,因此我们采用了位图来实现。

    3> 当位图用来存储有符号整数时,有两种解决方案,一种是我们约定好最小值不再从0开始,所有的计算都需要减去有符号整数的最大值,另一种是这里采用两位来存储一个数,用两位来表示正数、负数、不存在三种状态。

    试想,如果我们要求统计40亿个无符号整数中,出现两次以上的数该如何处理?

。。。。。。

    同样,多加一位标志位,用两个bit位来进行处理,那这样的话,就需要我们自己来实现一个基本的两位为一个单元的位图结构。

    除此之外,位图还可用来排序,判重,当然这里仅仅限于无符号整数,和上一节的哈希一样,受限于整数范围确实是个不好的地方,下一谈会提到字符串哈希算法与布隆过滤器,正是由于字符串哈希算法,才使得这些数据结构得以大范围的使用。

关于哈希算法:
    http://muhuizz.blog.51cto.com/11321490/1870717

                            -----muhuizz整理


本文出自 “暮回” 博客,请务必保留此出处http://muhuizz.blog.51cto.com/11321490/1874719

海量数据处理第二谈-----位图BitMap