Redis和PHP的Bitmap于二进制串的相互转换

Redis和PHP的Bitmap于二进制串的相互转换

场景

错题集的存储,需要有正确的题号id集合,错误的题号id集合,两者并集后在全量题的集合中取反就是未答题号id

选型

基于场景的数据结构设计,有试过列表等,测试结果:bitmap要比列表方式节省10倍的空间使用;列表可以FIND_IN_SET查询,但是bitmap必须整存整取后,在应用端计算。各有优缺点,最后还是选择bitmap节省空间。

原理

将id对应二进制串的位数进行存储,有该id,就将位数的值设置为1,反之为0

操作系统中的位运算,64位的,最大仅支持 0 ~63 之间的位移,但是id没有长度限制。
需要定义一个区间长度,进行拼接。如区间长度:8,id:101的存储位置,即:第 ceil(101/8) 区间的第 101%8 位,这里首位永远是 0,id从正整数开始。

实现

PHP中有实现无限整数的位运算扩展:GMP,对于平常的id使用够用了,但是超大量(id位数上千万甚至亿)的运算会比较耗内存。解决方案也是按照区域划分块,参考:PHP实现Bitmap的探索 - GMP扩展使用


class common_BitMap 
{// bit映射基数:二进制const BIT_BASE = 2;private static $cacheObj;static function inst(): self{return self::$cacheObj = self::$cacheObj ?? new self();}/*** setBit** @param int|string $num* @param int $bit* @param bool $bitVal* @return string*/public static function setBit($num, int $bit, bool $bitVal = true):string{$gmp = gmp_init($num, self::BIT_BASE);gmp_setbit($gmp, $bit, $bitVal); // index starts at 0$res = gmp_strval($gmp, self::BIT_BASE);return $res;}/*** setBits** @param array $ids* @return string*/public static function setBits(array $ids = []): string{if (empty($ids)) {return '';}$max = '1'.str_repeat(0, max($ids));$maxGmp = gmp_init($max, self::BIT_BASE);sort($ids);foreach ($ids as $id) {gmp_setbit($maxGmp, $id);}$res = gmp_strval($maxGmp, self::BIT_BASE);return $res;}/*** getArrByBitStr (这里按照字符串处理)** @param string $bitStr* @return array*/public static function getArrByBitStr(string $bitStr):array{if (empty($bitStr)) {return [];}$collect = [];$len = strlen($bitStr);$str = strrev($bitStr);for ($i = 1; $i <= $len; $i ++) {if (intval($str{$i}) == 1) {$collect[] = $i;}}return $collect;}}$num = '10010001';
$str = common_BitMap::setBit($num, 3, 1);
var_dump($str);
// string(8) "10011001"$arr = [1, 12, 23];
$str = common_BitMap::setBits($arr);
var_dump($str);
// string(24) "100000000001000000000010"$decArr = common_BitMap::getArrByBitStr($str);
var_dump($decArr);
/* 
array(3) {[0]=>int(1)[1]=>int(12)[2]=>int(23)
}
*/

由于大多数都要应用缓存,所以需要存储上兼容redis的bitmap操作,先实现id数组转换

/*** redisSetBit** @param string $key* @return string*/public static function getRedisBitStrByKey(string $key = ''){if (empty($key)) {return '';}$str = of_accy_cache_redis::inst()->get($key);  // 此处基于redis的封装类$res = '';for ($i = 0; $i < strlen($str); $i++) {// 获取每个字节的二进制表示,并去掉前缀 '0b'$byteBinary = str_pad(decbin(ord($str[$i])), 8, '0', STR_PAD_LEFT);$res .= $byteBinary;}return $res;}/*** redisSetBits** @param string $key* @param array $ids* @return string*/public static function redisSetBits(string $key = '', array $ids = []){if (empty($key) || empty($ids)) {return '';}$redis = of_accy_cache_redis::inst();foreach ($ids as $id) {$redis->setBit($key, $id, 1);}$str = $redis->get($key);$res = '';for ($i = 0; $i < strlen($str); $i++) {// 获取每个字节的二进制表示,并去掉前缀 '0b'$byteBinary = str_pad(decbin(ord($str[$i])), 8, '0', STR_PAD_LEFT);$res .= $byteBinary;}return $res;}/*** getBitArrByRedisStr** @param string $bitStr* @return array*/public static function getBitArrByRedisStr(string $bitStr = ''){if (empty($bitStr)) {return [];}$collect = [];$len = strlen($bitStr);for ($i = 1; $i <= $len; $i ++) {if (intval($bitStr{$i}) == 1) {$collect[] = $i;}}return $collect;}
$key = 'bit-test';
of_accy_cache_redis::inst()->setBit($key, 12, 1);
$rStr = common_BitMap::getRedisBitStrByKey($key);
var_dump($rStr);
// string(16) "0000000000001000"$arr = [1 ,12 ,23];
$rStr = common_BitMap::redisSetBits($key, $arr);
var_dump($rStr);
// string(24) "010000000000100000000001"$rArr = common_BitMap::getBitArrByRedisStr($rStr);
var_dump($rArr);
/*
array(3) {[0]=>int(1)[1]=>int(12)[2]=>int(23)
}
*/

对比数组转换后的二进制串差异,发现redis的结果是反序的,如此就有了转换方式。

/*** redisStr2BitStr** @param string $str* @return string*/public static function redisStr2BitStr(string $str = ''){return strrev($str);}
$key = 'bit-test';
$arr = [1 ,12 ,23];$str = common_BitMap::setBits($arr);
var_dump($str);
// string(24) "100000000001000000000010"$rStr = common_BitMap::redisSetBits($key, $arr);
var_dump($rStr);
// string(24) "010000000000100000000001"$bitStr = common_BitMap::redisStr2BitStr($rStr);
var_dump($bitStr);
// string(24) "100000000001000000000010"$bitArr = common_BitMap::getArrByBitStr($bitStr);
var_dump($bitArr);
/*
array(3) {[0]=>int(1)[1]=>int(12)[2]=>int(23)
}
*/

欢迎交流!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/862312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

confluence集成LDAP

一、confluence的权限管理 在集成前&#xff0c;我们必须得知道confluence自身的权限管理是如何做的。 用户组对应空间权限&#xff0c;用户组可以是一个项目&#xff0c;也可以是一个部门或组。 一个用户组里的用户&#xff0c;可以读写本空间的页面&#xff0c;而把其他组隔离…

力扣随机一题 6/28 数组/矩阵

&#x1f4dd;个人主页&#x1f339;&#xff1a;誓则盟约⏩收录专栏⏪&#xff1a;IT 竞赛&#x1f921;往期回顾&#x1f921;&#xff1a;6/27 每日一题关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d…

真实案例解析!企业如何做好安全生产管理工作?

很多企业都有相对应的安全管理制度&#xff0c;安全管理系统&#xff0c;安全管理人员等等&#xff0c;但这都仅限于企业“做了”安全生产管理&#xff0c;并不能“做好”安全生产管理。其实做好安全生产管理需要安全管理系统的配合。 听说过EHS系统吗&#xff1f;这系统能帮企…

百元平价蓝牙耳机哪款好?揭秘百元高性价比蓝牙耳机推荐

如今&#xff0c;市面上的耳机种类繁多&#xff0c;有线、无线、降噪等功能让人眼花缭乱。而对于那些预算有限、但又希望享受无线便捷和高性价比的朋友们来说&#xff0c;百元左右的蓝牙耳机无疑是一个不错的选择。这类耳机不仅能够提供不错的音质体验&#xff0c;同时价格也十…

安宝特分享 | 数字化革命,AR技术打造智慧城市的未来

随着城市化进程的加速和科技创新的不断推进&#xff0c; AR技术正逐步融入智慧城市建设的方方面面&#xff0c;为城市居民提供更智能、便捷、舒适的生活体验&#xff0c;开启了智慧城市的新时代。 01 优化城市规划与建设 AR技术在城市规划和建设中发挥着重要作用。城市规划师和…

可以在Mac电脑玩的拳皇97 for Mac(KOF97) 支持M1

《拳皇97》&#xff08;The King of Fighters 97&#xff09;是一款由SNK公司制作的拳击格斗游戏&#xff0c;于1997年在Arcade平台发布&#xff0c;随后在多个游戏平台上推出。该游戏是《拳皇》系列的第三个作品&#xff0c;继承了前作《拳皇96》的“adius”系统&#xff0c;并…

chrome 配置允许跨域

目录 1.Chrome跨域插件配置 1.1启动插件 1.2. 设置本地调试跨域 2 Firefox跨域插件 2.1. 安装插件 CORS Everywhere 2.2. 启动插件 3 工具下载链接 1.Chrome跨域插件配置 使用chrome插件“Allow CORS: Access-Control-Allow-origin ”来解决跨域问题。 点击pin图标&…

东兴市金顺心贸易有限公司联合越南名厨研发的阿吉贡河粉灵魂汤底料

东兴市金顺心贸易有限公司联合越南名厨研发的阿吉贡河粉灵魂汤底料&#xff0c;一包汤底料竟然就能撑起一家店的灵魂&#xff01;&#x1f372; 简单的食材&#xff0c;却散发出不平凡的美味&#xff0c;仿佛带我穿越千里之外。每一口都是满满的幸福与满足&#xff0c;真心推荐…

element ui 的 el-date-picker 日期选择组件设置可选日期范围

有时候&#xff0c;在使用日历控件的时候&#xff0c;我们需要进行定制&#xff0c;控制用户只能在指定日期范围内进行日期选择&#xff0c;在这里&#xff0c;我使用了 element ui 的 el-date-picker 日期选择控件&#xff0c;控制只能选择当前月及往前的2个月&#xff0c;效果…

java 笔记 第十二章 集合(部分整理细化)

集合概述 &#xff08;1&#xff09;集合是存储其他对象的特殊对象。可以将集合当做一个容器。 &#xff08;2&#xff09;集合的相关接口和类位于java.util包中 &#xff08;3&#xff09;集合中的接口和类是一个整体、一个体系。 集合接口 接口定义了一组抽象方法&#x…

98%企业竟存N日漏洞超5年,新漏洞利用攻击时长极速缩短!

专注推动网络与安全融合的全球网络安全领导者 Fortinet&#xff08;NASDAQ&#xff1a;FTNT&#xff09;&#xff0c;近日发布 FortiGuard Labs&#xff08;Fortinet全球威胁情报响应与研究团队&#xff09;《2023 下半年全球威胁态势研究报告》。本次新发布的半年度研究报告&a…

使用Python进行Socket接口测试

大家好&#xff0c;在现代软件开发中&#xff0c;网络通信是不可或缺的一部分。无论是传输数据、获取信息还是实现实时通讯&#xff0c;都离不开可靠的网络连接和有效的数据交换机制。而在网络编程的基础中&#xff0c;Socket&#xff08;套接字&#xff09;技术扮演了重要角色…

python练习题2

python期考复习题 目录 1. 判断n**2的值每一位互不相同​编辑 2. 密码 3. 图书版号 4. 情感分类矩阵 5. 计算数对个数 1. 判断n**2的值每一位互不相同 def isdiff(n):sstr(n)for i in range(len(s)):for j in range(len(s)):if i!j:if s[i]s[j]:return Falsereturn Truel…

Python输入与输出基础

Python输入与输出基础 引言 Python是一种非常直观且功能强大的编程语言&#xff0c;它允许用户轻松地处理输入和输出操作。无论是从用户那里获取数据&#xff0c;还是将结果展示给用户&#xff0c;Python都提供了简单易用的函数和方法。 一、输入数据 在Python中&#xff0c…

LSTM理解

目录 一、LSTM的本质 二、LSTM的原理 三、LSTM的应用 本文将从LSTM的本质、LSTM的原理、LSTM的应用三个方面&#xff0c;带您一文搞懂长短期记忆网络Long Short Term Memory | LSTM。 一、LSTM的本质 RNN 面临问题&#xff1a;RNN&#xff08;递归神经网络&#xff09;在处理…

数字时代的软件架构:持续架构的兴起与架构师角色的转变

在数字化浪潮的推动下&#xff0c;软件架构领域正经历着前所未有的变革。Eoin Woods在《数字时代的软件架构》演讲中&#xff0c;深入探讨了这一变革&#xff0c;并提出了“持续架构”这一概念。本文将基于Eoin的观点&#xff0c;结合个人理解&#xff0c;探讨持续架构的重要性…

Kali系统的中英文切换

执行命令&#xff1a;sudo dpkg-reconfigure locales 命令作用&#xff1a;重新生成locales配置文件并允许你重新选择所需的语言环境。 中文&#xff1a;zh_CN.UTF-8 UTF-8 英文&#xff1a;en_US.UTF-8 UTF-8 用空格键选中和取消选项。 要设置成中文&#xff1a;取消选择en…

【Git】远程仓库

一、常用的托管服务[远程仓库] 二、注册码云 三、创建远程仓库 四、配置SSH公钥 五、操作远程仓库 5.1、添加远程仓库 5.2、查看远程仓库 5.3、推送到远程仓库 5.4、 本地分支与远程分支的关联关系 5.5、从远程仓库克隆 5.6、从远程仓库中抓取和拉取 5.7、解决合并冲突 一、常…

Labview_Occurrencel(事件发生)

PS&#xff1a;这里遇到 一个很Low的事情&#xff1a; 在停止第二个while循环的时候出现了停止不了的情况。因为等待事件发生设置的超时时间为:-1。所以等事件发生后出现了条件接线端已经执行的情况&#xff0c;所以当下次事件发生时未能及时停止。初版的停止设置如下图&#x…

MMM部署

一.MySQL&#xff0c;MySQL主主复制管理器&#xff09; 是一套支持双主故障切换和双主日常管理的脚本程序。MMM 使用 Perl 语言开发&#xff0c;主要用来监控和管理 MySQL Master-Master &#xff08;双主&#xff09;复制&#xff0c;虽然叫做双主复制&#xff0c;但是业务上同…