bitmap(请问位图(Bitmap)与矢量图(Vector Graph)的区别)
本文目录
请问位图(Bitmap)与矢量图(Vector Graph)的区别
位图,也叫做点阵图,删格图象,像素图,简单的说,就是最小单位由象素构成的图,缩放会失真。构成位图的最小单位是象素,位图就是由象素阵列的排列来实现其显示效果的,每个象素有自己的颜色信息,在对位图图像进行操作的时候,可操作的对象是每个象素,我们可以改变图像的色相、饱和度、明度,从而改变图像的显示效果。矢量图。
什么是bitmap文件
BIT是位 MAP是图,合起来就是位图~这种格式与jpg等文件想区别的是他不是那种压缩格式,没有牺牲图象的质量,而与PNG等矢量文件相比他就是记录了一张图片里每个“点”的信息,如一个点的颜色,深浅等,而JPG就是压缩了相近的颜色区域而减少了文件大小~而PNG文件呢,就是说他记录的“线”的信息,如一个正方形是4个边组成,每个边是什么颜色的?这个正方形是什么颜色的,等等信息,而不是如实的去记录这个正方形上的每个的点的信息~
BitMap及其在ClickHouse中的应用
问题要从面试或者大数据场景下最常见的一个算法说起,问题是这样的,假如有几十亿个unsigned int类型的数据,要求去重或者计算总共有多少不重复的数据?最简单的办法就是直接利用一个HashMap,进行去重。但是这里面有个内存使用量的问题,几十亿个元素,即使不考虑HashMap本身实现所用到的数据结果,单单key本身,假如每个unsigned int占用4个字节,简单算一下的话,这里都需要几十GB的内存占用,因此,这里就引出了BItMap。 BItMap的思想非常简单,就是用一个bit表示一个二元的状态,比如有或者没有,存在或者不存在,用bit本身的位置信息,对应不同的数据。比如针对上面的问题,我们可以开辟一个2^32 bit的内存空间,每一个bit存储一个unsigned int类型的数据,有就是1,没有就是0,总共需要存储unsigned int类型的最大范围个数据,也就是2^32 个数据,这个2^32其实就是所谓的基数。如下图所示: 假如存在数字8,那就把对应的第8位的值赋为1。上图插入的数据为1、3、7、8。接着依次把所有的数据遍历然后更新这个BitMap。这样我们就可以得到最终结果。 假如上面的问题变成了对几十亿个URL做判断,那应该怎么去做呢?URL没有办法和BitMap的位置关系对应上,所以,我们需要加一层哈希,把每个URL经过哈希运算得到一个整数,然后对应上BitMap。如下图所示: 但是有哈希,肯定会存在碰撞,如果BitMap基数(也就是长度)比较小,那碰撞的概率就大,如果基数比较大,那占用的空间又会比较多。Bloom Filter的思想就是引入多个哈希函数来解决冲突的问题。也就是说对每个URL,经过多个哈希函数的运算,得到多个值,每个数值对应的BitMap的对应的位置都赋值为1。这个两个URL经过多个哈希函数结果还是一样的概率就大大降低。 但是由于依然存在冲突的可能性(其实冲突就是来源于我们BitMap的长度小于了数据量的基数,这也就是牺牲了准确性换来了空间使用的减少),所以Bloom Filter 存在假阳性的概率,不适用于任何要求 100% 准确率的场景,也就是说Bloom Filter 只能用来判无,不能用来判有。比如一个URL经过多次哈希运算之后,发现对应的BitMap的位置都已经是1了,那也不能说明,这个URL之前存在过了,也有可能是哈希冲突的结果。但是一个URL经过多次哈希运算之后,发现对应的BitMap的位置不是都是1,那当前URL之前一定是没有存在过的。 可以看到,Bloom Filter 引入多次哈希,在查询效率和插入效率不变的情况下,用较少空间的BitMap解决大数据量的判断问题。 大部分情况下仅仅做有无的判断是不能满足使用需求的,我们还是需要真正意义上的BitMap(可以方便的用来做交并等计算),但是最好可以在基数比较大的时候,依然可以占用相对比较小的空间。这就是RoaringBitMap所要实现的。 简单来说RoaringBitMap是BitMap的一种带索引的复杂BitMap数据结构。以32位的RoaringBitMap为例,首先划分2^16 个空间(Container),每个Container内部都是一个大小为2^16 bit的BitMap,总的内存使用量还是2^32 = 512Mb。这样的话和普通的BitMap是没有区别的,而RoaringBitMap的创新之处在于每个Container内的BitMap是在没有使用到的情况下是可以不分配内存空间的。这样可以大大减小内存的使用量。 (这个图片是Roaring Bitmaps: Implementation of an Optimized Software Library 论文原图) 要将一个4个字节的数据插入RoaringBitMap,首先要用数据的高16位,找到对应的Container,然后用数据的低16在Container中插入。 在每个Container内部,RoaringBitMap不是简单的用BitMap来进行数据的存储,而是把Container的类型划分为几种,不同的Container用来存储不同情况的数据。 当2个字节(4个字节的原数据,低16位用来插入具体的Container中)的数据,总的个数小于4096个的时候,当前Container使用 array Container。为什么是4096个呢?4096*2B=8Kb,而一个Container如果是bitmap的结构的话,最多也就是2^16bit=8Kb的空间。所以这里当数据个数小于4096使用array Container会更节省空间。当然这里名字为array Container,实际上是链表结构,不需要最开始就初始化4096个short int的数组。 当array Container存储的数到4096个的时候(也就是使用内存到8Kb的时候),array Container会转换为bitmap container,bitmap container就是一个2^16 bit普通的bitmap,可以存储2^16 = 65536个数据。这个8Kb还有一个好处,是可以放到L1 Cache中,加快计算。 这个严格的说,只是一种数据压缩存储方法的实现。其压缩原理是对于连续的数字只记录初始数字以及连续的长度,比如有一串数字 12,13,14,15,16 那么经过压缩后便只剩下12,5。从压缩原理我们也可以看出,这种算法对于数据的紧凑程度非常敏感,连续程度越高压缩率也越高。当然也可以实现其他的压缩方法。 RoaringBitMap其核心就在于加了一层索引,利用复杂的数据结构换取了空间上的效率。需要注意的是这里并没有增加计算的复杂度,其出色的数据结构让其在做交并计算的时候性能也毫不逊色。 ClickHouse中有bloom_filter类型的Skipping indexs,可以方便的用来过滤数据。 ClickHouse实现了大量的BitMap的函数,用来操作BitMap。ClickHouse中的BitMap在32位的时候用的是Set实现的,大于32位的时候也是使用RoaringBitMap实现的。我们这里不看具体的函数,我们来看一个典型的使用场景。 最常见的一个场景是根据标签来进行用户的圈选。常见的解决办法是有一张用户标签表,比如 要查询标签tag1=’xx’和tag2=’xx’的用户需要执行SQL: 但是由于不可能对每个tag列构建一级索引,所以这条SQL执行的效率并不高。可选的一种方式是先构建关于标签的BitMap数据结果,然后进行查询: (1) 创建tag的bitmap表: (2)写入数据 (3)查询 如果有多张tag表,进行交并计算(要比普通的用户表进行JOIN或者IN计算要高效很多):
更多文章:
诺基亚5130保密码忘了怎么办(诺基亚5130手机保密码忘记了怎么找回或更改)
2024年6月18日 19:15
x50pro玩家版和k30pro(三千块钱左右买个啥手机合适啊)
2024年6月22日 13:53
诺基亚n97强制恢复出厂设置(诺基亚N97恢复出厂设置的密码是多少)
2024年3月25日 07:35
天语u86手机(天语u86手机支不支持电信3G卡然后电信网络是不是到处都有)
2024年10月17日 13:30
魅族4怎么恢复出厂设置(魅族4设置里找不到“关于手机”如何重置手机)
2023年12月20日 20:40
三星柱31芯和33芯的区别(金彭前减震朱丽叶31和33点有什么区别)
2023年10月29日 09:30
红米1s屏幕多大(红米1s和红米note1屏幕大小一样吗,就比如说红米note1的手机壳红米1s能用)
2024年7月31日 00:30
iphone4s刷机正在发送基代(iphone4刷机问题,,正在发送restore配置文件)
2023年6月18日 19:30
n5100处理器相当于i几(英特尔赛扬的N5100与N5095哪个处理器更好)
2024年7月24日 10:02