BitMap位圖與海量數據的理解與應用

1. Bit Map算法簡介

        來自於《編程珠璣》。所謂的Bit-map就是用一個bit位來標記某個元素對應的Value, 而Key便是該元素。因爲採用了Bit爲單位來存儲數據,所以在存儲空間方面,能夠大大節省。html

二、 Bit Map的基本思想

        咱們先來看一個具體的例子,假設咱們要對0-7內的5個元素(4,7,2,5,3)排序(這裏假設這些元素沒有重複)。那麼咱們就能夠採用Bit-map的方法來達到排序的目的。要表示8個數,咱們就只須要8個Bit(1Bytes),首先咱們開闢1Byte的空間,將這些空間的全部Bit位都置爲0,以下圖:
                                                       java


而後遍歷這5個元素,首先第一個元素是4,那麼就把4對應的位置爲1(能夠這樣操做 p+(i/8)|(0x01<<(i%8)) 固然了這裏的操做涉及到Big-ending和Little-ending的狀況,這裏默認爲Big-ending),由於是從零開始的,因此要把第五位置爲一(以下圖):
 算法

                                                      


而後再處理第二個元素7,將第八位置爲1,,接着再處理第三個元素,一直到最後處理完全部的元素,將相應的位置爲1,這時候的內存的Bit位的狀態以下: 
 編程

                                                    


而後咱們如今遍歷一遍Bit區域,將該位是一的位的編號輸出(2,3,4,5,7),這樣就達到了排序的目的。數組

 

優勢:數據結構

1.運算效率高,不準進行比較和移位;dom

2.佔用內存少,好比N=10000000;只需佔用內存爲N/8=1250000Byte=1.25M。 
缺點:ide

       全部的數據不能重複。即不可對重複的數據進行排序和查找。    spa

 

算法思想比較簡單,但關鍵是如何肯定十進制的數映射到二進制bit位的map圖。.net

三、 Map映射表

假設須要排序或者查找的總數N=10000000,那麼咱們須要申請內存空間的大小爲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位。 

如題:

給你一個文件,裏面包含40億個整數,寫一個算法找出該文件中不包含的一個整數, 假設你有1GB內存可用。

若是你只有10MB的內存呢?

一個位表明一個數據,那40一個數據大概要40*10^8*bit = 0.5GB,知足內存要求。

首先咱們用int來表示:int  bmap[1+N/32]; //N是總數,N=40億,一個int32bit

而後咱們插入一個整數val,要先計算val位於數組bmap中的索引:index = val/32;

好比整數33,index=33/32=1,第33位於數組中的index=1

好比整數67,index=67/32=2,位於數組中index=2

而後在計算在這個index中的位置,由於數組中的每一個元素有32位

33,index=1,在1中的位置爲33%32=1

67,index=2,在2中的位置爲67%32=3

而後就是標識這個位置爲1:

bmap[val/32]  |= (1<<(val%32));

33: bmap[1]    != (1<<1);//xxxxxx1x,紅絲位置被置爲1

67: bmap[2]   !=  (1<<3);//xxxx1xxx

void setVal(int val)
{
    bmap[val/32] |= (1<<(val%32));
    //bmap[val>>5] != (val&0x1F);//這個更快?
}

 

怎樣檢測整數是否存在?

好比咱們檢測33,一樣咱們須要計算index,以及在index元素中的位置

33: index = 1, 在bmap[1]中的位置爲 1,只須要檢測這個位置是否爲1

bmp[1] &(1<<1),這樣是1返回true,否側返回false

67:bmp[2]&(1<<3)

127:bmp[3]&(1<<31)

bool testVal(int val)
{
    return bmap[val/32] & (1<<(val%32));
    //return bmap[val>>5] & (val&0x1F);
}

 

如今咱們來看若是內存要求是10MB呢?

 

這固然不能用bitmap來直接計算。由於從40億數據找出一個不存在的數據,咱們能夠將這麼多的數據分紅許

多塊, 好比每個塊的大小是1000,那麼第一塊保存的就是0到999的數,第2塊保存的就是1000 到1999的數……

實際上咱們並不保存這些數,而是給每個塊設置一個計數器。 這樣每讀入一個數,咱們就在它所在的塊對應的計數器加1。

處理結束以後, 咱們找到一個塊,它的計數器值小於塊大小(1000), 說明了這一段裏面必定有數字是文件中所不包含的。而後咱們單獨處理
這個塊便可。接下來咱們就能夠用Bit Map算法了。咱們再遍歷一遍數據, 把落在這個塊的數對應的位置1(咱們要先把這個數
歸約到0到blocksize之間)。 最後咱們找到這個塊中第一個爲0的位,其對應的數就是一個沒有出如今該文件中的數。)

四、 Bit-Map的應用

      1)可進行數據的快速查找,判重,刪除,通常來講數據範圍是int的10倍如下。

       2)去重數據而達到壓縮數據

