BitMap概述

本文介绍 BitMap 算法的应用背景,算法思想和相关实现细节。

概括而言,BitMap 主要用来解决海量数据中元素查询,去重、以及排序等问题。这里对海量数据场景的强调,似乎暗示了这个算法对空间的利用相当的精巧和经济,事实确实如此。

BitMap算法

本来数据序列的排序是一个平凡的任务,现有的多种排序算法,都有各自擅场能适应不同情形的具体要求。但我们考虑这样一个场景:有一台内存为 4 GB 的 PC,其硬盘中的一个存储了 30 亿个无符号整型数据文件,这些整数一行一个且无重复。现在需要我们对这个文件中的数据进行排序后输出。

简单计算不难得到,这个数据文件的大小为 \(4⋅3⋅10^9/2^{30}\) 约为 11.2 GB,显然将这个数据文件直接读入内存是办不到的。能否强行利用现有的内存 size 来存储这些数据呢?答案是可能的,此时 BitMap 算法就该 C 位亮相了。BitMap 的想法相当精妙,它对整型数据作了一种转化,使得这个办不到的存储成为可能。我们这里忽略不同语言的设定,假设一个 int 整数占 4 个字节,即32 bit,如果我们能用一个 bit 位来标示一个 int 整数,那么需要的存储空间将大大减少,估算一下可知,30亿个整数需要的内存空间为 \(4⋅3⋅10^9/8/2^{20}\) 大概为 357.6 MB,这样,我们可以轻易将这 30 亿个 int 数放到内存中进行处理。

具体而言,BitMap 对数据的转化可简述如下:

一个整型 int 占 4 bytes,共32位,我们申请一个 int 长度为 N//32 + 1 的数组,即可存储完这些数据,其中 N 表示要进行查找的最大整数,这可以经读取遍历一轮数据获得。通过数组中的每个元素在内存在占 32 位对应表示十进制数 0-31,故可得到 BitMap 表:

array[0] 可表示 0-31

array[1] 可表示 32-63

array[2] 可表示 64-95

...

下面就只剩下如何将十进制数转换为对应的二进制 bit 位,实现以 1 当 32 的效果,显然,这部分实现只需用到一些位运算操作,具体细节见下面的代码示例。不难看出,Bitmap 排序需要的时间复杂度和空间复杂度依赖于数据中最大的数字。

代码实现

from array import array

class BitMap:
def __init__(self):
self.n = 5
self.bitsize = 1 << self.n
self.typecode = 'I' # 32位unsighed整型
self.lowerbound = 0 # 若数组中有负数,则所有数都减去最小的那个负数 @staticmethod
def greater_power2n(x):
i = 1
while True:
y = x >> i
x |= y
if y == 0:
break
i <<= 1
return x + 1 def load(self, inp):
'''
一般情形,数据应该是流式读取,这里简化起见不失一般性,将数据直接全部读完
'''
mini = min(inp)
if mini < 0:
self.lowerbound = -mini # 如果数组中有<0的数,则所有数都要减去最小的那个负数
inp = [i + self.lowerbound for i in inp]
maxi = max(inp)
num_arr = max(self.greater_power2n(maxi) >> self.n, 1) # 至少应该使用一个数组
self.arr = array(self.typecode, [0] * num_arr)
for x in inp:
self._set(x) def _set(self, x, set_val=True):
'''
将x在数组中对应元置为1
'''
arr_idx = x >> self.n # 元素在第几个数组中,等价于x // 2**self.n
bit_idx = x & (self.bitsize - 1) # 元素在相应数组中的第几个bit位,等价于x % 2**self.n
if set_val:
self.arr[arr_idx] |= 1 << bit_idx
else:
self.arr[arr_idx] &= ~(1 << bit_idx) def search(self, x):
if self.lowerbound != 0:
x += self.lowerbound
arr_idx = x >> self.n
bit_idx = x & (self.bitsize - 1)
existence = True if self.arr[arr_idx] & (1 << bit_idx) else False
return existence def sort(self):
sorted_seq = []
for arr_idx, a in enumerate(self.arr):
for bit_idx in range(self.bitsize):
if a & (1 << bit_idx):
sorted_seq.append(arr_idx * self.bitsize + bit_idx - self.lowerbound)
return sorted_seq def show_bitmap(self):
for i, a in enumerate(self.arr):
print('The {}th array elements: {:032b}'.format(i, a))

运行测试

>>> bitmap = BitMap()
>>> bitmap.load([-3, 2, 56, -34, 40, 21, 99, 25])
>>> bitmap.search(21), bitmap.search(3)
(True, False)
>>> bitmap.sort()
[-34, -3, 2, 21, 25, 40, 56, 99]

参考资料

黑胡同里の猫,详解BitMap算法,https://www.520mwx.com/view/59057

位图算法及其实现,BitMap,https://www.pythonf.cn/read/84436

