iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >RoaringBitmap原理及在Go中如何使用
  • 948
分享到

RoaringBitmap原理及在Go中如何使用

2023-07-05 06:07:41 948人浏览 薄情痞子
摘要

这篇文章主要介绍了RoaringBitmap原理及在Go中如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇RoaringBitmap原理及在Go中如何使用文章都会有所收获,下面我们一起来看看吧。引言我们看

这篇文章主要介绍了RoaringBitmap原理及在Go中如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇RoaringBitmap原理及在Go中如何使用文章都会有所收获,下面我们一起来看看吧。

引言

我们看一道老生常谈的面试题

给定含有40亿个不重复的位于[0, 2^32 - 1]区间内的整数的集合,如何快速判定某个数是否在该集合内?

首先,40 亿在存储上我们需要消耗 40亿 * 32 位 = 160 Byte,大致是 16000 MB 即 14.9 GB 的内存,显然这是我们不能接受的。如果你给出的是这个答案,那么你就已经输了!

我们可以用位图来存储,第 0 个 bit 表示数字 0,第 1 个 Bit 表示数字 1,以此类推。如果某个数位于原集合内,就将它对应的位图内的 bit 置为 1,否则保持为 0。这样只占用了 512MB 的内存,不到原来的 3.4%。

我们会发现当数据稀疏的时候,也需要要开辟这么大的内存空间,就发挥不出其存储效率。为了解决位图不适应稀疏存储的问题,RoaringBitmap(咆哮位图)诞生了,因此本文重点探讨它。下面简称 RBM。

1 什么是 RoaringBitmap

是一种基于位图的数据结构,可以高效地存储大量的非负整数,并支持多种集合运算,如并集、交集、差集等。它可以高效地判断一个元素是否在集合中,并且可以使用很少的空间来存储集合。

2 数据结构

源码

short[] keys;Container[] values;int size;

RoaringBitmap 当前有两个版本,分别用来存储 32 位和 64 位整数。以 32 位为例,RBM 会将 32 位的整形(int)拆分成高 16 位和低 16位 两部分来处理。其中

  • 高 16位 会被作为 key 存储到 short[] keys

  • 低 16 位则被看做 value,存储到 Container[] values中的某个 Container 中

keys 和 values 通过下标一一对应。size 则标示了当前包含的 key-value pair的数量,即 keys 和 values 中有效数据的数量。

RoaringBitmap原理及在Go中如何使用

注意:keys 数组永远保持有序,方便二分查找!

3 三种 Container

Container 是 RoaringBitmap的核心,我们结合上面的图会发现每个 32 位整形(int)的高 16 位已经作为key 存储在 RoaringArray 中了,那么 Container 只需要处理低 16 位的数据即可。

3.1 ArrayContainer

源码:

private static final int DEFAULT_INIT_SIZE = 4;private static final int ARRAY_LAZY_LOWERBOUND = 1024;static final int DEFAULT_MAX_SIZE = 4096;private static final long serialVersionUID = 1L;protected int cardinality;short[] content;

从源码可以可以看出 16 位数据 value 直接存储在 short[] content中,因为是数组,始终保持顺序存储且不会重复,有利于二分查找。Container 存储数据没有任何压缩,只适合存储少量数据。

ArrayContainer 占用的空间大小与存储的数据量为线性关系,每个 short 大小为 2 kb,所以存储了 N 个数据的ArrayContainer 占用空间大致为 2N kb。存储一个数据需要占用 2kb,存储 4096 需要占用 8kb。

上面 DEFAULT_MAX_SIZE 值为 4096,可以知道,当容量超过这个值的时候会将当前 Container 替换为BitmapContainer。

3.2 BitmapContainer

源码:

private static final int DEFAULT_INIT_SIZE = 4;private static final int ARRAY_LAZY_LOWERBOUND = 1024;static final int DEFAULT_MAX_SIZE = 4096;private static final long serialVersionUID = 1L;protected int cardinality;short[] content;

