“高端”的位运算

封面:“高端”的位运算.png

王有志,一个分享硬核Java技术的互金摸鱼侠
加入Java人的提桶跑路群:共同富裕的Java人

原计划迭代作为预备知识的收尾,不过在解2的幂和4的幂时,想到关于数字2的问题可以通过位运算去解决,因此补充了关于位运算的内容。

“征服”面试官

当我还在校园的时候,听到过一个故事:某位学长去面试腾讯时,要求优化冒泡排序,学长“苦思冥想”后使用位运算交换变量,成功“征服”面试官拿下Offer。
故事我们可以当做段子来看,不过提到的位运算交换变量却值得我们去探究。先来看下“普通”程序员是如何交换变量的:

int a = 3, b = 5;
int temp = a;
a = b;
b = temp;

那么“高端”程序员是如何使用位运算交换变量的呢?

int a = 3, b = 5;
a = a ^ b;
b = a ^ b;
a = a ^ b;

同样是4行代码,使用位运算无需引入临时变量,因此在空间复杂度上更优,并且位运算更靠近计算机,因此在运算效率上更有优势。

位运算

计算机中,数以二进制的形式存储在内存中,而位运算就是对内存中的二进制数进行操作。维基百科中是这样定义的:

位操作是程序设计中对位数组或二进制数的一元和二元操作。在许多古老的微处理器上,位运算比加减运算略快,通常位运算比乘除法运算要快很多。在现代架构中,位运算的运算速度通常与加法运算相同(仍然快于乘法运算),但是通常功耗较小,因为资源使用减少。

既然位运算是操作二进制数的,那么我们有必要先了解计算机中二进制数是如何表示的。

码,反码和补码

计算机中有3种有符号数的表示方法:原码,反码和补码。
原码,反码和补码的共同点是:最高位为符号位,0代表正数,1代表负数。但它们在数值位上有着不同的表示方法。
我们通过十进制数字-11,来展示这3种表示方法的二进制数(使用8位)。

原码表示

使用原码表示时,除了符号位外,数值位和我们通过“除二倒取余法”计算后的数字相同,-11的原码为:1000 1011。
图1:原码表示.png

反码表示

反码是原码和补码之间转换的过渡码,原码转换为反码的规则为:

  • 正数时,反码和原码相同
  • 负数时,符号位保持不变,数值位时原码按位取反

因此,-11的反码表示为:1111 0100。

补码表示

通过反码,我们可以计算出数字的补码,转换规则为:

  • 正数时,补码和反码相同;
  • 负数时,符号位保持不变,数值位时反码数值位加1。

因此,-11的补码表示为:1111 0101。
补码是计算机系统中普遍使用的表示方式,补码相较于原码和反码有两个特点:

  • 符号位可以参与运算
  • 加法和减法可以统一处理

到此为止,我们已经了解了计算机中3种二进制数的表示方式,下面简单的做个总结:当数字为正数时,原码,反码和补码相同
当数字为负数时,原码,反码和补码遵循以下转换规则:
图2:原码,反码和补码的转换.png

位运算符

Java中提供了7种位运算操作符:

符号描述运算规则优先级示例
~取反操作一个数,0变为1,1变为01~ a
<<带符号左移操作两个数,数值位向左移动,高位丢弃,低位补02a << 1
>>带符号右移操作两个数,数值位向右移动,低位丢弃,高位补符号位2a >> 1
>>>无符号右移操作两个数,数值位向右移动,低位丢弃,高位补02a >>> 1
&按位与操作两个数,同为1时,结果为1,否则为03a & b
^按位异或操作两个数,相同为0,不同为14a ^ b
|按位或操作两个数,同为0时,结果为0,否则为15

用一段程序来展示位运算符的基础操作:

