好玩的位运算

简介: 好玩的位运算

73bda8a097adfa7e007330fbc8786c1f_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

进行枚举

通过移位操作,可以创建互不干扰(比特位置不一样)的变量

type1 = 1
type2 = 1 << 1
type3 = 1 << 2
type4 = 1 << 5

最常见的例子,莫过于在LInux系统中的权限信息了,一般说的777、711、700这些都是位运算运用的结果,由于Linux权限是由三个数字组成的,每1个数字都可以包含三个权限信息执行

exc = 1            # 执行
write = 1 << 1     # 写入
read = 1 << 2      # 读取

在定义出权限值后,我们就可以根据这些权限值来进行组合权限

相加/相减

根据位运算来进行权限的相加和相减

read_exc = exc | read                 # 5
read_exc_write = exc | read | write   # 7

通过或运算符|可以将不同的权限加起来,而搭配使用与运算符&和取反运算符~则可以将某一个权限减去

read_exc_write & (~read)    # 去掉了read权限  3

通过与运算符&则可以检查是否含有某个权限

read_exc = exc | read       # 5
(read_exc & read) == read   # True

获取互补碱基

DNA由A、C、G、T四种碱基组成,首先为其赋值,使用二进制形式来进行赋值

A = 0b00
G = 0b01
C = 0b10
T = 0b11

使用异或运算符^可以直接获得对应碱基的互补碱基

mask = 0b11
A ^ mask == T  # True
G ^ mask == C  # True
C ^ mask == G  # True
T ^ mask == A  # True

选取kmer

在选取kmer时,大多数是使用字符串切片的方法,这种方法的效率不高,可以选择使用位运算的方法来进行kmer的选取。首先,这里假设你的碱基已经是由上面的0b000b010b100b11进行表示,那么对于一条序列而言,他的表示形式就由AGCTGATGCTGATGGATTAG变成了一组数字形式的表示01231031231031103301,下面就使用这条序列,以k=4为例,进行选取kmer。

A = 0b00
G = 0b01
C = 0b10
T = 0b11
map_base = {
    'A':A,
    'G':G,
    'C':C,
    'T':T
}
seq = 'AGCTGATGCTGATGGATTAG'
k = 4
mask = (1 << k * 2) - 1    # 掩码,只保留8个比特位 mask = 0b11111111
kmer_list = []
mer_4 = 0  # 初始化mer_4
for index,s in enumerate(seq,start=1):
    # 每次碱基进来,都将mer_4左移两位,空出最右边的两位用来放新的碱基
    # 然后使用mask掩码保留最后8位,即一个新的4-mer
    # 对于k=4而言,每一个kmer的值不会大于255
    mer_4 = (mer_4 << 2 | map_base[s]) & mask
    # 当mer_4里面的碱基个数大于等于k个的时候
    # 就可以将mer_4保存到kmer_list中
    if index >= k:
        kmer_list.append(mer_4)

对于一个kmer而言,获取他的互补序列,也可以使用异或运算的方法,只是这里不再使用0b11作为掩码,而是使用选取kmer时使用的mask作为掩码。

mask = (1 << k * 2) - 1
kmer ^ mask

获取kmer的反向互补序列

上面的方法我们已经可以获取kmer并且可以获取kmer的互补序列,但一般我们是希望获取kmer的反向互补序列。对于位运算而言,我们第一个互补碱基就不能从右往左放,而是应该从左往右放,这样才能达到反向的目的。

shift = 2 * (k - 1)
kmer = 0
for index,s in enumerate(seq,start=1):
    kmer = (kmer >> 2) | (0b11 ^ map_base[s]) << shift
    if index >= k:
      print(kmer)

这里,我们引入了一个新的变量shift,他的值是2*(k-1),因为,每一个碱基都是由两个比特位来表示的,在放置的时候,首先将kmer右移两位,为下一个kmer留出位置,然后将新的碱基的互补碱基左移shift位,为了将新碱基放到左边空出来的两个位置,最后将两个数字做|运算,这样就可以在选择kmer的反向互补序列了。

