位运算

本文详细介绍了位运算的常见用法,包括按位与、按位异或、按位或、取反、位移等操作,并给出了实际应用场景,如判断2的次幂、数值转换、位运算加速技巧等。此外,还探讨了bitset在C++中的使用方法及其优化作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:https://blog.youkuaiyun.com/qq_30076791/article/details/50571194

&   按位与   如果两个相应的二进制位都为1,则该位的结果值为1,否则为0    AND
|    按位或   两个相应的二进制位中只要有一个为1,该位的结果值为1              OR
^   按位异或   若参加运算的两个二进制位值相同则为0,否则为1                     XOR
~   取反   ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变0
<< 左移   用来将一个数的各二进制位全部左移N位,右补0
>> 右移   将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数,高位补0

优先级:   位反(~ )  >  算术  >  位左移、位右移  >  关系运算位与  >  位或  >  位异或  >  逻辑运算

 

异或:

a^b最多等于a+b,最小等于a-b

出现偶数次的数异或值为0 

常见用法

1.去掉最后一位

x>>1

2.在最后加一个0

x<<1

3.在最后加一个1

x<<(1+1)

4.把最后一位变成1

x|1

5.把最后一位变成0

x|1-1

6.最后一位取反

x^1

7.把右数第k位变成1

x|(1<<(k-1))

8.把右数第k位变成0

x&~(1<<(k-1))

9.右数第k位取反

x^(1<<(k-1))

10.取末三位

x&7

11.取k位

x&(1<<k-1)

12.取右数第k位

x>>(k-1)&1

13.把末k位变成1

x|(1<<k-1)

14.末k位取反

x^(1<<k-1)

15.把有边连续的1变成0

x&(x+1)

16.把右起第一个0变成1

x|(x+1)

17.把右边连续的0变成1

1|(x-1)

18.取右边连续的1

(x^(x+1))>>1

19.去掉右起第一个1的左边

x&(x^(x-1))

按位的用途:

  如果需要判断一个数a是否为2的次幂,只需要计算出a&(a-1)的值,如果是0的话该数是2的次幂,反之则反之

(1)清零
若想对一个存储单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合一下条件:

原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可达到清零目的。
例:原数为43,即00101011(2),另找一个数,设它为148,即10010100(2),将两者按位与运算:
 00101011(2)
&10010100(2)
 00000000(2)

(2)取一个数中某些指定位
若有一个整数a(2byte),想要取其中的低字节,只需要将a与8个1按位与即可。
a 00101100 10101100
b 00000000 11111111
c 00000000 10101100

(3)保留指定位:
与一个数进行“按位与”运算,此数在该位取1.
例如:有一数84,即01010100(2),想把其中从左边算起的第3,4,5,7,8位保留下来,运算如下:
 01010100(2)
&00111011(2)
 00010000(2)
即:a=84,b=59     c=a&b=16

按位异或的用途

1)使特定位翻转
设有数01111010(2),想使其低4位翻转,即1变0,0变1.可以将其与00001111(2)进行“异或”运算,

即:
 01111010
^00001111
 01110101
运算结果的低4位正好是原数低4位的翻转。可见,要使哪几位翻转就将与其进行∧运算的该几位置为1

即可。
(2)与0相“异或”,保留原值
例如:012^00=012
        00001010
       ^00000000
        00001010
因为原数中的1与0进行异或运算得1,0^0得0,故保留原数。
(3) 交换两个值,不用临时变量
例如:a=3,即11(2);b=4,即100(2)。
想将a和b的值互换,可以用以下赋值语句实现:
    a=a∧b;
    b=b∧a;
    a=a∧b;
a=011(2)
    (∧)b=100(2)
a=111(2)(a∧b的结果,a已变成7)
    (∧)b=100(2)
b=011(2)(b∧a的结果,b已变成3)
    (∧)a=111(2)
a=100(2)(a∧b的结果,a已变成4)
等效于以下两步:
① 执行前两个赋值语句:“a=a∧b;”和“b=b∧a;”相当于b=b∧(a∧b)。

② 再执行第三个赋值语句: a=a∧b。由于a的值等于(a∧b),b的值等于(b∧a∧b),因此,相当于a=a∧b∧b∧a∧b,即a的值等于a∧a∧b∧b∧b,等于b。
很神奇吧!

位运算加速技巧


1. 如果乘上一个2的倍数数值,可以改用左移运算(Left Shift) 加速 300%

x = x * 2;
x = x * 64;
//改为:
x = x << 1; // 2 = 21
x = x << 6; // 64 = 26

2. 如果除上一个 2 的倍数数值,可以改用右移运算加速 350%

x = x / 2;
x = x / 64;
//改为:

x = x >> 1;// 2 = 21
x = x >> 6;// 64 = 26

3. 数值转整数加速 10%

x = int(1.232)
//改为:

x = 1.232 >> 0;

4. 交换两个数值(swap),使用 XOR 可以加速20%

var t:int = a;
a = b;
b = t;
//equals:
a = a^b;
b = a^b;
a = a^b;

5. 正负号转换,可以加入 300%