int number = -11;// 输出Java中的二进制表示(补码),11111111111111111111111111110101
System.out.println("原值,二进制:" + Integer.toBinaryString(number));// 取反,0变为1,1变为0
System.out.println("取反,十进制" + ~number);
System.out.println("取反,二进制:" + Integer.toBinaryString(~number));// 按位异或,相同为0,不同为1
System.out.println("按位异或" + (number ^ number));// 按位与,同为1时,结果为1,否则为0
System.out.println("按位与:" + (number & 1));// 按位或,同为0时,结果为0,否则为1
System.out.println("按位或:" + (number | ~number));// 左移,数值位向左移动,高位丢弃,低位补0
System.out.println("左移,十进制:" + (number << 1));
System.out.println("左移,二进制:" + Integer.toBinaryString(number << 1));// 右移,数值位向右移动,低位丢弃,高位补符号位
System.out.println("右移,十进制:" + (number >> 2));
System.out.println("右移,二进制:" + Integer.toBinaryString(number >> 2));// 无符号右移,数值位向右移动,低位丢弃,高位补0
System.out.println("无符号右移,十进制:" + (number >>> 1));
System.out.println("无符号右移,二进制:" + Integer.toBinaryString(number >>> 1));

位运算技巧

到目前为止,我们已经了解了二进制数和位运算符,不过这些操作看起平平无奇,好像并没有什么用?那么接下来就介绍一些基础的位运算技巧。

2的幂

还记得2的幂吗?当时使用递归求解,效率上还是有些差强人意的,那么有没有更高效的方法呢?
有一个二进制数字:1101 0101。根据常规的二进制转换为十进制的方法,可以得到如下等式: 1 × 2 7 + 1 × 2 6 + 0 × 2 5 + 1 × 2 4 + 0 × 2 3 + 1 × 2 2 + 0 × 2 1 + 1 × 2 0 = 213 1\times2^7+1\times2^6+0\times2^5+1\times2^4+0\times2^3+1\times2^2+0\times2^1+1\times2^0=213 1×27+1×26+0×25+1×24+0×23+1×22+0×21+1×20=213
显而易见,如果是2的幂,二进制数字中有且仅有1个1。例如:1000 0000。那么判断是否为2的幂就变成了证明二进制数字仅有1个1,或者说是,证明二进制数字中1后面所有位都为0。
如果数字n是2的幂,那么n-1一定是比n少一位,且二进制位都为1的数字。可以利用0&1=0的特性判断是否符合我们的预期,代码如下:

if((n & (n - 1)) == 0) {return true;
} else {return false;
}

奇偶性

此外,在二进制转换为十进制的过程中,还可以得到另外一点信息,即如果二进制的最低位为1,则数字为奇数
那么在判断一个数字的奇偶性时,我们只需要得知二进制数字的最后一位是否为1即可,代码如下:

if ((number & 1) == 0) {System.out.println("偶数");
} else {System.out.println("奇数");
}

快速乘/除2

还是在二进制转换为十进制的过程中,我们可以看到,如果想要乘/除2,只需要向左/右移动一位即可,不过对于奇数来说是除2后向下取整
今天介绍的只是位运算中的基础技巧,算是为大家抛砖引玉。实际上位运算的技巧远不止这些,或者说是二进制数的使用技巧远不止这些。
离我们最近的有Java中ThreadPoolExecutor处理线程状态时使用的技巧,或者叫做位掩码。另外,相信有的小伙伴在面试中被问到过布隆过滤器,这也是一种二进制的进阶用法。
更多的技巧,也可以参考位运算的简单应用和位运算的进阶介绍。

结语

今天的内容到这里就结束了,我们来回顾下都聊了哪些内容:
首先是简单介绍了计算机中的原码,反码和补码,接着是Java中7种位运算操作符,不过并不是所有语言都提供了无符号右移(>>>),最后介绍了一些简单位运算的技巧,但位运算的用法远不止这些,包括听起来很高端的布隆过滤器,也使用了位运算,这也是为什么我说位运算“高端”。
最后补充一篇关于为什么要使用位运算的问答《What are the advantages of using bitwise operations?》,虽然已经过去了11年,但依旧可以作为参考。

练习

