• 技术文章 >后端开发 >Python教程

    Python如何实现bitmap数据结构

    零到壹度零到壹度2018-04-14 14:37:02原创1359
    bitmap是很常用的数据结构,比如用于Bloom Filter中、用于无重复整数的排序等等。bitmap通常基于数组来实现,数组中每个元素可以看成是一系列二进制数,所有元素组成更大的二进制集合。对于Python来说,整数类型默认是有符号类型,所以一个整数的可用位数为31位。

    bitmap实现思路

    bitmap是用于对每一位进行操作。举例来说,一个Python数组包含4个32位有符号整型,则总共可用位为4 * 31 = 124位。如果要在第90个二进制位上操作,则要先获取到操作数组的第几个元素,再获取相应的位索引,然后执行操作。

    上图所示为一个32位整型,在Python中默认是有符号类型,最高位为符号位,bitmap不能使用它。左边是高位,右边是低位,最低位为第0位。

    初始化bitmap

    首先需要初始化bitmap。拿90这个整数来说,因为单个整型只能使用31位,所以90除以31并向上取整则可得知需要几个数组元素。代码如下:

    #!/usr/bin/env python#coding: utf8class Bitmap(object):
    	def __init__(self, max):
    		self.size = int((max + 31 - 1) / 31) 
                    #向上取整if __name__ == '__main__':
    	bitmap = Bitmap(90)	
    	print '需要 %d 个元素。' % bitmap.size

    $ python bitmap.py
    需要 3 个元素。

    计算索引

    确定了数组大小后,也就可以创建这个数组了。如果要将一个整数保存进这个数组,首先需要知道保存在这个数组的第几个元素之上,然后要知道是在这个元素的第几位上。因此计算索引分为:

    1. 计算在数组中的索引

    2. 计算在数组元素中的位索引

    计算在数组中的索引

    计算在数组中的索引其实是跟之前计算数组大小是一样的。只不过之前是对最大数计算,现在换成任一需要存储的整数。但是有一点不同,计算在数组中的索引是向下取整,所以需要修改calcElemIndex方法的实现。代码改为如下:

    #!/usr/bin/env python#coding: utf8class Bitmap(object):
    	def __init__(self, max):
    		self.size  = self.calcElemIndex(max, True)
    		self.array = [0 for i in range(self.size)]	def calcElemIndex(self, num, up=False):
    		'''up为True则为向上取整, 否则为向下取整'''
    		if up:			return int((num + 31 - 1) / 31) #向上取整
    		return num / 31if __name__ == '__main__':
    	bitmap = Bitmap(90)	print '数组需要 %d 个元素。' % bitmap.size	print '47 应存储在第 %d 个数组元素上。' % bitmap.calcElemIndex(47)

    $ python bitmap.py
    数组需要 3 个元素。47 应存储在第 1 个数组元素上。

    所以获取最大整数很重要,否则有可能创建的数组容纳不下某些数据。

    计算在数组元素中的位索引

    数组元素中的位索引可以通过取模运算来得到。令需存储的整数跟31取模即可得到位索引。代码改为如下:

    #!/usr/bin/env python#coding: utf8class Bitmap(object):
    	def __init__(self, max):
    		self.size  = self.calcElemIndex(max, True)
    		self.array = [0 for i in range(self.size)]	def calcElemIndex(self, num, up=False):
    		'''up为True则为向上取整, 否则为向下取整'''
    		if up:			return int((num + 31 - 1) / 31) #向上取整
    		return num / 31
    
    	def calcBitIndex(self, num):
    		return num % 31if __name__ == '__main__':
    	bitmap = Bitmap(90)	print '数组需要 %d 个元素。' % bitmap.size	print '47 应存储在第 %d 个数组元素上。' % bitmap.calcElemIndex(47)	print '47 应存储在第 %d 个数组元素的第 %d 位上。' % (bitmap.calcElemIndex(47), bitmap.calcBitIndex(47),)

    $ python bitmap.py
    数组需要 3 个元素。47 应存储在第 1 个数组元素上。47 应存储在第 1 个数组元素的第 16 位上。

    别忘了是从第0位算起哦。

    置1操作

    二进制位默认是0,将某位置1则表示在此位存储了数据。代码改为如下:

    #!/usr/bin/env python#coding: utf8class Bitmap(object):
    	def __init__(self, max):
    		self.size  = self.calcElemIndex(max, True)
    		self.array = [0 for i in range(self.size)]	def calcElemIndex(self, num, up=False):
    		'''up为True则为向上取整, 否则为向下取整'''
    		if up:			return int((num + 31 - 1) / 31) #向上取整
    		return num / 31
    
    	def calcBitIndex(self, num):
    		return num % 31
    
    	def set(self, num):
    		elemIndex = self.calcElemIndex(num)
    		byteIndex = self.calcBitIndex(num)
    		elem      = self.array[elemIndex]
    		self.array[elemIndex] = elem | (1 << byteIndex)if __name__ == '__main__':
    	bitmap = Bitmap(90)
    	bitmap.set(0)	print bitmap.array

    $ python bitmap.py
    [1, 0, 0]

    因为从第0位算起,所以如需要存储0,则需要把第0位置1。

    清0操作

    将某位置0,也即丢弃已存储的数据。代码如下:

    #!/usr/bin/env python#coding: utf8class Bitmap(object):
    	def __init__(self, max):
    		self.size  = self.calcElemIndex(max, True)
    		self.array = [0 for i in range(self.size)]	def calcElemIndex(self, num, up=False):
    		'''up为True则为向上取整, 否则为向下取整'''
    		if up:			return int((num + 31 - 1) / 31) #向上取整
    		return num / 31
    
    	def calcBitIndex(self, num):
    		return num % 31
    
    	def set(self, num):
    		elemIndex = self.calcElemIndex(num)
    		byteIndex = self.calcBitIndex(num)
    		elem      = self.array[elemIndex]
    		self.array[elemIndex] = elem | (1 << byteIndex)	def clean(self, i):
    		elemIndex = self.calcElemIndex(i)
    		byteIndex = self.calcBitIndex(i)
    		elem      = self.array[elemIndex]
    		self.array[elemIndex] = elem & (~(1 << byteIndex))if __name__ == '__main__':
    	bitmap = Bitmap(87)
    	bitmap.set(0)
    	bitmap.set(34)	print bitmap.array
    	bitmap.clean(0)	print bitmap.array
    	bitmap.clean(34)	print bitmap.array

    $ python bitmap.py[1, 8, 0][0, 8, 0][0, 0, 0]

    清0和置1是互反操作。

    测试某位是否为1

    判断某位是否为1是为了取出之前所存储的数据。代码如下:

    #!/usr/bin/env python#coding: utf8class Bitmap(object):
    	def __init__(self, max):
    		self.size  = self.calcElemIndex(max, True)
    		self.array = [0 for i in range(self.size)]	def calcElemIndex(self, num, up=False):
    		'''up为True则为向上取整, 否则为向下取整'''
    		if up:			return int((num + 31 - 1) / 31) #向上取整
    		return num / 31
    
    	def calcBitIndex(self, num):
    		return num % 31
    
    	def set(self, num):
    		elemIndex = self.calcElemIndex(num)
    		byteIndex = self.calcBitIndex(num)
    		elem      = self.array[elemIndex]
    		self.array[elemIndex] = elem | (1 << byteIndex)	def clean(self, i):
    		elemIndex = self.calcElemIndex(i)
    		byteIndex = self.calcBitIndex(i)
    		elem      = self.array[elemIndex]
    		self.array[elemIndex] = elem & (~(1 << byteIndex))	def test(self, i):
    		elemIndex = self.calcElemIndex(i)
    		byteIndex = self.calcBitIndex(i)		if self.array[elemIndex] & (1 << byteIndex):			return True
    		return Falseif __name__ == '__main__':
    	bitmap = Bitmap(90)
    	bitmap.set(0)	print bitmap.array	print bitmap.test(0)
    	bitmap.set(1)	print bitmap.test(1)	print bitmap.test(2)
    	bitmap.clean(1)	print bitmap.test(1)

    $ python bitmap.py
    [1, 0, 0]TrueTrueFalseFalse

    接下来实现一个不重复数组的排序。已知一个无序非负整数数组的最大元素为879,请对其自然排序。代码如下:

    #!/usr/bin/env python#coding: utf8class Bitmap(object):
    	def __init__(self, max):
    		self.size  = self.calcElemIndex(max, True)
    		self.array = [0 for i in range(self.size)]	def calcElemIndex(self, num, up=False):
    		'''up为True则为向上取整, 否则为向下取整'''
    		if up:			return int((num + 31 - 1) / 31) #向上取整
    		return num / 31
    
    	def calcBitIndex(self, num):
    		return num % 31
    
    	def set(self, num):
    		elemIndex = self.calcElemIndex(num)
    		byteIndex = self.calcBitIndex(num)
    		elem      = self.array[elemIndex]
    		self.array[elemIndex] = elem | (1 << byteIndex)	def clean(self, i):
    		elemIndex = self.calcElemIndex(i)
    		byteIndex = self.calcBitIndex(i)
    		elem      = self.array[elemIndex]
    		self.array[elemIndex] = elem & (~(1 << byteIndex))	def test(self, i):
    		elemIndex = self.calcElemIndex(i)
    		byteIndex = self.calcBitIndex(i)		if self.array[elemIndex] & (1 << byteIndex):			return True
    		return Falseif __name__ == '__main__':
    	MAX = 879
    	suffle_array = [45, 2, 78, 35, 67, 90, 879, 0, 340, 123, 46]
    	result       = []
    	bitmap = Bitmap(MAX)	for num in suffle_array:
    		bitmap.set(num)	
    	for i in range(MAX + 1):		if bitmap.test(i):
    			result.append(i)	print '原始数组为:    %s' % suffle_array	print '排序后的数组为: %s' % result

    $ python bitmap.py原始数组为:   [45, 2, 78, 35, 67, 90, 879, 0, 340, 123, 46]排序后的数组为:[0, 2, 35, 45, 46, 67, 78, 90, 123, 340, 879]

    结束语

    bitmap实现了,则利用其进行排序就非常简单了。其它语言也同样可以实现bitmap,但对于静态类型语言来说,比如C/Golang这样的语言,因为可以直接声明无符号整型,所以可用位就变成32位,只需将上述代码中的31改成32即可,这点请大家注意。

    相关推荐:

    数据结构——bitmap

    C++实现BitMap数据结构

    数据结构之位图(bitmap)详解

    【数据结构】BitMap使用

    以上就是Python如何实现bitmap数据结构的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:bitmap Python 数据结构
    上一篇:总结关于python中的中文编码问题 下一篇:Python简单实现控制电脑的方法
    20期PHP线上班

    相关文章推荐

    精选22门好课,价值3725元,开通VIP免费学习!• Python轻量级搜索工具Whoosh的使用(总结分享)• python正则表达式如何实现重叠匹配• 总结分享Python冷门的技巧• python虚拟环境配置与管理• 完全掌握Python中的双下方法
    1/1

    PHP中文网