i = -i;
//改为
i = ~i + 1; // NOT 写法
//或
i = (i ^ -1) + 1; // XOR 写法


6. 取余数,如果除数为 2 的倍数,可利用 AND 运算加速 600%

x = 131 % 4;
//equals:
x = 131 & (4 - 1);

7. 利用 AND 运算检查整数是否为 2 的倍数,可以加速 600%

isEven = (i % 2) == 0;
//equals:
isEven = (i & 1) == 0;

8. 加速 Math.abs 600% 的写法1,写法2 又比写法1加速 20%

//写法1
i = x < 0 ? -x : x;

//写法2

i = (x ^ (x >> 31)) - (x >> 31);

//写法3

i=x^(~(x>>31)+1)+(x>>31);

9. 比较两数值相乘之后是否拥有相同的符号,加速 35%

eqSign = a * b > 0;
//equals:
eqSign = a ^ b > 0;

其它位运算技巧
1. RGB 色彩分离

var 24bitColor:uint = 0xff00cc;
var r:uint = 24bitColor >> 16;
var g:uint = 24bitColor >> 8 & 0xFF;
var b:uint = 24bitColor & 0xFF;

2. RGB 色彩合并

var r:uint = 0xff;
var g:uint = 0x00;
var b:uint = 0xcc;
var 24bitColor:uint = r << 16 | g << 8 | b;

ps——尤其是在嵌入式中,位运算尤为重要!

应用:

打印数的二进制

void print2jz(ll x)
{
    int c[63];
    int k=0;
    while(x)
    {
        c[k]=x%2;
        k++;
        x=x/2;
    }
    for(int i=k-1;i>=0;i--)
        printf("%d",c[i]);
    printf("\n");
}

1.不查表统计1的数量

ll BitCnt(unsigned ll n) { 
    ll value = 0; 
    while (n) ++value, n &= n - 1; 
    return value; 
}

2.循环位移 

unsigned shift(unsigned now, int n, int N) { //n正表示左移,否则为右移 
    if (n > 0) 
    now = (now >> (N - n)) | (now << n); 
    if (n < 0) { 
        n = -n; 
        now = (now << (N - n)) | (now >> n); 
    } 
    now &= base; 
    return now; 
}

3.k位取反 

a=~(~a^(a<<k))

4.判断n是否是2的整次幂 link

bool fun(ll n){
    return (!(n & (n-1))) && n;
}

5.lowbit(x)是x的二进制表达式中最低位的1所对应的值。
   比如,6的二进制是110,所以lowbit(6)=2。

ll lowbit(ll x){  
return x&(-x);  
}  

6.取出某个数的某一位(2,4,8,16,32等)

bool get_bit(ll t,int x) {
    //    在 t 中,取出第 x 位     --从零开始
    return t & (1<<(x));
}

7.改位

#define set_bit(x,ith,bool) ((bool)?((x)|(1<<(ith))):((x)&(~(1<<(ith)))));
//从零开始
//  设置 x 的从第 ith 位起连续 k 位 为bol
int mset(ll x,int ith,int k,int bol)
{
        while(k --)x = set_bit(x,ith+k,bol);
        return x;
}

8.返回某个数的第x位

int get_01(ll z,int x)
{
    return (z>>x)&1;
}

 

POJ 1324 

POJ 3460

POJ 2286

POJ 2513

bitset

bitset 是STL库中的二进制容器,根据C++ reference 的说法,bitset可以看作bool数组,但优化了空间复杂度和时间复杂度,并且可以像整形一样按位与或。

使用方法
申明
bitset的申明要指明长度

1
bitset<length> bi
这样就申明了一个长度为length的名叫bi的bitset

 赋值
bitset重载了[]运算符,故可以像bool数组那样赋值

bi[2] = 1;
这样就能将第二位赋值为1

常用函数
b1 = b2 & b3;//按位与
b1 = b2 | b3;//按位或
b1 = b2 ^ b3;//按位异或
b1 = ~b2;//按位补
b1 = b2 << 3;//移位
int one = b1.count();//统计1的个数
优化作用
常常碰到处理的数组只有0和1的变化,此时就可以使用bitset优化。比如求两个集合的交集可以使用按位与运算,求并集可以使用按位或运算


常用的成员函数:
b.any() b中是否存在置为1的二进制位?
b.none() b中不存在置为1的二进制位吗?
b.count() b中置为1的二进制位的个数
b.size() b中二进制位数的个数
b[pos] 访问b中在pos处二进制位
b.test(pos) b中在pos处的二进制位置为1么?
b.set() 把b中所有二进制位都置为1
b.set(pos) 把b中在pos处的二进制位置为1
b.reset( ) 把b中所有二进制位都置为0
b.reset( pos ) 把b中在pos处的二进制位置置为0
b.flip( ) 把b中所有二进制位逐位取反
b.flip( pos ) 把b中在pos处的二进制位取反
b.to_ulong( ) 把b中同样的二进制位返回一个unsigned


线段树位运算操作:https://blog.youkuaiyun.com/weixin_41183791/article/details/84791177

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值