因为后面很少会再出现位运算的内容了,因此这次的题目会比较多。
简单难度:

  • 67.二进制求和
  • 136.只出现一次的数字
  • 190.颠倒二进制位
  • 191.位1的个数
  • 231.2的幂
  • 342.4的幂
  • 693.交替位二进制数
  • 剑指 Offer 65.不用加减乘除做加法

中等难度:

  • 36.有效的数独
  • 89.格雷编码
  • 137.只出现一次的数字 II
  • 201.数字范围按位与

如果本文对你有帮助的话,还请多多点赞支持。如果文章中出现任何错误,还请批评指正。最后欢迎大家关注分享硬核Java技术的金融摸鱼侠王有志,我们下次再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/610740.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3D软件坐标系速查【左手/右手】

本文介绍不同3D软件的世界坐标系之间的差异及其工作原理。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 基本上&#xff0c;游戏引擎和3…

Vue3-watch的用法

watch简介 作用:监视数据的变化 (和 Vue2 中的 watch 作用一致) 特点: Vue3 中的 watch 只能监视以下四种数据 1.ref 定义的数据(又可以分 基本 和 对象 ) 2.reactive 定义的数据 3.函数返回一个值。(getter 函数) 4.一个包含上述内容的数组 我们在 Vue3 中使用 watch …

为什么大家都在学鸿蒙开发HarmonyOS?

随着HarmonyOS讨论的话题是越来越火热了&#xff0c;大家学习过程中难免也会想这么一个问题&#xff0c;学习HarmonyOS有哪些发展空间&#xff1f;为什么大家都在学鸿蒙开发HarmonyOS? 学习鸿蒙开发OpenHarmony可以满足市场需求&#xff0c;提升技术能力&#xff0c;并参与到…

研发型企业怎样选择安全便捷的数据摆渡解决方案?

研发型企业在市场经济发展中发挥着至关重要的作用&#xff0c;研发型企业是指以科技创新为核心&#xff0c;以研发新产品、新技术、新工艺为主要业务的企业。这类企业注重技术创新和研发&#xff0c;持续不断地进行技术创新和产品升级&#xff0c;为经济发展注入新鲜的活力。 研…

世邦通信 SPON IP网络对讲广播系统exportrecord.php 任意文件读取漏洞

产品介绍 世邦通信SPON IP网络对讲广播系统采用领先的IPAudio™技术,将音频信号以数据包形式在局域网和广域网上进行传送,是一套纯数字传输系统。 漏洞描述 spon IP网络对讲广播系统exportrecord.php存在任意文件读取漏洞&#xff0c;攻击者可通过该漏洞在服务器端读取任意敏…

Phoenix基本使用

1、Phoenix简介 1.1 Phoenix定义 Phoenix是HBase的开源SQL皮肤。可以使用标准JDBC API代替HBase客户端API来创建表&#xff0c;插入数据和查询HBase数据。 1.2 Phoenix特点 容易集成&#xff1a;如Spark&#xff0c;Hive&#xff0c;Pig&#xff0c;Flume和Map Reduce。性能…

元宇宙电商带你走进数字商城

在当今这个数字化、互联网高速发展的时代&#xff0c;传统的购物方式已逐渐被新兴的电商模式所替代。而在这股变革的浪潮中&#xff0c;一个全新的概念——元宇宙电商&#xff0c;正逐步走入我们的视野&#xff0c;元宇宙概念的兴起成为了热门话题。元宇宙是一个虚拟的世界&…

遥感影像大气校正一:6S模型使用

6S介绍 1986年&#xff0c;法国里尔科技大学大气光学实验室Tanr等人为了简化大气辐射传输方程&#xff0c;开发了太阳光谱波段卫星信号模拟程序5S&#xff08;SIMULATION OF THE SATELLITE SIGNAL IN THE SOLAR SPECTRUM&#xff09;&#xff0c;用来模拟地气系统中太阳…

spring cloud之集成sentinel

写在前面 源码 。 本文一起看下spring cloud的sentinel组件的使用。 1&#xff1a;准备 1.1&#xff1a;理论 对于一个系统来说&#xff0c;最重要的就是高可用&#xff0c;那么如何实现高可用呢&#xff1f;你可能会说&#xff0c;集群部署不就可以了&#xff0c;但事实并…

