[算法系列之十八]海量資料處理之BitMap
一:簡介
所謂的BitMap就是用一個bit位來標記某個元素對應的Value, 而Key即是該元素。由於採用了bit為單位來儲存資料,因此在儲存空間方面,可以大大節省。
二:基本思想
我們用一個具體的例子來講解,假設我們要對0-7內的5個元素(4,7,2,5,3)排序(這裡假設這些元素沒有重複)。那麼我們就可以採用BitMap的方法來達到排序的目的。要表示8個數,我們就只需要8個bit(1Bytes)。
(1)首先我們開闢1位元組(8bit)的空間,將這些空間的所有bit位都置為0,如下圖:
(2)然後遍歷這5個元素,首先第1個元素是4,那麼就把4對應的位置為1,因為是從零開始的,所以要把第5個位置為1(如下圖):
然後再處理第2個元素7,將第8個位置為1,,接著再處理第3個元素,一直到處理完所有元素,將相應的位置為1,這時候的記憶體的bit位的狀態如下:
(3)然後我們現在遍歷一遍bit區域,將該位是1的位的編號輸出(2,3,4,5,7),這樣就達到了排序的目的。
演算法思想比較簡單,但關鍵是如何確定十進位制的數對映到二進位制bit位的map圖。
三:Map對映
假設需要排序或者查詢的總數N=10000000。
BitMap中1bit代表一個數字
1個int = 4Bytes = 4*8bit = 32 bit,那麼N個數需要N/32 int空間。所以我們需要申請記憶體空間的大小為int a[1 + N/32],其中:a[0]在記憶體中佔32為可以對應十進位制數0-31,依次類推:
BitMap表為:
a[0] ---------> 0-31
a[1] ---------> 32-63
a[2] ---------> 64-95
a[3] ---------> 96-127
..........
那麼十進位制數如何轉換為對應的bit位,下面介紹用位移將十進位制數轉換為對應的bit位。
申請一個int一維陣列,那麼可以當作為列為32位的二維陣列。
a[0]
a[1]
a[2]
a[3]
a[i] ……………………………….
a[n]
例如:
十進位制1 在a[0]中,位置如下圖:
十進位制31 在a[0]中,位置如下圖:
十進位制32 在a[1]中,位置如下圖:
十進位制33 在a[1]中,位置如下圖:
通過上圖分析得出通過以下幾步將十進位制數如何轉換為對應的bit位:
(1)求十進位制數在對應陣列a中的下標
十進位制數0-31,對應在陣列a[0]中,32-63對應在陣列a[1]中,64-95對應在陣列a[2]中………
分析得出:對於一個十進位制數n,對應在陣列a[n/32]中
例如n=11,那麼 n/32=0,則11對應在陣列a中的下標為0,n=32,那麼n/32=1,則32對應在陣列a中的下標為1,n = 106,那麼n/32 = 3,則106對應陣列a中的下標為3。
(2)求十進位制數在對應陣列a[i]中的下標
例如十進位制數1在a[0]的下標為1,十進位制數31在a[0]中下標為31,十進位制數32在a[1]中下標為0。
在十進位制0-31就對應0-31,而32-63則對應也是0-31,即給定一個數n可以通過模32求得在對應陣列a[i]中的下標。
分析得出:對於一個十進位制數n,對應在陣列a[n/32][n%32]中
(3)移位
對於一個十進位制數n,對應在陣列a[n/32][n%32]中,但陣列a畢竟不是一個二維陣列,我們通過移位操作實現置1。
a[n/32] |= 1 << n % 32
移位操作:
a[n>>5] |= 1 << (n & 0x1F)
n & 0x1F 保留n的後五位 相當於 n % 32 求十進位制數在陣列a[i]中的下標
/*--------------------------------
* 日期:2015-02-07
* 作者:SJF0115
* 題目: BitMap
* 部落格:
------------------------------------*/
#include <iostream>
#include <vector>
using namespace std;
#define N 1000000000
//申請記憶體的大小
int a[1 + N/32];
// 設定所在的bit位為1
void BitMap(int n){
// row = n / 32 求十進位制數在陣列a中的下標
int row = n >> 5;
// n & 0x1F 保留n的後五位
// 相當於 n % 32 求十進位制數在陣列a[i]中的下標
a[row] |= 1 << (n & 0x1F);
}
// 判斷所在的bit為是否為1
bool Exits(int n){
int row = n >> 5;
return a[row] & ( 1 << (n & 0x1F));
}
void Show(int row){
cout<<"BitMap點陣圖展示:"<<endl;
for(int i = 0;i < row;++i){
vector<int> vec;
int tmp = a[i];
for(int i = 0;i < 32;++i){
vec.push_back(tmp & 1);
tmp >>= 1;
}//for
cout<<"a["<<i<<"]"<<"->";
for(int i = vec.size()-1;i >= 0;--i){
cout<<vec[i]<<" ";
}//for
cout<<endl;
}//for
}
int main(){
int num[] = {1,5,30,32,64,56,159,120,21,17,35,45};
for(int i = 0;i < 12;++i){
BitMap(num[i]);
}//for
int row = 5;
Show(5);
/*if(Exits(n)){
cout<<"該數字已經存在"<<endl;
}//if
else{
cout<<"該數字不存在"<<endl;
}//else*/
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
應用範圍
可以運用在快速查詢、去重、排序、壓縮資料等。
c++版BitMap
在C++中提供了bitset這種集合,專門用來進行位操作,因此實現起來比較容易。
具體參考:STL bitset用法總結
擴充套件
Bloom filter可以看做是對BitMap的擴充套件
布隆過濾器具體參考:[算法系列之十]大資料量處理利器:布隆過濾器
具體應用
(1)已知某個檔案內包含一些電話號碼,每個號碼為8位數字,統計不同號碼的個數。
思路
電話號碼每個為8位數字,最小電話號碼為00 000 000,最大99 999 999,一共100 000 000個電話號碼,
100 000 000bit = 12 500 000Bytes = 12MBytes 故需要12Mbytes的記憶體即可儲存所有的電話號碼。
可以理解為從00 000 000 - 99 999 999的數字,每個數字對應一個bit位,所以只需要12MBytes,這樣,就用了小小的12M左右的記憶體表示了所有的8位數的電話。
程式碼
/*-------------------------------------
* 日期:2015-03-27
* 作者:SJF0115
* 題目: 統計電話號碼個數
* 來源:海量資料
* 部落格:
------------------------------------*/
#include <iostream>
#include <vector>
#include <time.h>
using namespace std;
// 給定的電話號碼個數
#define NUM 1000
// 電話號碼的最小值
#define MIN 10000000
// 電話號碼的最大值
#define MAX 99999999
// 電話號碼總共個數
#define N (MAX - MIN + 1)
// 置1
void SetBitMap(int bitMap[],int num){
num -= MIN;
//bitMap[num/32] |= 1 << num % 32;
bitMap[num >> 5] |= (1 << (num % 32));
}//void
// 置0
void ClearBitMap(int bitMap[],int num){
bitMap[num >> 5] &= ~(1 << (num % 32));
}//void
// 獲取
bool GetBitMap(int bitMap[],int num){
return bitMap[num >> 5] & (1 << (num % 32));
}//void
// 統計電話號碼個數
int PhoneCount(int phone[],int n,int bitMap[]){
for(int i = 0;i < n;++i){
SetBitMap(bitMap,phone[i]);
}//for
// 統計個數
int count = 0;
for(int i = 0;i < N;++i){
if(GetBitMap(bitMap,i)){
++count;
}//if
}//for
return count;
}
int main(){
// 隨機生成NUM個電話號碼
int phone[NUM];
// 點陣圖大小
int* bitMap = new int[N/32+1];
// 清空
for(int i = 0;i < N;++i){
ClearBitMap(bitMap,i);
}//for
// 隨機生成100個電話號碼
srand((unsigned)time(nullptr));
for(int i = 0;i < NUM;++i){
phone[i] = rand() % N + MIN;
cout<<phone[i]<<endl;
}//for
cout<<"電話號碼個數->"<<PhoneCount(phone,NUM,bitMap)<<endl;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
待完善…….