在本文中, 我们来介绍一下bit-map,你可别以为这是什么bitmap图像, 那本文要介绍的bit-map是什么呢? 且听我慢慢道来。
坐过火车没? 当你在火车上, 想拉屎的时候, 你得去上厕所啊。 屁颠屁颠爬到厕所处, 发现上面亮了一个灯, 表示里面有人, 你郁闷至极, 但无可奈何。 过了很久, 那个人出来了, 那个灯也就熄灭了, 你就可以上厕所了。
这个灯, 就是计算机中的一个bit, 有两种状态 , 1表示有人在厕所中, 0表示没有人在厕所中。 所以, 从这个意义上说, 一个bit可以标志某事物的一个状态, 当这个bit与某事物状态建立映射后, 我说们, 形成了一个bit到事物状态的map.
我们知道, 一个unsign char有8bit, 也就是说, 一个无符号字符可以标志某个厕所的8个坑位的状态, 下面我们看看程序:
#include <iostream>
using namespace std;
int main(void)
{
unsigned char c;
// 8个坑位都没有人
c = 0;
// 8个坑位都有人
c = 255;
// 第1个(也可以认为是第0个)坑位有人
c = 128;
// 最后两个坑位有人
c = 3;
return 0;
}
看到没? 一个bit标志一个坑, 标志一个事物的状态, 那么一个unsigned char标志着8个事物的状态。 同理, 一个unsigned int标志着32个事物的状态, 其实一个int也可以标志着32个事物的状态。(当然, 这里所说的状态都是二值状态)
现在, 假设有N个事物状态, 那至少需要多少个int来表示呢? 很显然是 N/32 + 1. 假设有100个事物(N=100), 那至少需要4个int (总共128位). 说的有点多了, 直接看程序吧:
#include <iostream>
using namespace std;
#define BIT_INT 32 // 1个int可以标志32个坑
#define SHIFT 5
#define MASK 0x1f
#define N 100
int a[1 + N / BIT_INT]; // 需要1 + N / BIT_INT 个整数来标志N个事物
// 将所有位都初始化为0状态
void setAllZero()
{
memset(a, 0, (1 + N / BIT_INT) * sizeof(int));
}
// 设置第i位为1, 表示有人在厕所里面
void setOne(int i)
{
a[i >> SHIFT] |= (1 << (i & MASK));
}
// 设置第i位为0, 表示没有人在厕所里面
void setZero(int i)
{
a[i >> SHIFT] &= ~(1 << (i & MASK));
}
// 检查第i位的值, 看看有没有人在厕所里面
int getState(int i)
{
return (a[i >> SHIFT] & (1 << (i & MASK))) && 1;
}
int main(void)
{
int bitNumber = (1 + N / BIT_INT) * BIT_INT;
cout << bitNumber << endl;
setAllZero();
int i = 0;
for(i = 0; i < bitNumber; i++)
{
cout << getState(i);
}
cout << endl;
setOne(0);
setOne(1);
setOne(2);
setOne(3);
setOne(4);
for(i = 0; i < bitNumber; i++)
{
cout << getState(i);
}
cout << endl;
setZero(0);
setZero(1);
setZero(2);
for(i = 0; i < bitNumber; i++)
{
cout << getState(i);
}
cout << endl;
return 0;
}
看看结果吧:
128
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
果然, 结果与预期符合。
我们再看程序:
#include <iostream>
#include <set>
using namespace std;
#define BIT_INT 32 // 1个int可以标志32个坑
#define SHIFT 5
#define MASK 0x1f
#define N 100
int a[1 + N / BIT_INT]; // 需要1 + N / BIT_INT 个整数来标志N个事物
// 产生伪随机数
unsigned int getRandom()
{
return rand();
}
// 将所有位都初始化为0状态
void setAllZero()
{
memset(a, 0, (1 + N / BIT_INT) * sizeof(int));
}
// 设置第i位为1, 表示有人在厕所里面
void setOne(int i)
{
a[i >> SHIFT] |= (1 << (i & MASK));
}
// 设置第i位为1, 表示没有人在厕所里面
void setZero(int i)
{
a[i >> SHIFT] &= ~(1 << (i & MASK));
}
// 检查第i位的值, 看看有没有人在厕所里面
int getState(int i)
{
return (a[i >> SHIFT] & (1 << (i & MASK))) && 1;
}
int main(void)
{
int r = N;
int size = 40; // 假设有40个事物(数字), 他们在[0, N-1]这个区间内
// 构造40个互不相等的事物, 实际上, s中的元素是有序的, 但在该程序中, 我们不需要太关注这个
set<int> s;
while(size != s.size())
{
s.insert(getRandom() % r);
}
// 将所有坑位的人赶出来, 初始化
setAllZero();
set<int>::iterator it;
for(it = s.begin(); it != s.end(); it++)
{
setOne(*it); // 将事物*it与其对应的坑位*it联系起来, 当*it这个事物存在时, 对应的坑位*it的状态为1
cout << *it << " ";
}
cout << endl;
int i = 0;
int bitNumber = (1 + N / BIT_INT) * BIT_INT;
for(i = 0; i < bitNumber; i++)
{
cout << i << "对应的坑位状态为:--->" << getState(i) << endl; // 获取坑位状态
}
cout << endl;
return 0;
}
结果为:
0 2 3 4 5 11 12 16 18 21 22 24 26 27 33 34 35 36 38 41 42 45 47 53 58 61 62 64 67 69 71 73 78 81 82 91 92 94 95 99
0对应的坑位状态为:--->1
1对应的坑位状态为:--->0
2对应的坑位状态为:--->1
3对应的坑位状态为:--->1
4对应的坑位状态为:--->1
5对应的坑位状态为:--->1
6对应的坑位状态为:--->0
7对应的坑位状态为:--->0
8对应的坑位状态为:--->0
9对应的坑位状态为:--->0
10对应的坑位状态为:--->0
11对应的坑位状态为:--->1
12对应的坑位状态为:--->1
13对应的坑位状态为:--->0
14对应的坑位状态为:--->0
15对应的坑位状态为:--->0
16对应的坑位状态为:--->1
17对应的坑位状态为:--->0
18对应的坑位状态为:--->1
19对应的坑位状态为:--->0
20对应的坑位状态为:--->0
21对应的坑位状态为:--->1
22对应的坑位状态为:--->1
23对应的坑位状态为:--->0
24对应的坑位状态为:--->1
25对应的坑位状态为:--->0
26对应的坑位状态为:--->1
27对应的坑位状态为:--->1
28对应的坑位状态为:--->0
29对应的坑位状态为:--->0
30对应的坑位状态为:--->0
31对应的坑位状态为:--->0
32对应的坑位状态为:--->0
33对应的坑位状态为:--->1
34对应的坑位状态为:--->1
35对应的坑位状态为:--->1
36对应的坑位状态为:--->1
37对应的坑位状态为:--->0
38对应的坑位状态为:--->1
39对应的坑位状态为:--->0
40对应的坑位状态为:--->0
41对应的坑位状态为:--->1
42对应的坑位状态为:--->1
43对应的坑位状态为:--->0
44对应的坑位状态为:--->0
45对应的坑位状态为:--->1
46对应的坑位状态为:--->0
47对应的坑位状态为:--->1
48对应的坑位状态为:--->0
49对应的坑位状态为:--->0
50对应的坑位状态为:--->0
51对应的坑位状态为:--->0
52对应的坑位状态为:--->0
53对应的坑位状态为:--->1
54对应的坑位状态为:--->0
55对应的坑位状态为:--->0
56对应的坑位状态为:--->0
57对应的坑位状态为:--->0
58对应的坑位状态为:--->1
59对应的坑位状态为:--->0
60对应的坑位状态为:--->0
61对应的坑位状态为:--->1
62对应的坑位状态为:--->1
63对应的坑位状态为:--->0
64对应的坑位状态为:--->1
65对应的坑位状态为:--->0
66对应的坑位状态为:--->0
67对应的坑位状态为:--->1
68对应的坑位状态为:--->0
69对应的坑位状态为:--->1
70对应的坑位状态为:--->0
71对应的坑位状态为:--->1
72对应的坑位状态为:--->0
73对应的坑位状态为:--->1
74对应的坑位状态为:--->0
75对应的坑位状态为:--->0
76对应的坑位状态为:--->0
77对应的坑位状态为:--->0
78对应的坑位状态为:--->1
79对应的坑位状态为:--->0
80对应的坑位状态为:--->0
81对应的坑位状态为:--->1
82对应的坑位状态为:--->1
83对应的坑位状态为:--->0
84对应的坑位状态为:--->0
85对应的坑位状态为:--->0
86对应的坑位状态为:--->0
87对应的坑位状态为:--->0
88对应的坑位状态为:--->0
89对应的坑位状态为:--->0
90对应的坑位状态为:--->0
91对应的坑位状态为:--->1
92对应的坑位状态为:--->1
93对应的坑位状态为:--->0
94对应的坑位状态为:--->1
95对应的坑位状态为:--->1
96对应的坑位状态为:--->0
97对应的坑位状态为:--->0
98对应的坑位状态为:--->0
99对应的坑位状态为:--->1
100对应的坑位状态为:--->0
101对应的坑位状态为:--->0
102对应的坑位状态为:--->0
103对应的坑位状态为:--->0
104对应的坑位状态为:--->0
105对应的坑位状态为:--->0
106对应的坑位状态为:--->0
107对应的坑位状态为:--->0
108对应的坑位状态为:--->0
109对应的坑位状态为:--->0
110对应的坑位状态为:--->0
111对应的坑位状态为:--->0
112对应的坑位状态为:--->0
113对应的坑位状态为:--->0
114对应的坑位状态为:--->0
115对应的坑位状态为:--->0
116对应的坑位状态为:--->0
117对应的坑位状态为:--->0
118对应的坑位状态为:--->0
119对应的坑位状态为:--->0
120对应的坑位状态为:--->0
121对应的坑位状态为:--->0
122对应的坑位状态为:--->0
123对应的坑位状态为:--->0
124对应的坑位状态为:--->0
125对应的坑位状态为:--->0
126对应的坑位状态为:--->0
127对应的坑位状态为:--->0
现在应该完成清楚了bit-map吧, 所谓bit-map, 实际上就是用一个bit去map一个事物的状态(二值状态). bit-map的好处是: 节省空间。
实际上, bit-map在大数据处理中经常会用到, 一些笔试面试题经常考, 后续我们会陆续介绍到。