论文封面表格制作

原文参考&#xff1a;【【论文排版】论文封面完美对齐 强迫症重度患者的经验分享】https://www.bilibili.com/video/BV18f4y1p7hc?vd_source046491acdcff4b39fed20406b36a93e2 视频里up主介绍很详细。我自己也记录一下。 介绍一下如何完成论文封面信息的填写。 创建一个3列…

[足式机器人]Part2 Dr. CAN学习笔记 - Ch02动态系统建模与分析

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Ch02动态系统建模与分析 1. 课程介绍2. 电路系统建模、基尔霍夫定律3. 流体系统建模4. 拉普拉斯变换&#xff08;Laplace&#xff09;传递函数、微分方程4.1 Laplace Transform 拉式变换4.2 收…

Exception sending a multicast message:Network is unreachable故障

出现这个故障就是没有连接到网络&#xff0c;如果虚拟机没有连接到本机&#xff0c;那么就会出现这个情况&#xff0c;当虚拟机连接到本机就会自动消失&#xff0c;同时如果是用电脑直接安装Ubuntu运行也会出现这个情况&#xff0c;应该是要连接到一个路由器里面这个情况才会消…

Eigen 中的传播计算

Eigen: Reductions, visitors and broadcasting

太实用了!关于ControlNet,这篇你一定要看

一篇文章教会你&#xff0c;从入门到使用。 这里是行者AI&#xff0c;我们专注于人工智能在游戏领域的研究和应用&#xff0c;凭借自研算法&#xff0c;推出游戏AI、智能内容审核、数据平台等产品服务。 controlNet控制生成 anypaint 看到这篇文章的你&#xff0c;一定也是AI绘…

SpringBoot整合人大金仓数据库KingBase

1 去KingBase官网下载驱动jar包 2 将解压得到的所有jar包放置在libs目录下&#xff08;没有就新建一个目录&#xff09; 3 在pom文件添加相关依赖 <!--添加KingBase所需要的依赖--> <dependency><groupId>com.kingbase</groupId><artifactId>kin…

DNS解析原理和k8s DNS 实践

1. 问题背景 1.1 域名解析异常 近期开发的一个功能&#xff0c;需要在k8s集群容器环境中调用公司内部api&#xff0c;api提供了内网域名&#xff0c;解析内网域名异常导致请求超时&#xff0c;因此梳理了下DNS的知识点。 可以先看到下面&#x1f447;这段配置&#xff0c;修…

向量数据库:Milvus

特性 Milvus由Go(63.4%),Python(17.0%),C(16.6%),Shell(1.3%)等语言开发开发&#xff0c;支持python&#xff0c;go&#xff0c;java接口(C,Rust,c#等语言还在开发中)&#xff0c;支持单机、集群部署&#xff0c;支持CPU、GPU运算。Milvus 中的所有搜索和查询操作都在内存中执行…

【XR806开发板试用】XR806无线OTA功能实验

前言 非常感谢基于安谋科技STAR-MC1的全志XR806 Wi-FiBLE开源鸿蒙开发板试用活动,并获得开发板试用。 XR806不仅硬件功能多&#xff0c;XR806也提供了功能极其丰富的SDK&#xff0c;几天体验下来非常容易上手。常见的功能几乎都有相应的cmd或demo实现&#xff0c;HAL也做得非…

Apache ActiveMQ RCE漏洞复现

Apache ActiveMQ RCE漏洞复现&#xff08;CNVD-2023-69477&#xff09; 1、漏洞版本 Apache ActiveMQ < 5.18.3 Apache ActiveMQ < 5.17.6 Apache ActiveMQ < 5.16.7 Apache ActiveMQ < 5.15.16 2、POC 通过dnslog验证的poc.xml <?xml version"1.…

SpringBoot:详解Bean装配

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《SpringBoot从官方文档学习》《java项目分享》 《RabbitMQ》《Spring》《SpringMVC》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 前言一、IoC容…