位运算的实现

         这是C++算法基础-基础算法专栏的第十六篇文章,专栏详情请见此处


ps:转眼间暑假已过半,我在这段时间也积累了很多文章,所以到开学(9月1日)为止,每个周我将会在周三和周六发文章(o゚▽゚)o  

引入

        在计算机中,数据都是用二进制储存的,所以对它们的运算,也就是位运算,它的速度是相当快的。

        下面我们就来讲位运算的实现。

定义

        位运算是基于整数的二进制表示进行的运算。

过程

        位运算一般有三种作用:

  1. 高效地进行某些运算,代替其它低效的方式。

  2. 表示集合(常用于状压DP)。

  3. 题目本来就要求进行位运算。

        而我们目前最常见的就是第1种和第3种作用。

        下面我们通过基本位运算与位运算的应用来讲解。

基本位运算

        基本的位运算共6种,分别为按位与、按位或、按位异或、按位取反、左移和右移(为了方便叙述,下文中省略「按位」)。

        与、或和异或

        与、或和异或都是将两个整数作为二进制数,对二进制表示中的每一位逐一运算。

运算运算符解释
&只有两个对应位都为1时才为1
|只要两个对应位中有一个1时就为1
异或^只有两个对应位不同时才为1

        例如:我们令A=60,B=15,则

A&B:
   0011 1100(60)
&  0000 1111(15)
----------------
   0000 1100(12)

A|B:
   0011 1100(60)
|  0000 1111(15)
----------------
   0011 1111(63)

A^B:
   0011 1100(60)
^  0000 1111(15)
----------------
   0011 0011(51)
        取反

        然后是取反,用符号~表示,作用是将该数字每一位取反(0变成1,1变成0),有符号整数的符号位同样也会被取反。

        例如:我们令A=60,则

~A:
~  0011 1100(60)
-----------------
   1100 0011(-61)

        取反有一个性质,对于数字s,那么\sim s则为-s-1

        左移和右移

        最后是左移和右移,用符号<<和>>表示,作用是将该数字的二进制表示中,向左或向右移动指定位数。

        左移时低位需用0填充,高位越界后则丢弃;而右移高位需用符号位填充,低位越界后则丢弃。

        因此,n<<m=2^{m}nn>>m=\left \lfloor \frac{n}{2.0^{m}} \right \rfloor

        例如:我们令A=60,则

A<<1:
<<  0011 1100(60)
------------------
    0111 1000(120)

A>>1:
>>  0011 1100(60)
-----------------
    0001 1110(30)
        复合赋值运算符

        同+=,-=等运算符类似,位运算也有复合赋值运算符:&=,|=,^=,<<=,>>=(取反是单目运算,所以没有),这些运算很简单,这里就不再讲解。

位运算的应用

        基本的位运算比较简单,但我们要重点学习的是位运算的应用。

        成对变换

        对于非负整数n\bigoplus是异或运算):

  • n为偶数时,n \bigoplus 1= n+1
  • n为奇数时,n \bigoplus 1= n-1

        因此,0与1、2与3、4与5...关于\bigoplus 1操作构成成对变换。这一性质可以在存储与获取无向边时使用。

        lowbit运算

        lowbit运算是指一个数二进制表示下最低位的1连同后面的0,例如n=10可表示为\left ( 1010 \right )_{2},则lowbit\left ( n \right )=\left ( 10 \right )_{2}=2

        lowbit运算的公式为:

lowbit(n)=n&(-n)
        二进制状态压缩(操作一个数的二进制位)

        二进制状态压缩是指操作一个数的二进制位,具体有以下五种常见操作:

        1. 取出整数n在二进制表示下的第k位:

(n>>k)&1

        2. 取出整数n在二进制表示下的第0\sim k-1位(后k位):

n&((1<<k)-1)

        3. 把整数n在二进制表示下的第k位取反:

n^(1<<k)

        4. 对整数n在二进制表示下的第k位赋值为1

n|(1<<k)

        5. 对整数n在二进制表示下的第k位赋值为0

n&(~(1<<k))
        汉明权重

        汉明权重是一串符号中不同于零符号(zero-symbol)的个数。对于一个二进制数,它的汉明权重就等于它位置上为1的个数。

        计算二进制数的汉明权重,可以不断地去掉这个数在二进制下的最后一位(即右移1位),维护一个答案变量,在除的过程中根据最低位是否为1更新答案。

        但是使用lowbit操作是更好的:我们将这个数不断地减去它的lowbit并计数,直到这个数变为0。

        汉明权重的计算代码如下:

int popcount(int x){
    int cnt=0;
    while(x){
        cnt++;
        x-=x&-x;
    }
    return cnt;
}

上一篇-双指针算法的实现(三题详解)    C++算法基础专栏文章    下一篇-离散化的实现


每周六更新一篇文章,内容一般是自己总结的经验或是在其他网站上整理的优质内容

点个赞,关注一下呗~

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值