位运算:带带孩子吧,孩子很强的!

2024-09-08 12:44
文章标签 运算 孩子 带带 很强

本文主要是介绍位运算:带带孩子吧,孩子很强的!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

快速进制

在聊到位运算之前,不妨先简单过一遍二进制的东西。熟悉二进制和十进制的快速转换确实是掌握位运算的基础,因为位运算直接在二进制位上进行操作。如果不熟悉二进制表示,很难直观理解位运算的效果。

这里主要涉及二进制和十进制之间的互相转换。


十进制转二进制

十进制转二进制可以使用常见的 除2取余法 进行。每次将十进制除以2并记录所得余数,直到商为0,然后再将记录的余数 从下往上排列即可得到对应的二进制。

将十进制 13 转为二进制:

  • 13 ÷ 2 = 6 余 1 13 ÷ 2 = 6 余 1 13÷2=61
  • 6 ÷ 2 = 3 余 0 6 ÷ 2 = 3 余 0 6÷2=30
  • 3 ÷ 2 = 1 余 1 3 ÷ 2 = 1 余 1 3÷2=11
  • 1 ÷ 2 = 0 余 1 1 ÷ 2 = 0 余 1 1÷2=01

将上面的余数从下往上排得到1101就是13对应的二进制了;是不是so easy too happy!


二进制转十进制

从右往左每一位乘以对应2的幂,然后求和即可;

将二进制1011 转为对应十进制;

  • 1 × 2³ = 8
    0 × 2² = 0
  • 1 × 2¹ = 2
  • 1 × 2⁰ = 1

求和:8 + 0 + 2 + 1 = 1111即为1011的十进制表示;

这些是常规的计算方法,可能大部分人在开始学习的时候多少都接触过,但我们的诉求是尽可能的通过心算的方式快速进行二进制十进制之间的转换,这样才能在使用位运算时得心应手。下面就介绍一下如何练习以及一些常用的技巧;


1248原则

1248原则是一个快速进行二进制到十进制转换的技巧,主要用于帮助我们快速计算二进制数对应的十进制值。这一原则来源于二进制的基本权值,也就是每一位二进制数字代表的2的幂次方值。

从右向左,二进制的各个位的值分别对应着:

  • 第一位:2⁰ = 1
  • 第二位:2¹ = 2
  • 第三位:2² = 4
  • 第四位:2³ = 8

继续向左,依次是16、32、64、128等。

所以1248代表了二进制数前四位的权重:1、2、4、8

当你看到一个二进制数时,可以直接用“1248”来快速确定前几位的权重值,从而进行快速的心算。

例如,转换二进制数 1011 到十进制:

  1. 首先使用

    1248

    原则标记每一位的权值:

    • 最右边一位(1):代表 1
    • 第二位(1):代表 2
    • 第三位(0):代表 4
    • 第四位(1):代表 8
  2. 然后将这些有值的位加起来:1 + 2 + 8 = 11 因此,二进制 1011 对应的十进制数是 11

再举一个例子: 二进制数 11010

  1. 应用 1248原则,最右边开始的前四位的权值是

    1、2、4、8

    ,第五位是16:

    • 最右边第一位是0,代表的值是0
    • 第二位是1,代表的值是2
    • 第三位是0,代表的值是0
    • 第四位是1,代表的值是8
    • 第五位是1,代表的值是16
  2. 将这些有值的位加起来: 16 + 8 + 2 = 26

所以,二进制 11010 对应的十进制数是 26


其他常用技巧

  • 熟记常见的2的幂,这样可以帮你快速推断数值,如(2⁰ = 1,2¹ = 2,2² = 4,2³ = 8,2⁴ = 16,2⁵ = 32...),这可以帮助你快速判断十进制数字在哪个幂次范围内,并由此快速推导出二进制。
  • 熟记小范围内的特殊值,记住一些常见的二进制值,可以进行快速映射。例如:

1111 = 15, 1010 = 10, 1001 = 9, 0111 = 7 等常用值。如果遇到类似的值,可以直接推导,而不需要逐位计算。

  • 如果数字接近2的幂次方,可以快速得到前几位。比如 14 接近 2⁴ = 16,因此前面部分是 1110,只需要从16减去14推导出最后两位的变化。

  • 对于10、100、1000等特殊数字的二进制:这些数字的二进制可以直接记忆,心算时直接转换。

    • 10 的二进制:1010
    • 100 的二进制:1100100
    • 1000 的二进制:1111101000
  • 当遇到大数字时,可以通过分解法来加快心算速度。例如,将一个大数分成几个较小的数字,每个数字转换成二进制后再合并。

    • 例如:75 转为二进制,可以将其分为64(2⁶)+ 8(2³)+ 2(2¹)+ 1(2⁰):
      • 64 的二进制是 1000000
      • 8 的二进制是 1000
      • 2 的二进制是 10
      • 1 的二进制是 1
      • 最终结果为 1001011

位运算

使用位运算之前,务必先熟悉下面的几个运算规则:

与运算(&):将两个数按位进行“与”操作,只有对应位都是1时结果才为1,否则为0。