bitmap

bitmap是一种利用比特位置来进行去重的一种方法,原理是,将一组数字映射成位置索引,位置上的0或1代表这个数字有无,这样也就是说,记录一个数字的有无只需要一个比特的空间。比如下面这个例子,有一组数字12359,将他们表示到bitmap上就是将,bitmap中的12359位置的值设为1,其他地方设为0。下面使用python实现一下这个bitmap

cd07e669d0a65549f872cb4c8ed3fb59_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

class Bitmap:
    """ bitmap """
    def __init__(self,max,chunk_size = 32):
        """  """
        self.btimap = []
        self.max = max
        self.chunk_size = chunk_size
        self.init_bitmap(max)
    def init_bitmap(self,max):
        """ 创建一个bitmap """
        # 在这里,我们使用分块存储的方案
        # python中一个整型数字的所占用内存空间的大小是根据数字大小来动态分配的
        # 这里,我们规定一个数字最多存储32个比特位
        # |  32位  | 32位 |...| 32位 | 32位 |
        self.bitmap = [0]  * ((max // self.chunk_size) + 1 )
    def clear(self):
        self.btimap = [0]
        return True
    def add(self,number):
        """ 往bitmap中记录一个数字 """
        chunk_idx,bit_idx = self._get_number_idx(number)
        self.bitmap[chunk_idx] |= (1 << bit_idx)
    def delete(self,number):
        """ 从bitmap中删除一个数字 """
        chunk_idx,bit_idx = self._get_number_idx(number)
        self.bitmap[chunk_idx] &= ~(1 << bit_idx)
    def check(self,number):
        """ 检查这个数字是否在bitmap中存在 """
        chunk_idx,bit_idx = self._get_number_idx(number)
        return bool(self.bitmap[chunk_idx] & 1 << bit_idx)
    def _get_number_idx(self,number):
        """ 获取数字在bitmap中的位置 """
        # 返回两个值
        # 第一个值是在第几个数字
        # 第二个是在第几个比特位
        if number > self.max:
            raise ValueError('数字太大了')
        chunk_idx,bit_idx = number // self.chunk_size, number % self.chunk_size
        if bit_idx == 0:
            bit_idx = self.chunk_size
        return chunk_idx, bit_idx
bitmap = Bitmap(100)
bitmap.add(1)
bitmap.add(80)
print(bitmap.check(1))  # True
print(bitmap.check(50)) # False
bitmap.add(32)
print(bitmap.check(32)) # True

从上面的代码和示意图可以发现,bitmap可以做到节省空间的效果,但是其空间是由一组数中最大的一个数字来决定的,这样在存储量较少但含有一个较大的数字时(比如[1,100000]),是不划算的。下面,介绍一个bitmap的优化版本,布隆过滤器。

布隆过滤器

布隆过滤器与bitmap相比,不再是使用数字大小来直接用作索引,而是使用一个散列函数,将一个数字映射成多个索引,也就是说,由几个组合的比特位来记录一个数字是否存在。

42c1096354b1980970594997c307ddd9_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

从上面的图中可以看到,一个数字被映射到多个比特位,在添加一个数字到bitmap中时,需要根据散列函数,将多个比特位设为1,在检查一个数字是否存在时,则需要一次性检查多个比特位。这种方法虽然摆脱了受个别较大数字的影响,但这种方法具有一定的误差和难以删除数字的困难。理论上,一个数字经过散列函数被散列到多个位置,只需要将其中任意一个位置设为0即可达到将该数字从bitmap删除的目的,但是由于散列函数散列效果并不完美,不同的数字,有可能会产生相同的位置,比如上图中那样,当你删除1这个数字时,将第5个比特位设为0虽然可以将1删除,但与此同时9也被删除了。

相关文章
|
数据采集 Web App开发 JSON
浏览器插件:WebScraper基本用法和抓取页面内容(不会编程也能爬取数据)
本文以百度为实战案例演示使用WebScraper插件抓取页面内容保存到文件中。以及WebScraper用法【2月更文挑战第1天】
2247 2
浏览器插件:WebScraper基本用法和抓取页面内容(不会编程也能爬取数据)
嵌入式开发常用的接口和通信协议
本文介绍了嵌入式开发中常见的接口和通信协议,如串口(UART)、COM口、USB口及TTL、RS-232、RS-485电平标准。串口、UART口、COM口和USB口指物理接口,而TTL、RS-232、RS-485则指电平标准。UART通常用于微控制器的串口通信,采用TTL电平;PC的COM口使用RS-232电平。RS-232使用负电压,而RS-485采用差分信号,适合长距离和抗干扰通信。
1238 2
|
3月前
|
人工智能 自然语言处理 测试技术
告别手动画图:用自然语言生成可直接发布的 SVG+PNG 技术图
`fireworks-tech-graph`它把技术图这件事,从一次性手工劳动,变成了一种可以沉淀、复用、批量生成的 Skill 能力。在 AI/Agent 相关内容越来越多的背景下,这是一个很值得试一下的项目。
452 10
告别手动画图:用自然语言生成可直接发布的 SVG+PNG 技术图
|
6月前
|
SQL 存储 安全
PHP应用安全防护——规避常见安全风险
PHP应用在开发和部署过程中,面临着多种安全风险,例如SQL注入、XSS攻击、CSRF攻击、文件上传漏洞、代码注入等,这些安全漏洞可能导致数据泄露、网站被篡改、服务器被入侵等严重后果。
461 0
|
4月前
|
存储 人工智能 固态存储
阿里云4核云服务器租用价格解析:4核8G、4核16G、4核32G配置最新收费标准与活动价格
本文介绍了阿里云4核云服务器的配置选择、价格体系及购买策略。4核配置涵盖经济型e实例、通用算力型u2i/u2a、计算型c9i/c9a、通用型g9及内存型r9等多个实例族,分别适用于个人博客、企业Web应用、AI推理及大数据处理等场景。同时,文中列出了4核8G、16G、32G在各实例下的官方标准价及2026年活动价(如u2i实例4核8G低至1252.63元/年起)。建议用户根据业务需求选型,结合优惠券实现折上折,有效降低上云成本。
|
4月前
|
人工智能 NoSQL API
从 0 到后端闭环: Day2 跑通 Prisma 7 + NestJS + Redis 的实战记录
记录我在 AiTodos Day2 打通后端闭环的过程:完成 Prisma 7 迁移、NestJS + Fastify 接入,以及 AI 资讯/Todo/统计接口和 Redis 日缓存落地。
|
8月前
|
人工智能 供应链 算法
从 “像个玩具” 到 “广告大片”,工业视频的 AIGC 精度终于进化了
AI视频“从 0 到 1” 的一站式交付能力,覆盖创意策划至高精制作的全流程,为企业节省大量时间与人力成本
303 1
|
弹性计算 开发框架 .NET
阿里云服务器购买教程及云服务器地域、实例、操作系统、带宽等参数选择指南
对于初次购买阿里云服务器的用户来说,想使用阿里云服务器搭建网站或者运行APP、小程序等项目,第一步就是要先购买阿里云服务器,下面小编以图文形式给大家介绍一下阿里云服务器的购买流程,以及购买过程中如何云服务器地域、实例、带宽等关键配置和选择这些参数的一些注意事项,以供参考。
|
编译器 C语言 图形学
SCADA系统基于IEC 61131-3标准的编程软件
SCADA系统基于IEC 61131-3标准的编程软件
|
JSON 小程序 前端开发
小程序长列表优化实践
小程序如何实现长列表优化呢
小程序长列表优化实践