位圖算法

樓主酷愛王者,可是因爲忙於業務,王者有一段時間沒玩了,待再次上線的時候,TM(天美)發來了一封郵件,親愛的召喚師,歡迎回歸王者榮耀,你已有88日沒有登陸過遊戲,這是爲你精心準備的迴歸大禮包,禮包是一些體驗卡和磚石等。but做爲一名程序猿,讓樓主更在乎的是88這個數字的統計方式。算法

 

咱們知道王者榮耀用戶數不少,假設有一億用戶,如何來記錄用戶的登陸信息,如何來查詢活躍用戶(如一週內登陸三次以上的),最常規的作法就是建一張用戶登陸信息表,有用戶ID,有登陸時間這樣的,而後用戶每登陸一次就往表中插入一條數據,沒毛病,那麼假設一天以內有1億用戶登陸,那麼2天表中就會有2億數據,這裏會有很嚴重的問題,首先表中不可能承載這麼多數據量,其次就算能夠裝得下這麼多數據,那你怎麼統計這麼多數據的表?效率性能如何?因此在傳統數據庫存儲層面是很差解決這個問題。數據庫

 

所以,咱們不妨設置用一個1bit位來標識用戶的登陸狀態,1/0,1是表明登陸,0是表明沒登陸,那麼能夠創建以下的數字模型數組

假設有10個用戶,統計一週以內用戶的登陸次數,模型假如是這樣的數據結構

星期一:0000011111性能

星期二:1001011011優化

星期三:1001011111spa

星期四:1011000001code

星期五:1001011001blog

 

橫着來看:就標識着星期一這天后邊5個用戶登陸了,前5個用戶沒登陸,星期二1,4,6,7,9,10用戶登陸其他沒有,其他同理,清晰可見。排序

豎着來看:就標識這同一我的一週以內的登陸狀況,好比第一我的,週二三五登陸了遊戲,二四就沒有玩,其他同理,便於統計。

 

這裏的數字模型能夠是一個字符串或者是數組,這是簡體思路。

 

下面進入主題,位圖算法,瞭解一下!

數據庫作持久化的時候,把數據作成數字模型這種形式來存儲(好比只存用戶ID),如有數據就標誌爲1或true,若無數據標誌爲0或false。

好比有一數字模型{5,2,1,2} 這裏最大值爲5,因此數組的長度就是5,而0到5中不存0,3,4數字

因此:Array[0]=0,Array[1]=1,Array[2]=2,Array[3]=0,Array[4]=0,Array[5]=1

數組模型以下 :int[] ={0,1,2,0,0,1}

上面數中因爲2有兩個,因此只能用int存數組的值,不用boolean型,這樣若是有多個一樣的數字能夠用值表示個數。如上面Array[2]=2,就表示2有2個。

又如:

假設咱們有{0,6,3,4}這數組,在位圖中數據結構初始化狀態應該就是這樣的,首先最大是6,那咱們申請l大小爲6的數組

 

經過位圖算法處理後,獲得的位圖是這樣的

 

 

 這種算法的缺點在於,最大值和最小值之間不能相差太大,不然浪費申請數組的空間。(蛋士能夠優化滴~)

 

實際應用:

1.判斷一個數是否存在某數據中,假若有40億數據,咱們如何快速判斷指定一個數是否存在?

申請512M的內存 512M=512*1024*1024B*8=4294967296比特(bit)  這個空間能夠裝40億了

一個bit位表明一個int值

讀入40億個數,設置相應的bit位

讀入要查詢的數,查看相應bit位是否爲1,爲1表示存在,爲0表示不存在

 

2.判斷整形數組是否重複

它的作法是按照集合中最大元素max建立一個長度爲max+1的新數組,而後再次掃描原數組,遇到幾就給新數組的第幾位置上1,如遇到 5就給新數組的第六個元素置1,這樣下次再遇到5想置位時發現新數組的第六個元素已是1了,這說明此次的數據確定和之前的數據存在着重複。它的運算次數最壞的狀況爲2N。若是已知數組的最大值即能事先給新數組定長的話效率還能提升一倍。

 

3.給數組排序

首先遍歷數組,獲得數組的最大最小值,而後根據這個最大最小值來縮小bitmap的範圍。這裏須要注意對於int的負數,都要轉化,並且取位的時候,數字要減去最小值。

給出JAVA代碼

public class WeiTu {
    
    public static int[] bitmapSort(int[] arr) {
        // 找出數組中最值
        int max = arr[0];
        int min = max;
        
        for (int i : arr) {
            if (max < i) {
                max = i;
            }
            if (min > i) {
                min = i;
            }
        }
        //初始化位圖數組大小
        int temp=0;//用於解決數組有負數的狀況        
        int[] newArr=null;
        if(min<0){
            temp=0-min;
            newArr = new int[max - min + 1];
        }else{
            newArr = new int[max+1];
            min=0;
        }
                        
        //構建位圖
        for(int i:arr){
            newArr[i+temp]++;//算法體現
        }
        // 從新調整arr數組中的元素
        int index = 0;
        for (int i = 0; i < newArr.length; i++) {
        // 位圖是1的就輸出,對數組排序
            while (newArr[i] > 0) {
                arr[index] = i + min;
                index++;
                newArr[i]--;
            }
        }
        return arr;
】    }

    public static void main(String[] args) {
        int[] arr={5,2,3,7,1};
        //int[] arr={-5,2,-3,7,1};
        int[] arrsort=bitmapSort(arr);
        for(int i:arrsort)
        System.out.println(i);
    }

}

 

4.作交集和並集效率極高

舉個例子,現有一位圖0000101,表明喜歡吃蘋果用戶

      另外一位圖0000111,表明喜歡吃西瓜用戶

統計喜歡吃蘋果或西瓜的用戶,0000101|0000111=0000111

 

 

 

優化:

在谷歌實現的EWAHCompressedBitmap中,把Bitmap存在Long的數組中,Long數組的每一個元素能夠被當作64位二進制,也是Bitmap的元素,叫word

當建立一個空Bitmap的時候,初始化有4個word元素,不夠就進行擴容

 

第一個w0不存入信息,當插入數字爲1時候w1變爲00000001,當插入爲4的時候w1,000010001,當插入爲64,超過了w1容量,w2,00000001

 

 

當存數字1000000時

1000001/64=15625 餘1 那麼按照正常思考的方式變成了這樣

如願以償的浪費了15625個w

事實上,它是這樣的

 

而後w0實際上是LRW,存儲分爲2部分,高32位表示橫跨多少個w,此處爲15625,低32位表示後方有多少個連續的w,此處爲0個

最終是這樣

 

end

相關文章
相關標籤/搜索