大數據的處理算法——BitMap算法
首先我們來看一個問題:
如何判斷一個整數是否在40億個整數之中?
當時我看到這個問題首先想到的是利用一個map或者set進行存儲(/笑哭)
但是一個整數4個字節,40億個整數就是160億個字節,需要16GB的內存(/驚訝)
如果分多次加載就需要利用到磁盤的IO,但是磁盤的IO讀取速度遠遠低於內存的讀取速度
所以是不可取的!!!
那麼可以採用分佈式的算法來實現,利用8臺計算機,每臺計算機2G內存,將數據分散到這8臺計算機中進行處理,這樣的效率就比較能夠接受。
但是還有沒有更好的方式呢?
有!!!
是什麼?
Bitmap算法!!!
Bitmap算法
首先我們來了解一下bitmap算法是個啥?
1:Bit-Map算法又名位圖算法,其原理是,使用下標代替數值或特定的意義,使用這個位爲0或者1代表特性是否存在。
2:Bit-Map算法具有效率高,節省空間的特點,適用於對大量數據進行去重,查詢等。
說簡單點
就是咱們把每個數據的狀態存在爲1,不存在爲0,利用bit字節來進行存儲,這樣大大的節約了空間!!
那麼可以簡單的聯想到BitMap的應用領域:
排序,查找,存儲一個用戶的各種標籤數據啊,用bitmap算法都是一種比較棒的方式。
那麼回到題目:
如何判斷一個整數是否在40億個整數之中?
我們來看一段代碼,當然這裏我沒用40億這麼大
我這裏用10000000,其實也很大了(/doge)
import java.util.ArrayList;
import java.util.List;
public class BitMap {
private static final int N = 10000000;
private int[] a = new int[N/32 + 1];
/**
* 設置所在的bit位爲1
* @param n
*/
public void addValue(int n) {
//int row = n / 32;
int row = n >> 5;
// >>相當於/2的n次方,<<相當於*2的n次方
a[row] |= 1 << (n & 0x1F);
// |=是相當於a 和=後面的值做或運算然後賦值給a
// 0x在計算機中代表16進制,F代表16 1F = 1*F + F = 32
}
/**
* 判斷所在bit位是否爲1
* @param n
* @return
*/
public boolean exits(int n) {
int row = n >> 5;
return (a[row] & (1 << (n & 0x1F))) != 0;
}
public void display(int row) {
System.out.println("bitMap位圖展示");
for(int i = 0; i < row; i++) {
List<Integer> list = new ArrayList<Integer>();
int temp = a[i];
for(int j = 0; j < 32; j++) {
list.add(temp & 1);
temp >>= 1;
}
System.out.println("a["+i+"]" + list);
}
}
public static void main(String[] args) {
int[] num = {1,5,30,32,64,56,159,120,21,17,35,45};
BitMap map = new BitMap();
for(int i = 0; i < num.length; i++) {
map.addValue(num[i]);
}
int temp = 120;
if(map.exits(temp)) {
System.out.println("temp:" + temp + "has already exists");
}
int temp2 = 131;
if(map.exits(temp2)) {
System.out.println("temp2:" + temp2 + " has already exists");
}else {
System.out.println("temp2:" + temp2 + " not has already exists");
}
map.display(5);
}
}
運行結果:
temp: 120 has already exists
temp2: 131 not has already exists
bitMap位圖展示
a[0][0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
a[1][1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
a[2][1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
a[3][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
a[4][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
要想實現這個bitMap算法,首先我們要想明白兩點:
1.十進制如何轉化爲對應數組a中的下標
解決辦法:
十進制數0-31,對應在數組a[0]中,32-63對應在數組a[1]中,64-95對應在數組a[2]中………,使用
數學歸納分析得出結論:對於一個十進制數n,其在數組a中的下標爲:a[n/32]
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]
中的下標。
那麼這裏就不能不碰到java中的位運算了
然後有關Java的位運算,我在註釋裏面寫的很詳細,可以仔細去看下(/doge)
當然
a[row] |= 1 << (n & 0x1F);
// |=是相當於a 和=後面的值做或運算然後賦值給a
// 0x在計算機中代表16進制,F代表16 1F = 1*F + F = 32
這裏是通過移位的方式來置1的
結語
當我看到bitmap算法,我就和看到寶一樣。這個算法的優越性不言而喻了,以後編程中可以多多利用,故此記錄下來!