位运算的应用

内容学习自 https://oi-wiki.org/math/bit/

2的幂

位运算用于2的整数次幂可以优化复杂度

//计算 n * (2^m)
int mulPowerOfTwo(int n, int m) {
    return n << m;
}
//计算 n / (2^m)
int divPowerOfTwo(int n, int m) {
    return n >> m;
}

注意:/ 除法是向 0 取整,而右移是向下取整。

取绝对值

在某些机器上,效率比 n > 0 ? n : -n 高。

int abs(int n) {
    return (n ^ (n >> 31)) - (n >> 31);
}

int 类型占32位,n >> 31 取得 n 的符号,若 n 为正数,结果为 0,若 n 为负数,则结果为 -1。
若 n 为正数,(n ^ 0) - 0 仍是 n;
若 n 为负数,(n ^ -1) - -1) 相当于对 n 按位取反末尾加 1。

取两个数的最大/最小值

在某些机器上,效率比 a > b ? a : b 高。

// 如果 a >= b, (a - b) >> 31 为 0,否则为 -1
int max(int a, int b) { 
    return (b & ((a - b) >> 31)) | (a & (~(a - b) >> 31)); 
}
int min(int a, int b) { 
    return (a & ((a - b) >> 31)) | (b & (~(a - b) >> 31)); 
}

判断两非零数符号是否相同

bool isSameSign(int x, int y) { //有 0 的情况除外
    return (x ^ y) >=0
}

交换两个整数

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

但其实不推荐使用这种方式,推荐临时变量法:

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

对于临时变量法,每次赋值只要读取一个变量的值到寄存器,然后再从寄存器写回到另一个变量中即可,前后涉及两次内存操作;对于异或运算,每次都需要读取两个数据到寄存器中。然后进行运算,将结果写回变量中,前后共需三次内存操作。另外就是异或操作的代码可读性差。

操作一个数的二进制位

// 获取一个数二进制的某一位
// 获取 a 的第 b 位,最低位编号为 0
int getBit(int a, int b) {
    return (a >> b) & 1;
}

// 将一个数二进制的某一位设置为0
// 将 a 的第 b 位设置为 0 ,最低位编号为 0
int unsetBit(int a, int b) { 
    return a & ~(1 << b);
}

//将一个数二进制的某一位设置为 1
// 将 a 的第 b 位设置为 1 ,最低位编号为 0
int setBit(int a, int b) { 
    return a | (1 << b);
}

//将一个数二进制的某一位取反
// 将 a 的第 b 位取反 ,最低位编号为 0
int flapBit(int a, int b) { 
    return a ^ (1 << b); 
}

汉明权重

汉明权重是一串符号中不同于(定义在其所使用的字符集上的)零符号(zero-symbol)的个数。对于一个二进制数,它的汉明权重就等于它 1 的个数(即 popcount)。

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

代码如下:

// 求 x 的汉明权重
int popcount(int x) {
    int cnt = 0;
    while (x) {
        cnt += x & 1;
        x >>= 1;
    }
    return cnt;
}

求一个数的汉明权重还可以使用 lowbit 操作:我们将这个数不断地减去它的 lowbit4,直到这个数变为 0。

代码如下:

// 求 x 的汉明权重
int popcount(int x) {
    int cnt = 0;
    while (x) {
        cnt++;
        x -= x & -x;
    }
    return cnt;
}
posted @   hzyuan  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2022-01-18 【链表】快慢指针的边界问题

喜欢请打赏

扫描二维码打赏

支付宝打赏

点击右上角即可分享
微信分享提示