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)
}
*/

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python笔记----少儿编程课程

第1课&#xff1a; 认识新朋友-python 知识点&#xff1a; 1、在英文状态下编写Python语句。 2、内置函数print()将结果输出到标准的控制台上&#xff0c;它的基本语法格式如下&#xff1a; print("即将输出的内容") #输出的内容要用引号引起来&#xff0c;可…

【Ant Design Vue的更新日志】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

SAP 创建BP 提示 CVI_MAPPING 011

原因编号不是外部给号的问题

腾讯云TI平台的大模型精调解决方案

腾讯云TI平台的大模型精调解决方案 随着人工智能和大数据技术的快速发展&#xff0c;大模型在各行各业的应用日益广泛。然而&#xff0c;大规模模型的训练和部署面临着诸多挑战&#xff0c;包括训练资源的高效利用、模型训练的稳定性和国产化适配需求。腾讯云TI平台凭借其强大…

智能网络构建:探索大模型在网络领域的应用

网络领域以其高度复杂性和快速迭代为特点&#xff0c;完成从网络设计、配置、诊断到安全的网络任务需要广泛的专业知识。这些任务的固有复杂性&#xff0c;加上网络技术和协议不断变化的格局&#xff0c;为传统基于机器学习的方法带来了显著的障碍。这些方法在泛化和自动化网络…

已训练好模型如何测试自己数据

1、前言 上一篇博客详细介绍了利用MNIST数据集训练模型,得到了训练参数,那么如何将这训练好的模型,用于训练自己的数据呢?本博客详细介绍,如何利用上篇博客训练好的模型参数,来预测自己的数据集。 2、测试数据 2.1 数据准备 在测试自己数据前,确保你的数据格式与训练时…

【linux/shell案例实战】解决Linux和Windows的换行符CRLF和LF问题

目录 一.什么是Linux 和 Windows 的换行符 CRLF 和 LF 二.使用Linux 中命令 dos2unix 和 unix2dos 实现CRLF 和LF的转换 三.使用 windows 中的代码编辑器实现 CRLF 和 LF 的转换&#xff08;Notepad&#xff09; 一.什么是Linux 和 Windows 的换行符 CRLF 和 LF CR是Carria…

EDA 虚拟机 Synopsys Sentaurus TCAD 2018.06-SP2 CentOS7.9

下载地址&#xff08;制作不易&#xff0c;下载使用需付费&#xff0c;不能接受的请勿下载&#xff09;&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1358rH_Ner1TYdc_TgoXrew?pwdyq3p 提取码&#xff1a;yq3p

瑞文标准IQ智商测验题其中三项

最近在网上看到想尝试一下&#xff0c;看到这三个题目感觉挺有意识&#xff01;&#xff01;&#xff01;

如何进行黄金期货日内波段交易-EE trade

日内波段交易是一种在单个交易日内抓取较大波段趋势的方法&#xff0c;旨在利用市场的短期波动获取利润。黄金期货市场由于其高波动性和高杠杆性&#xff0c;成为日内波段交易的理想选择。以下是黄金期货日内波段交易的详细策略和方法。 一、日内波段交易整体设计思想 1. 顺应…

Redis数据迁移-RedisShake

redis-shake是阿里云Redis团队开源的用于Redis数据迁移和数据过滤的工具。 一、基本功能 redis-shake它支持解析、恢复、备份、同步四个功能 恢复restore&#xff1a;将RDB文件恢复到目的redis数据库。 备份dump&#xff1a;将源redis的全量数据通过RDB文件备份起来。 解析deco…

VUE中,table border属性的不同使用方式

在vue中&#xff0c;使用table&#xff0c;在给table设置边框属性时&#xff0c;按照以往的习惯&#xff1a; <table border"1px solid"><table> 这种写法&#xff0c;产生的效果如下&#xff1a;下边、右边的边框明显和上边、左边不同。border属性设置…

DBA联创:区块链的架构正在不断趋同

原文标题&#xff1a;《We’re All Building the Same Thing》 撰文&#xff1a;Jon Charbonneau&#xff0c;DBA 联创 编译&#xff1a;Tia&#xff0c;Techub News 介绍 这篇文章分析了以下内容 异步执行——为什么高性能集成区块链&#xff08;例如 Solana 和 Monad&…

芒果YOLOv10改进64:主干Backbone篇RepVGG结构:简单但功能强大的卷积神经网络架构

💡本篇内容:YOLOv10改进RepVGG结构:简单但功能强大的卷积神经网络架构 💡🚀🚀🚀本博客 改进源代码改进 适用于 YOLOv10 按步骤操作运行改进后的代码即可 💡本文提出改进 原创 方式:二次创新,YOLOv10 应部分读者要求,新增一篇RepVGG 论文理论部分 + 原创最…

滑动窗口算法——部分OJ题详解

目录 关于滑动窗口 部分OJ题详解 209.长度最小的子数组 3.无重复字符的最长字串 1004.最大连续1的个数Ⅲ 1658.将x减到0的最小操作数 904.水果成篮 438.找到字符串中所有字母异位词 30.串联所有单词的子串 76.最小覆盖子串 关于滑动窗口 其实滑动窗口也是通过双指针…

气膜移动宴会厅:现代化宴会解决方案—轻空间

随着社会经济的发展和人们生活水平的提高&#xff0c;各类宴会、庆典和活动的需求日益增加。然而&#xff0c;传统的宴会厅受限于固定的地点和昂贵的建设成本&#xff0c;无法灵活应对不同规模和地点的需求。在此背景下&#xff0c;气膜移动宴会厅作为一种创新的解决方案&#…

北京小程序开发如何选择开发团队与开发语言?

随着移动互联网的飞速发展&#xff0c;小程序的开发与使用也变得越来越频繁。对于商户来说&#xff0c;市面上的小程序开发团队数量众多又鱼龙混杂&#xff0c;应该如何选择合适的开发团队与开发语言呢&#xff1f; 一&#xff0e; 北京小程序的开发语言的种类及不同 北京小程…

吉时利 Keithley2461 数字源表

Keithley2461吉时利SMU高电流数字源表 2461 型图形化高电流数字 SourceMeter SMU 2461 高电流 SMU 凭借其 10A/1000W 脉冲电流和 7A/100W 直流电流能力以及双 18 位 1MS/s 数字转换器&#xff0c;优化用于检定和测试高功率材料、器件和模块&#xff0c;例如碳化硅 (SiC)、氮化…

修改 app id - 鸿蒙 HarmonyOS Next

修改项目 app id 后通过真机 build run 的时候抛出了如下异常; 项目中更改后的配置与真机的不匹配; {app: {bundleName: "com.xxxxxx.xxx_harmony",vendor: "xxxxxx",versionCode: 1,versionName: "3.5.00",icon: "$media:app_icon",…

期末C语言易错知识点整理

1.在定义多维数组时&#xff0c;除了最左边的维度&#xff0c;其余的维度必须明确指定大小 2.int m[1][4]{4}; 定义的是一个 1 行 4 列的二维数组&#xff0c;初始化时提供了一个元素 4&#xff0c;其余元素默认初始化为 0&#xff0c;因此是正确的。 3.二维数组 a[3][6] 中的索…