BitmapContainer 底层用了 long[] 存储位图数据。RMB 每个 Container处理 16 位整形(int)数据,0~65535,需要 65536 个 bit 来存储数据,每个 bit 位用 1 来表示有,0 来表示无。每个 long 有 64 位,所以需要 1024 个 long 来提供 65536 个 bit。

BitmapContainer 中无论存储了 1 个还是存储了 65536 个数据,其占用的空间都是同样的 8 kb (4096)。

3.3 RunContainer

源码:

private short[] valueslength;int nbrruns;

RunContainer 又称行程长度压缩算法(Run Length Encoding),在连续数据上压缩效果显著。

RunContainer 原理在连续出现的数字,只会记录其初始数字和后续数量,举个例子:

  • 数列 22,它会压缩为 22,0;

  • 数列 22,23,24 它会压缩为 22,3;

  • 数列 22,23,24,32,33,它会压缩为 22,3,32,1;

其中,short[] valueslength中存储的就是压缩后的数据。

可以看出,这种压缩算法在性能和数据的连续性(紧凑性)关系极为密切,

  • 在连续的 100 个 short,可以将 200 字节压缩成 4 个 kb。

  • 对于不连续的 100 个 short,编码完之后会从 200 字节变为 400 kb。

如果要分析RunContainer的容量,我们可以做下面两种极端的假设:

  • 最优情况,只存在一个数据或者一串连续数字,存储 2 个 short 会占用 4 kb。

  • 最差情况,0~65535 的范围内填充所有的不连续数字,(全部奇数位或全部偶数位),需要存储 65536 个short 占用 128 kb。

小结一下:

RoaringBitmap原理及在Go中如何使用

4 Go 使用 RoaringBitmap

Go 语言支持了 RoaringBitmap,安装 roaring 库:

go get -u GitHub.com/RoaringBitmap/roaring// go get -u github.com/RoaringBitmap/roaring/roaring64

RoaringBitmap 支持多种集合运算,包括并集、交集、差集、异或等,这些运算都可以在高效地处理大规模数据集的同时,避免内存溢出和性能问题。

下面介绍一些 RoaringBitmap 集合运算的示例:

4.1 并集运算

// 创建两个 RoaringBitmaprb1 := roaring.NewBitmap()rb2 := roaring.NewBitmap()// 添加元素rb1.Add(1)rb1.Add(2)rb1.Add(3)rb2.Add(3)rb2.Add(4)rb2.Add(5)// 计算并集rb3 := roaring.Or(rb1, rb2)// 输出结果fmt.Println(rb3.ToArray())// Output: [1 2 3 4 5]

4.2 交集运算

// 创建两个 RoaringBitmaprb1 := roaring.NewBitmap()rb2 := roaring.NewBitmap()// 添加元素rb1.Add(1)rb1.Add(2)rb1.Add(3)rb2.Add(3)rb2.Add(4)rb2.Add(5)// 计算交集rb3 := roaring.And(rb1, rb2)// 输出结果fmt.Println(rb3.ToArray())// Output: [3]

4.3 差集运算

// 创建两个 RoaringBitmaprb1 := roaring.NewBitmap()rb2 := roaring.NewBitmap()// 添加元素rb1.Add(1)rb1.Add(2)rb1.Add(3)rb2.Add(3)rb2.Add(4)rb2.Add(5)// 计算差集rb3 := roaring.AndNot(rb1, rb2)// 输出结果fmt.Println(rb3.ToArray())// Output: [1 2]

4.4 异或运算

// 创建两个 RoaringBitmaprb1 := roaring.NewBitmap()rb2 := roaring.NewBitmap()// 添加元素rb1.Add(1)rb1.Add(2)rb1.Add(3)rb2.Add(3)rb2.Add(4)rb2.Add(5)// 计算异或rb3 := roaring.Xor(rb1, rb2)// 输出结果fmt.Println(rb3.ToArray())// Output: [1 2 4 5]

