python实现bitmap数据结构详解


Posted in Python onFebruary 17, 2014

bitmap是很常用的数据结构,比如用于Bloom Filter中;用于无重复整数的排序等等。bitmap通常基于数组来实现,数组中每个元素可以看成是一系列二进制数,所有元素组成更大的二进制集合。对于Python来说,整数类型默认是有符号类型,所以一个整数的可用位数为31位。

bitmap实现思路

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

python实现bitmap数据结构详解

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

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

初始化bitmap

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

#!/usr/bin/env python
#coding: utf8
class 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 个元素。

计算在数组中的索引

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

#!/usr/bin/env python
#coding: utf8
class 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
if __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: utf8
class 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
if __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),)

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

置1操作

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

#!/usr/bin/env python
#coding: utf8
class 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

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

清0操作

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

#!/usr/bin/env python
#coding: utf8
class 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

清0和置1是互反操作。

测试某位是否为1

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

#!/usr/bin/env python
#coding: utf8
class 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 False
if __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]
True
True
False
False

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

#!/usr/bin/env python
#coding: utf8
class 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 False
if __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

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

Python 相关文章推荐
python高手之路python处理excel文件(方法汇总)
Jan 07 Python
Python标准库之collections包的使用教程
Apr 27 Python
Python判断一个三位数是否为水仙花数的示例
Nov 13 Python
python实现简单多人聊天室
Dec 11 Python
Python 3.8正式发布重要新功能一览
Oct 17 Python
python实现操作文件(文件夹)
Oct 31 Python
Python中six模块基础用法
Dec 08 Python
matplotlib quiver箭图绘制案例
Apr 17 Python
浅谈pycharm导入pandas包遇到的问题及解决
Jun 01 Python
python实现简单的tcp 文件下载
Sep 16 Python
pytorch 实现L2和L1正则化regularization的操作
Mar 03 Python
python中redis包操作数据库的教程
Apr 19 Python
python实现ftp客户端示例分享
Feb 17 #Python
用smtplib和email封装python发送邮件模块类分享
Feb 17 #Python
python类参数self使用示例
Feb 17 #Python
python实现爬虫下载漫画示例
Feb 16 #Python
python发送邮件示例(支持中文邮件标题)
Feb 16 #Python
python定时器使用示例分享
Feb 16 #Python
python求素数示例分享
Feb 16 #Python
You might like
PHP 字符串操作入门教程
2006/12/06 PHP
mysql 中InnoDB和MyISAM的区别分析小结
2008/04/15 PHP
PHP中实现中文字符进制转换原理分析
2011/12/06 PHP
基于PHP服务端图片生成缩略图的方法详解
2013/06/20 PHP
PHP获取时间排除周六、周日的两个方法
2014/06/30 PHP
PHP编程快速实现数组去重的方法详解
2017/07/22 PHP
阿里对象存储OSS在laravel框架中的使用方法
2019/10/13 PHP
thinkphp5 框架结合plupload实现图片批量上传功能示例
2020/04/04 PHP
解决Extjs上传图片无法预览的解决方法
2012/03/22 Javascript
js如何判断用户是在PC端和还是移动端访问
2014/04/24 Javascript
node.js 动态执行脚本
2016/06/02 Javascript
Bootstrap CSS组件之按钮下拉菜单
2016/12/17 Javascript
Vue.js事件处理器与表单控件绑定详解
2017/03/20 Javascript
node.js中实现kindEditor图片上传功能的方法教程
2017/04/26 Javascript
Vue+Flask实现简单的登录验证跳转的示例代码
2018/01/13 Javascript
Node.js中你不可不精的Stream(流)
2018/06/08 Javascript
JavaScript实现答题评分功能页面
2020/06/24 Javascript
用Python的Django框架完成视频处理任务的教程
2015/04/02 Python
Python简易版图书管理系统
2019/08/12 Python
PHP统计代码行数的小代码
2019/09/19 Python
Django使用消息提示简单的弹出个对话框实例
2019/11/15 Python
python 和c++实现旋转矩阵到欧拉角的变换方式
2019/12/04 Python
Python中if有多个条件处理方法
2020/02/26 Python
详解基于Jupyter notebooks采用sklearn库实现多元回归方程编程
2020/03/25 Python
Python如何基于Tesseract实现识别文字功能
2020/06/05 Python
python绘制高斯曲线
2021/02/19 Python
canvas实现烟花的示例代码
2020/01/16 HTML / CSS
有机童装:Toby Tiger
2018/05/23 全球购物
英语翻译系毕业生求职信
2013/09/29 职场文书
路政管理专业推荐信
2013/11/11 职场文书
毕业班联欢会主持词
2014/03/27 职场文书
保护水资源的标语
2014/06/17 职场文书
艾滋病宣传标语
2014/06/25 职场文书
医生个人自我剖析材料
2014/10/08 职场文书
2016大学生就业指导课心得体会
2016/01/15 职场文书
小学四年级作文之写景
2019/08/23 职场文书