位运算简介

位运算​


位运算概述

在现代计算机中所有的数据都以二进制的形式存储在设备中,即 0、1 两种状态。计算机对二进制数据进行的运算 (+、-、*、/) 都叫作 位运算,即将符号位共同参与运算的运算。

例如计算两个数的和:

int a = 35;
int b = 47;
int c = a + b;

因为计算机中都是以二进制来进行运算,所以 int 变量会先在机器内部转换为二进制再进行相加:

35: 0 0 1 0 0 0 1 1
47: 0 0 1 0 1 1 1 1
———————————————————
82: 0 1 0 1 0 0 1 0

相比于在代码中直接使用 (+、-、*、/ ) 运算符,有时合理的运用位运算能提高代码在机器上的执行效率。

另外还需要了解下 源码、反码、补码

(1)正数的源码、反码、补码是一致的;

(2)负数的反码是对源码按位取反,只是最高位(符号位)不变,补码是反码加 1 .

(3)计算机的数字运算均是基于 补码 的。

例如:127 的源码、反码、补码如下所示

127-127
源码0111 11111111 1111
反码0111 11111000 0000
补码0111 11111000 0001

位运算符

位运算符作用于位,并逐位执行操作。

符号描述运算规则
&两个位都为1时,结果才为1
|两个位都为0时,结果才为0
^异或两个位相同为0,不同为1
~取反0变1,1变0
<<左移各二进位全部左移若干位,高位丢弃,低位补0
>>右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

用途

1、按位与

运算规则(全为 1,才为1)

0 & 0 = 00 & 1 = 01 & 0 = 01 & 1 = 1

注意:负数按补码形式参加按位与运算。

(1)清零

如果想将一个单元清零,使其全部二进制位为 0,只要__与一个各位都为零的数值相与__,结果为零。

(2)取一个数的指定位

比如取数 X = 1010 1110 的低 4 位,只需要另找一个数 Y,令 Y 的低 4 位为 1,其余位为 0,即 Y = 0000 1111,然后将 X 与 Y 进行按位与运算(X & Y = 0000 1110)即可得到 X 的指定位。

(3)判断奇偶

只要根据最未位是 0 还是 1 来决定,为 0 就是偶数,为 1 就是奇数。因此可以用 if ((a & 1) == 0) 代替 if (a % 2 == 0) 来判断 a 是不是偶数。

2、按位或

运算规则(全为 0,才为 0)

0 | 0 = 00 | 1 = 11 | 0 = 11| 1 = 1

(1)常用来对一个数据的某些位设置为1

比如将数 X = 1010 1110 的低 4 位设置为 1,只需要另找一个数 Y,令 Y 的低 4 位为 1,其余位为 0,即 Y = 0000 1111,然后将 X 与 Y 进行按位或运算(X | Y = 1010 1111)即可得到。

3、按位异或

运算规则(相同为 0,不同为 1)

0 ^ 0 = 00 ^ 1 = 11 ^ 0 = 11 ^ 1 = 0

(1)翻转指定位

比如将数 X = 1010 1110 的低 4 位进行翻转,只需要另找一个数 Y,令 Y 的低 4 位为 1,其余位为 0,即 Y = 0000 1111,然后将 X 与 Y 进行异或运算(X ^ Y = 1010 0001)即可得到。

(2)与 0 相异或值不变

例如:1010 1110 ^ 0000 0000 = 1010 1110

(3)交换两个数

void swap(int &a, int &b){
    if (a != b){
        a ^= b;
        b ^= a;
        a ^= b;
    }
}
4、按位取反

运算规则(0 变 1,1 变 0)

~1 = 0~0 = 1

(1)使一个数的最低位为 0

使 x 的最低位为 0,可以表示为:a & ~1 。~1 的值为 1111 1111 1111 1110 ,再按 运算,最低位一定为 0。因为 ~ 运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。

5、左移(<<)

定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

设 a = 1010 1110,a = a << 2 将 a 的二进制位 左移2位、右补0,即得 a = 1011 1000。

若左移时舍弃的高位不包含 1,则每左移一位,相当于该数乘以 2 。比如 左移 k 位,即乘上 2k .

6、右移(>>)

定义:将一个数的各二进制位全部右移若干位,正数左补 0 ,负数左补 1 ,右边丢弃。

例如:a = a >> 2 将 a 的二进制位右移 2 位,左补 0 或者 左补 1 得看被移数是正还是负。

操作数每右移一位,相当于该数除以 2。比如 右移 k 位,即除以 2k .


【例题一】n 的二进制表示中第 k 位数

思路:先把第 k 位数字移动到最后一位,n 右移 k 位,即 n >> k ,再看个位是几,用 n & 1 ,合并两步后,即 n >> k & 1 .

例如求 10 的二进制表示,代码如下:

#include <iostream>
using namespace std;

int main()
{
    int n = 10; //10的二进制表示为4位数
    
    for (int k = 3; k >= 0; k--)
        cout << (n >> k & 1);
    
    return 0;
}

输出如下:

1010

【例题二】 801. 二进制中1的个数 - AcWing题库

给定一个长度为 1 的数列,请你求出数列中每个数的二进制表示中 1 的个数。

输入格式

第一行包含整数 n .

第二行包含 n 个整数,表示整个数列

输出格式

共一行,包含 n 个整数,其中的第 i 个数表示数列中的第 i 个数的二进制表示中 1 的个数。

思路:使用 lowbit(x) 来解决,其表达式为 x & -x ,其中 -x 表示补码,即源码取反加 1 ,-x = (~x + 1)
作用:返回 x 的最后一位 1,比如 x = 1010, 则 lowbit(x) = 10x = 101000, 则 lowbit(x) = 1000 .

代码如下:

#include <iostream>
using namespace std;
const int N = 100010;

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

int main()
{
    int n;
    cin >> n;
    
    while (n--){
        int x;
        cin >> x;
        
        int res = 0;
        while (x){
            x -= lowbit(x);
            res++;
        }
        
        cout << res << " ";
    }
    
    return 0;
}

【例题三】90. 64位整数乘法 - AcWing题库

求 a 乘 b 对 p 取模的值。(1 <= a, b, p <= 1018

思路:两个 1018 级别的数相乘会超范围,但两个 1018 级别的数相加不会超范围,所以就可以将乘法转化为加法。即将 a * b 转化为 a + a + a + ... + a (共 b 个 a).

这里利用二进制来简化运算

a * 1 = a
a * 2 = 2a
a * 4 = 4a
a * 8 = 8a
...
a * (2^k) = 2^(k * a)

代码如下:

#include <iostream>
#define ull unsigned long long 
using namespace std;

int main()
{
    ull a, b, p;
    cin >> a >> b >> p;
    
    ull res = 0;
    while (b){
        if (b & 1) //如果个位是1
            res = (res + a) % p;
        
        b >>= 1; // b/2
        a = a * 2 % p;
    }
    
    cout << res << endl;
    
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BraumAce

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

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

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

打赏作者

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

抵扣说明:

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

余额充值