五、 具體實現(JAVA)

【問題實例】

1)已知某個文件內包含一些電話號碼,每一個號碼爲8位數字,統計不一樣號碼的個數。

8位最多99 999 999,大概須要99m個bit,大概10幾m字節的內存便可。

位圖法須要的空間不多(依賴於數據分佈,可是咱們也能夠經過一些放啊發對數據進行處理,使得數據變得密集),在數據比較密集的時候效率很是高。例如:8位整數能夠表示的最大十進制數值爲99999999,若是每一個數組對應於一個bit位,那麼把全部的八進制整數存儲起來只須要:99Mbit = 12.375MB.

實際上,Java jdk1.0已經提供了bitmap的實現BitSet類,不過其中的某些方法是jdk1.4以後纔有的。

分別使用本身實現的BitMap和jdk的BitSet類:

複製代碼
 1 //去除重複並排序
 2 import java.util.Arrays;
 3 import java.util.BitSet;
 4 import java.util.Random;
 5 
 6 /**
 7  * @author 8  * @date Time: 
 9  * @des:
10  */
11 public class BitMap {
12     int ARRNUM = 800;
13     int LEN_INT = 32;
14     int mmax = 9999;
15     int mmin = 1000;
16     int N = mmax - mmin + 1;
17 
18     public static void main(String args[]) {
19          new BitMap().findDuplicate();
20          new BitMap().findDup_jdk();
21     }
22 
23     public void findDup_jdk() {
24         System.out.println("*******調用JDK中的庫方法--開始********");
25         BitSet bitArray = new BitSet(N);
26         int[] array = getArray(ARRNUM);
27         for (int i = 0; i < ARRNUM; i++) {
28             bitArray.set(array[i] - mmin);
29         }
30         int count = 0;
31         for (int j = 0; j < bitArray.length(); j++) {
32             if (bitArray.get(j)) {
33                 System.out.print(j + mmin + " ");
34                 count++;
35             }
36         }
37         System.out.println();
38         System.out.println("排序後的數組大小爲:" + count );
39         System.out.println("*******調用JDK中的庫方法--結束********");
40     }
41     //下面是本身實現的方法:
42     public void findDuplicate() {
43         int[] array = getArray(ARRNUM);
44         int[] bitArray = setBit(array);
45         printBitArray(bitArray);
46     }
47 
48     public void printBitArray(int[] bitArray) {
49         int count = 0;
50         for (int i = 0; i < N; i++) {
51             if (getBit(bitArray, i) != 0) {
52                 count++;
53                 System.out.print(i + mmin + "\t");
54             }
55         }
56         System.out.println();
57         System.out.println("去重排序後的數組大小爲:" + count);
58     }
59 
60     public int getBit(int[] bitArray, int k) {// 1右移 k % 32位 與上 數組下標爲 k/32 位置的值
61         return bitArray[k / LEN_INT] & (1 << (k % LEN_INT));
62     }
63 
64     public int[] setBit(int[] array) {// 首先取得數組位置下標 i/32, 而後 或上
65                                         // 在該位置int類型數值的bit位:i % 32
66         int m = array.length;
67         int bit_arr_len = N / LEN_INT + 1;
68         int[] bitArray = new int[bit_arr_len];
69         for (int i = 0; i < m; i++) {
70             int num = array[i] - mmin;
71             bitArray[num / LEN_INT] |= (1 << (num % LEN_INT));
72         }
73         return bitArray;
74     }
75 
76     public int[] getArray(int ARRNUM) {
77 
78         @SuppressWarnings("unused")
79         int array1[] = { 1000, 1002, 1032, 1033, 6543, 9999, 1033, 1000 };
80 
81         int array[] = new int[ARRNUM];
82         System.out.println("數組大小:" + ARRNUM);
83         Random r = new Random();
84         for (int i = 0; i < ARRNUM; i++) {
85             array[i] = r.nextInt(N) + mmin;
86         }
87 
88         System.out.println(Arrays.toString(array));
89         return array;
90     }
91 }
複製代碼

 

2)2.5億個整數中找出不重複的整數的個數,內存空間不足以容納這2.5億個整數。 
將bit-map擴展一下,用2bit表示一個數便可,0表示未出現,1表示出現一次,2表示出現2次及以上,在遍歷這些數的時候,若是對應位置的值是0,則將其置爲1;若是是1,將其置爲2;若是是2,則保持不變。或者咱們不用2bit來進行表示,咱們用兩個bit-map便可模擬實現這個2bit-map,都是同樣的道理。

給你一個文件,裏面包含40億個整數,寫一個算法找出該文件中不包含的一個整數, 假設你有1GB內存可用。

若是你只有10MB的內存呢?原文地址:https://www.cnblogs.com/protected/p/6626447.html

 

相關文章
相關標籤/搜索