位运算与组合技

本文详细介绍了计算机中的位运算,包括基本运算符、组合技巧以及在负数处理中的应用。通过位运算实现乘除、交换数值、判断奇偶、改变正负性、取余和统计1的个数等高效编程方法得以展示。
摘要由CSDN通过智能技术生成

一、概述

我们知道,计算机中的数在内存中都是以二进制形式进行存储的 ,而位运算就是直接对整数在内存中的二进制位进行操作,因此其执行效率非常高,在程序中尽量使用位运算进行操作,这会大大提高程序的性能。

二、运算符

符号描述运算规则实例
&两个位都为1时,结果才为1。

0001&0001=1,

0001&0000=0,

0000&0000=0000

|两个位都为0时,结果才为0。

0001∣0001=0001,

0001∣0000=0001,

0000∣0000=0000

^异或两个位相同为0,相异为1。

0001∧0001=0000,

0001∧0000=1,

0000∧0000=0

~取反0变1,1变0。∼0=1,∼1=0
<<左移各二进位全部左移若干位,高位丢弃,低位补0。

0001<<k=0100,k=2,k kk是左移的位数,这里k = 2 k=2k=2

>>右移

各二进位全部右移若干位,对无符号数,高位补0,有符号数,右移补1 11。

0100>>k=0001,k=2,k kk是右移的位数,这里k = 2 k=2k=2

 三、组合技

功能示例位运算
去掉最后一位0100−>0010x>>1
在最后加一个00100−>1000x<<1
在最后加一个10100−>1001(x<<1)+1

将最后一位

变为1

0100−>0101x∣1

将最后一位

变为0

0101−>0100,

这里实际上就是

先确保最低位变为1,

再减去1

(x∣1)−1
最后一位取反

0100−>0101 ,

利用异或性质,

其中除最后一位其余不变。

x∧1

把右数的

第k位变为1

0001−>1001,k=4x∣(1<<(k−1))

把右数的

第k位变为0

1001−>0001,k=4,

这个操作实际上就是

先得到了1000

然后取反得到0111,

最后利用按位与的性质其余位不变,

最高位为0

x&(∼(1<<(k−1)))

把右数的

第k位取反

1000−>0000,k=4,利用异或性质x∧(1<<(k−1))
取末k位1011−>0011,k=2x&((1<<k)−1)

取右数的

第k位

1011−>0001,k=4,

右移k − 1位则是去掉了最后的k − 1位,

我们利用按位与即可将其提取出来

x>>(k−1)&1

把末k位

全变为1

1000−>1111,k=3x∣((1<<k)−1)
把末k位取反0101−>1010,k=4x∧((1<<k)−1)

把右边连续

的1变为0

0111−>0000 ,注意是右起连续的1

x&(x+1)

把右起连续

的0变为1

1000−>1111,注意是右起连续的0

x∣(x−1)
取右边连续的11011−>0011(x∧(x+1))>>1

去掉右起的

第一个1的左边

1101−>0001x&(x∧(x−1))

四、负数的位运算 

首先,我们要知道,在计算机中,运算是使用的二进制补码,而正数的补码是它本身,负数的补码则是符号位不变,其余按位取反,最后再+1得到的, 例如:

15,原码:00001111 补码:00001111

−15,原码:10001111 补码:11110001

那么对于负数的位运算而言,它们的操作都是建立在补码上的,得到的运算结果是补码,最后将补码结果转化成一个普通的十进制数结果。但需要注意的是,符号位是需要参与运算的,而在左移右移操作中,负数右移补1,左移右边补0。例如对于−15,其补码为11110001,右移一位( − 15 > > 1 ) 得到的是11111000,即−8,其他的同理。

五、应用

1、位运算实现乘除法

将x左移一位实现× 2,将x右移一位实现÷ 2。

a < < 1 ≡ a ∗ 2

a > > 1 ≡ a / 2

2、位运算交换两整数  

 void swap(int &a,int &b){
      a ^= b;
      b ^= a;
      a ^= b;
  }

3、位运算判断奇偶数

我们知道,在二进制中,最低位决定了是奇数还是偶数,所以我们可以提取出最低位的值,即与1相与即可实现目的,为0则是偶数,为1则是奇数。

4、位运算改变正负性和求绝对值

int change(int a){
    return ~ a + 1;
}

对于正数而言,补码就是原码,所以按位取反再+ 1 +1+1则得到对应真值负数的补码,而对于负数,其补码进行按位取反再+ 1 +1+1则得到对应真值正数的补码,变为原码。那么知道这个我们就可以特判是否为负数,利用条件表达式就可以根据判断结果求绝对值了。如下:

int abs(int a){
    return ~(a >> 31) ? a : ~a + 1;
}

5、位运算实现对p取余(p为2^k)

int mod(int a,int p){
    return a & (p - 1);
}

取余实际上就是舍去大于等于p的位数,所以我们只需要保留在p范围内的数。由于我们限定了p为2^k,所以(p−1)一定是将小于p的最高位全部变为了1,这样再进行与操作即可得到余数。

6、位运算统计二进制数1的个数

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

对于任意的x,转换成二进制后,是形如这样的数字:a a . . . a a 10...00,从右向左数有任意多个0,直到遇见第一个1,字母a用来占位,代表1左边的任意数字。x−1转换成二进制后,是形如这样的数字:aa...aa01...11,从右向左数,原来的任意多个0 00都变成1 11,原来的第一个1,变成0,字母a部分不变。对x和x−1 进行 按位与 计算,会得到:aa...aa00...00,从右向左数,原来的第一个1 11变成了0 00,字母a部分不变。所以 x&(x−1)相当于消除了 x从右向左数遇到的第一个1。那么,x转换成二进制后包含多少个1,count函数里的循环就会进行多少次,直到x所有的1都被“消除”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hiOoo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值