Python | BitMap算法及其实现的更多相关文章

  1. BitMap算法及其实现(Python)

    BitMap概述 本文介绍 BitMap 算法的应用背景,算法思想和相关实现细节. 概括而言,BitMap 主要用来解决海量数据中元素查询,去重.以及排序等问题.这里对海量数据场景的强调,似乎暗示了这 ...

  2. BitMap算法知识笔记以及在大数据方向的使用

    概述 所谓的BitMap算法就是位图算法,简单说就是用一个bit位来标记某个元素所对应的value,而key即是该元素,由于BitMap使用了bit位来存储数据,因此可以大大节省存储空间,这是很常用的 ...

  3. Python基础算法综合:加减乘除四则运算方法

    #!usr/bin/env python# -*- coding:utf-8 -*-#python的算法加减乘除用符号:+,-,*,/来表示#以下全是python2.x写法,3.x以上请在python ...

  4. BitMap算法应用:Redis队列滤重优化

    工作中有用到Redis滤重队列. 原来的方法如下: 方法一 为了保证操作原子性,使用Redis执行Lua脚本. 在脚本中的逻辑是,如果队列不超过某个数值,进行一次lrem操作(队列使用list结构), ...

  5. bitmap算法

    概述 所谓bitmap就是用一个bit位来标记某个元素对应的value,而key即是这个元素.由于采用bit为单位来存储数据,因此在可以大大的节省存储空间 算法思想 32位机器上,一个整形,比如int ...

  6. xsank的快餐 » Python simhash算法解决字符串相似问题

    xsank的快餐 » Python simhash算法解决字符串相似问题 Python simhash算法解决字符串相似问题

  7. 经典算法题每日演练——第十一题 Bitmap算法

    原文:经典算法题每日演练--第十一题 Bitmap算法 在所有具有性能优化的数据结构中,我想大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量时间,多么的简洁优美, 但是在特定的场 ...

  8. BitMap 算法

    什么是 BigMap 算法 所谓 BitMap 就是用一个 bit 位来标记某个元素对应的 value,而 key 即是这个元素.由于采用bit为单位来存储数据,因此在可以大大的节省存储空间. 算法思 ...

  9. 【算法与数据结构专场】BitMap算法基本操作代码实现

    上篇我们讲了BitMap是如何对数据进行存储的,没看过的可以看一下[算法与数据结构专场]BitMap算法介绍 这篇我们来讲一下BitMap这个数据结构的代码实现. 回顾下数据的存储原理 一个二进制位对 ...

  10. 浅谈bitmap算法

    一.bitmap算法思想 32位机器上,一个整形,比如int a; 在内存中占32bit位,可以用对应的32bit位对应十进制的0-31个数,bitmap算法利用这种思想处理大量数据的排序与查询.  ...

随机推荐

  1. java 405_Http状态405-方法不允许

    解决方法: 删除下列代码. super.doGet(req.resp); super.doPost(req.resp); 分析: 405错误一般指请求method not allowed 错误. 请求 ...

  2. Helm Chart 部署 Redis 的完美指南

    目录 一.Helm介绍 二.安装Helm 三.配置Helm的repository 四.部署chart(以部署redis为例) 1. 搜索chart 2. 拉取chart 3. 修改values.yam ...

  3. 实现 Raft 协议

    文章地址 简介 Raft 是一个分布式共识算法,用于保证所有机器对一件事达成一个看法.本文用于记录实现 Raft 选举和日志复制的代码细节. 选举 节点启动时首先是跟随者状态,如果到达选举超时时间就尝 ...

  4. 支付宝 v3 验签如何实现

    上次给大家介绍了 支付宝 v3 自签名如何实现,这次顺便再把验签也写一下. 为什么要验签 说起为什么要验签,如果要详细一点解释的话,可以写很多很多...... 我们就简单一点来解释:验签可以证明接收到 ...

  5. java断言机制(assert)

    java断言机制(assert) 概述 断言使用的时候不是很多,测试时会使用,springboot中也有使用,总的来说断言还是要慎重. 在Java中,同样也有assert关键字,表示断言 在Java中 ...

  6. Linux应急响应总结——更新中

    Linux应急响应 用户信息 方向 查看可登录的用户: cat /etc/passwd | grep /bin/bash awk -F: '{if($7!="/usr/sbin/nologi ...

  7. C realloc(): invalid next size错误

    C realloc(): invalid next size 问题代码 #include <stdio.h> #include <stdlib.h> int *getNumbe ...

  8. kafka源码阅读之MacBook Pro M1搭建Kafka2.7版本源码运行环境

    原创/朱季谦 最近在阅读Kafka的源码,想可以在阅读过程当中,在代码写一些注释,便决定将源码部署到本地运行. 日常开发过程中,用得比较多一个版本是Kafka2.7版本,故而在MacBook Pro笔 ...

  9. c#5.0/6.0/7.0

    发现很多.net 程序员水平一直停留在c#3.0阶段,现在来整理下c#5.0/6.0/7.0新语法新特性. 人生需要不断充电,不断去get新技能而不是固步自封,对于我自己而言,虽不盲目追求新技术,但每 ...

  10. libGDX游戏开发之NPC敌人事件(六)

    libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm-国内用libgdx比较少,多数情况需要去官网和google找资料,相互学习的可以加我联系方式. ...