0


bitmap的原理及使用python实现最基础的bitmap

前段时间读公众号老看到bitmap或者hash值来解决大批量数据的排序或者去重的工作,当然这里的数据绝对不是平常所使用的小数据,而是可能纯文本数据大小都达到了上T的。正是因为自己多看了几眼,觉得还是挺牛的,因此本文主要对bitmap进行一定介绍,并使用python实现,并尝试对其进行通俗易懂的解释。

文章目录


bitmap原理

实际上bitmap的原理不难,就是用位的方法来储存数值,也正是因此如果不是数值的话就不能用bitmap(当然通过什么方法来转化为数值也不是不行)。用位如何储存数值呢?我们知道,当我们随意向计算机输入一个数字,这个数字绝对不是以其本身的数值形式储存在计算机内存中的,而储存形式就是二进制。比如我们输入8,那么计算机中会储存为1000。是这样吗?当然还不止这么简单,每种计算机中的字符的最小储存单位就是字节,一个字节有8位,所以至少也是00001000嘛。也正因此一个字节最大能储存11111111这个二进制数值(代表255)。这样看一个字节绝对不够用啊,所以一般还需要更多的字节来储存大一点的数字。在每种编程语言中所用于储存数字的字节数可能不同,在python3版本中 int 类型是动态长度的,因此理论可以存非常大的数字了。

bitmap的作用是为了达到数据去重或者储存,那么肯定是将要存储的东西变得越少越好,在bitmap思想中使用bit来储存每一个值。什么意思呢?一起来看下面这张图:
在这里插入图片描述
这张图我只画了四个位,可以看到框内的是分别的四个位,这几个位上有的地方为1,有的地方为0,之后这几个位综合起来形成一个我们熟知的十进制数值。这个十进制数值就储存着bitmap储存的数值。比如上面的5可以存储1,3两个数,15可以存储1,2,3,4这几个数。这下懂了吧,实际上就是在哪个位上有一个1就是代表这里存储了一个数字。

这里以4个字节的整数来储存数据为例进行下面的介绍。以四个字节来储存的话,最大的整数为4294967295,这个整数的所有位上都是1(共4*8=32个1)。那么这个整数储存了什么呢?储存的就是1-32(在python中应该是0-31,因为python从0开始计数)这些数字构成的列表。要知道4294967295这一个数字就储存了这么多数字,而实际上每一个数字单独储存的话也会花去四个字节,这么一想,最大能节约32倍的内存,还是挺不错的吧!

那么比32大的数呢?这些数将储存在下一份的4个字节中,也就是说如果是33这一个值的话其需要先算除以32算出一个模,取模后的数字就按照上面的步骤一样去记在一个整数中。当然除以32这个信息我们是必须要记录下来的,同时还要记录其余数是多少,因为大一点的数除以32得到的余数都不一样,要取得储存的值时,只需要将这个模和余数结合,就知道储存的是什么数字了。一般可以使用一个新的数组来储存要存放的bitmap结果,之后对bitmap结果进行处理就能直到储存的原始数值了。

根据刚刚的介绍,应该也已经发现了,bitmap不能储存相同的数字,而只能储存唯一值(因为每个位储存一个值嘛)。这也刚好契合了去重的初衷。

python基础实现

刚刚的介绍可能还云里雾里,看一下例子可能清晰一些。这里要首先提一下位运算,本例中要使用到的位运算为<<,表示左移操作就是向数字的二进制形式的右边添加0,输入的参数决定要添加的0的数量(也是左移的位数)。比如1(1)左移两位就成了4(100),注意这里添加了两个0。

在python中整数类型默认有符号,第一位为符号位,所以一个整数可用的位数为31,因此取余和取模是除以31.

# 这里提醒一下python中可以使用bin计算十进制数的二进制形式
# 左移的运算也可以在命令行中不断尝试,直到自己理解

def get_remainder_and_mod(num):
    remainder = num//31
    mod = num%31return remainder,1<<mod   
    # 这个函数算出余数和模,并且将1向左推mod的数量,比如2这个数字就要推两位,
    # 刚好变为了100这个二进制数(十进制为4),这样理解了吧,被推的个数就是mod的大小,
    # 被推到变为100后,这个100的1不再会改变了(不能重复)
    # 变为100后,如果和1000这个数进行或运算,那么得到的结果就是1100,这时储存的就是两个数了
    # 之后和余数结合起来就成为了原始的数字了  
    # 注意这一大段中的10之类的都是二进制数字哈

a =[52,51,53,20,30,18,188,200,1000,10,15,20,18]  # 要储存的列表
arr =[0for i inrange(max(a)//31+1)] 
 # 用于储存的数组,根据最大的数字决定数组的大小,因为最大的数需要数组的最后一项来储存

for i in a:
    remainder, num_saved =get_remainder_and_mod(i)  
    arr[remainder]= arr[remainder]| num_saved
    # 这里的|符号表示或运算,对两个十进制数字进行或运算实际上是先把数字都变成了二进制之后再进行的
    # 由于不同的数字可能有不同的余数,因此需要不同的四个字节来储存新的更大的数
    # 这里arr[remainder]的remainder就表示一个索引,而这个索引刚好也是余数的大小
    # 之后,或运算就表示了这个余数下,已经储存的值和将要储存的新值进行或运算,也就是开始的某个位为0的话
    # 如果传入的这个位为1,那么这个地方就变1,这个位置就代表一个新值了
    # 这里读者不懂的话可以多理解一下
# 这里生成的arr为
# [1075086336,7340032,0,0,0,0,16388,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,256]for i inrange(max(a)+1):
    remainder, bit_index =get_remainder_and_mod(i)if arr[remainder]& bit_index:print(i)  
    # 由于之前存入的数字对应的位置已经存了1了,那么如果使用和运算的话应该是会返回1的
    # 这就是取回的方法

# 下面是输出的结果,可以看到已经排序好了,毕竟我们是一一循环的嘛!
'''
10151820305110020010001888'''    

这里代码中我只是非常简单的实现了一下bitmap,更为系统的实现方法读者可以去参考其他博主哈。

再理解一下整个bitmap的原理和实现,会发现实际上如果数字都是十分分散或者数字很少的话,比如数字间都是差距很大的情况,这样可能还是不适合使用bitmap的,而平时处理的数据量少的情况也根本不需要用到bitmap。但是学习到有这样的东西也不错,万一什么时候就用上了呢。

在跨年之际发布这篇文章,祝看到的人新一年事事顺心吧!

参考:https://blog.csdn.net/weixin_39198406/article/details/107716579

标签: python 大数据

本文转载自: https://blog.csdn.net/keepaware/article/details/122202477
版权归原作者 你大佬来啦 所有, 如有侵权,请联系我们删除。

“bitmap的原理及使用python实现最基础的bitmap”的评论:

还没有评论