或运算(|):将两个数按位进行“或”操作,任意一位为1时结果为1,否则为0。

异或运算(^):将两个数按位进行“异或”操作,相同为0,不同为1。

非运算(~):对数值按位取反,0变1,1变0。

左移(<<):将二进制位向左移动n位,相当于乘以2^n。

右移(>>):将二进制位向右移动n位,相当于除以2^n(有符号时保留符号位)。


对应上面的规则,下面通过简单的示例体验一下:

#include <iostream>
using namespace std;int main() {int A = 12;  // 二进制: 1100int B = 10;  // 二进制: 1010// 1. 与运算(&)int andResult = A & B;  // 结果: 8  (二进制: 1000)cout << "A & B = " << andResult << endl;// 2. 或运算(|)int orResult = A | B;  // 结果: 14 (二进制: 1110)cout << "A | B = " << orResult << endl;// 3. 异或运算(^)int xorResult = A ^ B;  // 结果: 6  (二进制: 0110)cout << "A ^ B = " << xorResult << endl;// 4. 非运算(~)int notResult = ~A;  // 结果: -13 (按位取反后的二进制表示: 11111111111111111111111111110011)cout << "~A = " << notResult << endl;// 5. 左移运算(<<)int leftShift = A << 2;  // A 向左移 2 位,结果: 48  (二进制: 110000)cout << "A << 2 = " << leftShift << endl;// 6. 右移运算(>>)int rightShift = A >> 2;  // A 向右移 2 位,结果: 3  (二进制: 0011)cout << "A >> 2 = " << rightShift << endl;return 0;
}

实际应用

为什么会突然想到写这么一篇文章也不是突发奇想。主要是最近在游戏开发过程中遇到了一个常见的需求,比如游戏界面的红点系统。由于判定一个界面是否需要显示红点,这些逻辑需要后端去处理,然后返回一个是否显示的标记给前端进行具体的渲染流程。

实现这个需求的方法很多,比较常见的做法是后端处理完之后将红点状态以表的形式存起来再通过请求发送到前端,但是这样做会加大前后端交互过程中数据传输的数据量,可能会导致数据传输超上限、服务器压力增加、后端计算成本提高等潜在的问题,毕竟一个游戏功能的界面的红点数是有可能多达几十上百个的,而且每次都需要后端不断的进行存表读表的操作,确实不够优雅。

所以才想到基于二进制特有的01标记来作为状态标志位,使用位运算实现一个高效简洁的红点效果。

通过使用位运算,我们只需要将每个位置对应的红点位置为1,不显示红点时置为0即可,而在向前端传输红点状态数据时就不需要来传表结构的数据了,只需要一个简单的十进制阿拉伯数字即可,将该传输传到前端结果位运算简单的解析之后即可获得红点状态信息。

上面只是一部分应用,其实在实际开发中,还有很多地方可以用到位运算,或者是使用位运算提高性能,简化代码的地方。比如

状态标志位

在嵌入式开发、游戏开发、网络通信等场景中,经常需要用位表示多个状态(如开关、错误标志)。通过位运算可以快速检查和设置这些状态。比如上面的红点系统。

const int FLAG_A = 1 << 0;  // 0001
const int FLAG_B = 1 << 1;  // 0010
const int FLAG_C = 1 << 2;  // 0100int flags = FLAG_A | FLAG_C;  // 具有 FLAG_A 和 FLAG_C 的状态// 检查 FLAG_B 是否设置
if (flags & FLAG_B) {cout << "FLAG_B is set" << endl;
} else {cout << "FLAG_B is not set" << endl;
}

应用场景:设备状态监控、通信协议标志。


数据压缩

位运算用于将多个小数值打包在一个更大的数据类型中,节省空间。例如,在图像处理、视频编码等领域,经常通过位运算来压缩颜色、透明度等多个通道的数据。

unsigned int color = (255 << 24) | (128 << 16) | (64 << 8) | 32;
// 将 RGBA 值分别存储在 32 位整数的不同段

哈希运算和校验

在哈希表实现、数据加密、CRC 校验中,位运算可以加速特定运算,避免浮点数操作,提高性能。

示例:
cppCopy codeunsigned int hashFunction(unsigned int x) {return x ^ (x >> 16);  // 异或和右移结合生成哈希
}

位图/布隆过滤器

位运算用于表示集合或快速查找元素是否存在。位图通过每一位表示一个元素是否存在,布隆过滤器则使用多个哈希函数来进行快速查找。

const int SIZE = 100;
int bitmap[SIZE / 32] = {0};  // 位图数组,每32位存储一个intvoid setBit(int index) {bitmap[index / 32] |= (1 << (index % 32));  // 设置位
}bool getBit(int index) {return bitmap[index / 32] & (1 << (index % 32));  // 检查位
}

高效计算(高频常用)

位运算可以代替乘法、除法、模运算,奇偶性判断等操作,提高效率,尤其在性能敏感的嵌入式系统、图形编程中广泛使用。