小结一下,RoaringBitmap 可以很方便地进行集合运算,这些运算都可以在高效地处理大规模数据集的同时,避免内存溢出和性能问题。同时,RoaringBitmap 还提供了丰富的 api 接口,支持更多高级的操作和应用场景。

关于“RoaringBitmap原理及在Go中如何使用”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“RoaringBitmap原理及在Go中如何使用”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

--结束END--

本文标题: RoaringBitmap原理及在Go中如何使用

本文链接: https://www.lsjlt.com/news/350304.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c语言怎么读入字符串中的数字符号
    使用 atoi() 函数可以将字符串中的数字转换为整数,具体步骤如下:包含 stdlib.h 头文件。使用 atoi() 函数,用法为:int num = atoi(str);其中 st...
    99+
    2024-05-14
    c语言
  • c语言怎么查找文件中的数据
    c 语言中查找文件中的数据步骤:打开文件;定位到数据点;读取数据;关闭文件。 如何使用 C 语言查找文件中的数据 在 C 语言中,可以通过以下步骤查找文件中的数据: 打开文件:使用...
    99+
    2024-05-14
    c语言
  • c语言怎么求回文数
    c 语言求回文数的方法:1. 读取数字;2. 循环反转数字;3. 比较反转后数字与原始数字;4. 相等则为回文数,否则不是。 如何使用 C 语言求回文数 什么是回文数? 回文数是指从左...
    99+
    2024-05-14
    c语言
  • 如何在 Golang 中从 panic 中恢复?
    在 go 中,使用 recover 函数从 panic 中恢复,该函数从函数栈中返回最近的 panic 值。实战中,可以处理 io.eof 异常,例如:打开文件,使用 defer 捕获 ...
    99+
    2024-05-14
    golang panic
  • c语言怎么判断一个数是整数
    判断一个数在 c 语言中是否为整数:使用 fmod() 函数计算该数除以 1 的余数。如果余数为 0,则该数是整数;否则,该数不是整数。 如何判断一个数在 C 语言中是否为整数 在 C...
    99+
    2024-05-14
    c语言
  • 如何在 Golang 并发环境中处理错误?
    在 go 并发环境中处理错误的有效方法包括:异常处理(panic/recover): 适用于处理意外错误,通过 panic 引发异常,再用 recover 捕获。错误通道: 用来将并发 ...
    99+
    2024-05-14
    golang 错误处理
  • c语言怎么跳出两层for循环
    直接跳出两层 c 语言嵌套 for 循环的方法有三种:使用 break 语句直接跳出。使用 goto 语句跳转到指定标签,继续执行。使用标志变量记录跳出条件,外部 for 循环根据标志值...
    99+
    2024-05-14
    c语言
  • c语言怎么让制表对齐
    c语言中可通过以下两种方法对齐制表符:使用printf()函数的%n和%*c格式说明符计算字节偏移量并插入制表符。使用snprintf()函数将数据写入缓冲区,计算偏移量,写入制表符字符...
    99+
    2024-05-14
    c语言
  • C++ 智能指针和内存管理在高性能服务器架构中的重要性
    在高性能服务器架构中,c++++智能指针通过以下方式优化了内存管理:自动内存释放:超出作用域时释放内存。防止内存泄漏:确保不再需要对象时释放内存。跟踪资源所有权:便于调试和错误处理。常见...
    99+
    2024-05-14
    c++ 内存管理 作用域
  • 用c语言怎么判断素数
    判断素数的方法有蛮力法和埃拉托斯特尼筛法。蛮力法通过逐一尝试从 2 到数字平方根的取模来判断素数,若取模结果有 0 则不是素数。埃拉托斯特尼筛法利用标记数组来跟踪从 2 到给定范围的素数...
    99+
    2024-05-14
    c语言
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作