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

news2024/11/25 5:21:20

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.coloradmin.cn/o/1872400.html

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

相关文章

Debian linux忘记root密码如何重置

重启电脑, 到下图再按 e 键 在页面中可以看到有个ro的行&#xff0c;在ro行的尾部&#xff0c;添加 rw init/bin/bas 3. ctrl X 启动系统&#xff0c;最后会进入命令行模式 4. 重设root密码&#xff0c;输入命令 passwd root&#xff0c;按照提示输入新密码并确认 5. 重启系…

Spring项目报错解读与全部报错详解

你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客 这是我的 github https://github.com/Qiuner ⭐️ ​ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我…

智能语音热水器:置入NRK3301离线语音识别ic 迈向智能家居新时代

一、热水器语音识别芯片开发背景 在科技的今天&#xff0c;人们对于生活品质的追求已不仅仅满足于基本的物质需求&#xff0c;更渴望通过智能技术让生活变得更加便捷、舒适。热水器作为家庭生活中不可或缺的一部分&#xff0c;其智能化转型势在必行。 在传统热水器使用中&#…

ProfibusDP主站转Modbus模块连接称重仪配置案例

在工业自动化领域&#xff0c;常常需要将不同协议的设备进行连接。比如&#xff0c;将ProfibusDP主站转Modbus模块&#xff08;XD-MDPBM20&#xff09;用于连接称重仪&#xff0c;可以实现不同设备之间的数据交换和通信。ProfibusDP主站转Modbus网关&#xff08;XD-MDPBM20&…

一站式AI服务平台:MaynorAI助您轻松驾驭人工智能

一站式AI服务平台&#xff1a;MaynorAI助您轻松驾驭人工智能 在当前的数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术已经成为推动各行各业创新发展的核心动力。为了更好地满足企业和个人用户对AI服务的需求&#xff0c;MaynorAI 作为一个领先的一站式调用国内…

万物皆可爬——亮数据代理IP+Python爬虫批量下载百度图片助力AI训练

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【导航大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

我用低代码平台自己搭建了一套MES应用系统,1天搞定!

MES系统是什么 MES系统是一套面向制造企业车间执行层的生产信息化管理系统。它能够为操作人员和管理人员提供计划的执行、跟踪以及所有资源&#xff08;包括人、设备、物料、客户需求等&#xff09;的当前状态。通过MES系统可以对从订单下达到产品完成的整个生产过程进行优化管…

2024年6月大众点评深圳餐饮店铺POI分析18万家

2024年6月大众点评深圳餐饮店铺POI共有178720家 店铺POI点位示例&#xff1a; 店铺id G9TSD2JvdLtA7fdm 店铺名称 江味龙虾馆(南山店) 十分制服务评分 8.8 十分制环境评分 8.8 十分制划算评分 8.6 人均价格 128 评价数量 12840 店铺地址 南山大道与桂庙路交叉口西北角…

vue3 【提效】使用 CSS 框架 UnoCSS 实用教程

该换种更高效的方式写 CSS 啦&#xff0c;举个例&#xff1a; <div class"flex"> </div>相当于 <div class"flex"> </div> <style> .flex {display: flex; } </style>当然&#xff0c;还有超多强大的功能帮我们提升…

MySQL事务:ACID特性的实现原理

事务是MySQL等关系型数据库区别于NoSQL的重要方面&#xff0c;是保证数据一致性的重要手段。本文将首先介绍MySQL事务相关的基础概念&#xff0c;然后介绍事务的ACID特性&#xff0c;并分析其实现原理。 一、基础概念 事务&#xff08;Transaction&#xff09;是访问和更新数…

大数据之FlinkCDC

最近在做FLinkCDC数据实时同步的数据抽取处理 目标: 将源端系统Oracle数据库的实时数据通过FLINKCDC的形式抽取到Doris中 问题: 在抽取的过程中,如果表的数据量太大,抽取超过30张表以后,所有的任务大概运行25~30分钟以后,所有的任务的状态会从running 变为 Failed. 解决方案…

RAG开发中常见的12个痛点及解决方案

受到 Barnett 等人论文《构建检索增强生成系统的七大挑战》启发&#xff0c;本文将探讨论文中提及的七大挑战及在开发 RAG&#xff08;检索增强生成&#xff09;流程中常遇到的五个额外难题。更为重要的是&#xff0c;我们将深入讨论解决这些 RAG 难题的策略&#xff0c;以便我…

综合IT运维管理解决方案

综合IT运维管理解决方案 在信息化和数字化高速发展的时代&#xff0c;企业的IT运维管理已经成为保障业务连续性和提升运营效率的关键环节。高效的IT运维管理不仅能够降低运维成本&#xff0c;还能提升服务质量和用户满意度。本文将详细介绍综合IT运维管理解决方案&#xff0c;…

照明物联网:基于网关的智能照明云监控系统解决方案

智能照明系统就是利用物联网技术&#xff0c;将同一空间的照明、空调、新风、排风等系统共同接入物联网平台&#xff0c;实现了“设备互联、数据互通”的智慧物联能力。照明数据、环境监测数据通过网关上传云端&#xff0c;在云端进行统计分析并将结果通过各种终端共享&#xf…

【资源】太绝了!整整16本Python必看书籍详细讲解,适合零基础小白,高清电子版PDF开放下载,带你从入门到入土~

小编为初学Python的朋友们汇总了16本零基础入门书籍&#xff0c;包括Python三剑客等&#xff0c;都是在编程届多年畅销的书籍&#xff0c;也是众多从业者的选择&#xff0c;全文详细介绍了书籍主要内容&#xff0c;有需要的宝子根据自身情况自取 【教程领取方式在文末&#xff…

江科大笔记—FLASH闪存

FLASH闪存 程序现象&#xff1a; 1、读写内部FLASH 这个代码的目的&#xff0c;就是利用内部flash程序存储器的剩余空间&#xff0c;来存储一些掉电不丢失的参数。所以这里的程序是按下K1变换一下测试数据&#xff0c;然后存储到内部FLASH&#xff0c;按下K2把所有参数清0&…

理解MySQL核心技术:外键的概念作用和应用实例

引言 在数据库管理系统&#xff08;DBMS&#xff09;中&#xff0c;外键&#xff08;Foreign Key&#xff09;是维持数据一致性和实现数据完整性的重要工具。本文将详细介绍MySQL外键的基本概念、作用&#xff0c;以及相关的操作指南和应用实例&#xff0c;帮助读者掌握并灵活…

module java.base does not “opens java.lang“ to unnamed module

目录 原因&#xff1a;解决方法&#xff1a;方法一&#xff1a;方法二&#xff1a;方法三&#xff1a; SpringBoot项目运行报如下错误 Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.def…

兴趣爱好广泛的人,如何填报高考志愿选专业?

一般来说&#xff0c;高考填报志愿都要以自己的兴趣为基础。但是对于有一些比较优秀的同学来说&#xff0c;自己的兴趣可能是非常广&#xff0c;涉及到各个专业方方面面。有些同学琴棋书画样样精通&#xff0c;对于很多的专业&#xff0c;他们都充满了兴趣&#xff0c;而且兴趣…

【机器学习】高斯混合模型(Gaussian Mixture Models, GMM)深度解析

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 高斯混合模型&#xff08;Gaussian Mixture Models, GMM&#xff09;深度解析引…