位图算法

楼主酷爱王者,可是因为忙于业务,王者有一段时间没玩了,待再次上线的时候,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

相关文章
相关标签/搜索