int x = 16;
int y = x << 1;  // x * 2
int z = x >> 1;  // x / 2// 判断奇偶性
number & 1 == 0 // 偶数
number & 1 == 1 // 奇数    

循环队列/环形缓冲区

使用位运算来快速处理循环队列的索引更新,尤其在队列的大小是2的幂时,可以通过与运算快速获取索引的值。

const int SIZE = 8;  // 大小为2的幂
int buffer[SIZE];
int head = 0;
int tail = 0;void enqueue(int value) {buffer[tail] = value;tail = (tail + 1) & (SIZE - 1);  // 位运算获取下一个索引
}

小结

可以的话,多用用位运算吧,这孩子还是很强的!

这篇关于位运算:带带孩子吧,孩子很强的!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1148148

相关文章

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

uva 575 Skew Binary(位运算)

求第一个以(2^(k+1)-1)为进制的数。 数据不大,可以直接搞。 代码: #include <stdio.h>#include <string.h>const int maxn = 100 + 5;int main(){char num[maxn];while (scanf("%s", num) == 1){if (num[0] == '0')break;int len =

【Java中的位运算和逻辑运算详解及其区别】

Java中的位运算和逻辑运算详解及其区别 在 Java 编程中,位运算和逻辑运算是常见的两种操作类型。位运算用于操作整数的二进制位,而逻辑运算则是处理布尔值 (boolean) 的运算。本文将详细讲解这两种运算及其主要区别,并给出相应示例。 应用场景了解 位运算和逻辑运算的设计初衷源自计算机底层硬件和逻辑运算的需求,它们分别针对不同的处理对象和场景。以下是它们设计的初始目的简介:

快速幂运算的一些模板

这里用递归和循环两种做法来做。 简单来说,快速幂就是把底数扩大,指数缩小,比如2*2=4;计算2的幂时,就可以转换成4的幂来运算,这样可以避免在计算大的数据时爆int的现象  //递归int power(int a,int n){int ans;if(n==2) ans=1;else{ans=power(a*a,n/2);if(n%2==1) ans*=a;}return ans;}

高精度计算----减法运算(浮点型)

基于上一贴,修改减法运算适合于高精度浮点型计算。 因为减法比加法难度大一点,考虑的地方也要多一些,可能代码有欠缺,欢迎指出。 运算说明: 1、相减函数依旧没改变,包括上一贴的判断被减数与减数的大小函数也没变。 2、增加两个函数,取小数位数函数和结果处理(回归小数点)函数 3、与加法浮点高精度运算相比,这里改变较多的是结果处理函数,加法加完后,位数不减反增,而且最多增一位。减法会消失掉好多

高精度计算----减法运算

处理大数减法运算: 1、首先要判断被减数与减数哪个更大,再相应的带入减法函数去处理。具体的比较可以使用字符串的相关知识去比较。 2、相减要先对齐数组,依照减数的长度,执行相应的减法运算次数。 3、不需要借位相减的话,直接减去;需要的话,向前借一位,若前一位是0,则再前借(此时前一位的0变为10)。 测试程序效果如下:   以下代码包括相减函数,比较被减数减数函数,若有错误,请指出:

Python中的位运算-从入门到精通

你是否曾经好奇过计算机是如何在底层处理数据的?或者,你是否想知道为什么有些程序员总是津津乐道于位运算的强大?如果是,那么你来对地方了!今天,我们将深入探讨Python中的位运算,揭示它们的神奇之处,以及如何利用它们来优化你的代码。 目录 位运算:计算机的秘密语言为什么位运算重要? Python中的位运算操作符1. 按位与 (&)2. 按位或 (|)3. 按位异或 (^)4. 按位取反 (~

【OpenCV2.2】图像的算术与位运算(图像的加法运算、图像的减法运算、图像的融合)、OpenCV的位运算(非操作、与运算、或和异或)

1 图像的算术运算 1.1 图像的加法运算 1.2 图像的减法运算 1.3 图像的融合 2 OpenCV的位运算 2.1 非操作 2.2 与运算 2.3 或和异或 1 图像的算术运算 1.1 图像的加法运算 add opencv使用add来执行图像的加法运算 图片就是矩阵, 图片的加法运算就是矩阵的加法运算, 这就要求加法运算的两张图shape必须是相同的. # 图片加法imp

c语言(基本运算)

算术运算符的基本使用: #include <stdio.h>int main(){int a = 10 + 1 + 2 - 3 + 5;printf("10 + 1 + 2 - 3 + 5 = %d\n", a); //10 + 1 + 2 - 3 + 5 = 15int b = -10;printf("b = %d\n", b); //b = -10int c = 10 * b;prin

Matlab simulink建模与仿真 第八章(数学运算库)【下】

参考视频:simulink1.1simulink简介_哔哩哔哩_bilibili 六、圆整函数及最值函数模块 1、Rounding Function圆整函数模块 圆整函数模块的功能是将小数转换为整数,它提供了四种取整方式: ①floor:向下取整,输出距离输入小数在负无穷方向上最近的整数。 ②ceil:向上取整,输出距离输入小数在正无穷方向上最近的整数。 ③round:四舍五入,输出距