当前位置: 首页 > news >正文

异或运算的高级应用和Briankernighan算法

本篇文章主要回顾一下计算机的位运算,处理一些位运算的巧妙操作。

特别提醒:实现位运算要注意溢出和符号扩展等问题。

先看一个好玩的问题:

$Problem1 $ 黑白球概率问题

袋子里一共a个白球,b个黑球,每次从袋子里拿2个球,每个球每次被拿出的机会均等。如果拿出的是2个白球、或者2个黑球,那么就往袋子里重新放入1个白球,如果拿出的是1个白球和1个黑球,那么就往袋子里重新放入1个黑球,那么最终袋子里一定会只剩1个球,请问最终是黑球的概率是多少?用a与b来表示这个概率。

问题分析:

细心的同学会发现,袋子中的黑球每次要么取出2个,要么取出0个。所以如果袋子里的初始黑球个数是奇数的话,最后一个球一定是黑球,如果是偶数的话,最后一个球一定是白球。我们可以用异或运算来清晰地体现。

异或运算性质

[!NOTE]

  1. 异或运算就是无进位相加。

  2. 异或运算满足交换律、结合律,也就是同一批数字,不管异或顺序是什么,最终结果都是一个。

  3. 0^n = n , n^n = 0

  4. 整体异或和如果是x,整体中某个部分的异或和如果是y,那么剩下部分的异或和是x^y。【区间上异或和】

  5. 通过异或运算来交换两个数(不用新开辟空间)

    a = a ^ b

    b = a ^ b

    a = a ^ b

    三行代码即可交换a 与 b

证明一下第4条性质:

​ 若 a^b=c ,则a = c^b b = c^a。根据异或运算满足交换律与结合律,我们能得到:若一部分的异或和为y,另一部分的异或为x^y,则这两部分的整体异或和为x

回到题目,我们把白球看成0,黑球看成1,根据题目条件,拿出1个白球和1个黑球则放入一个黑球,用数学数字表示就是 $0,1~ -> 1 $,那么全部条件如下:
0 , 1 − > 1 1 , 0 − > 1 0 , 0 − > 0 1 , 1 − > 0 0,1 -> 1\\ 1,0 -> 1\\ 0,0 -> 0\\ 1,1 -> 0\\ 0,1>11,0>10,0>01,1>0
很显然,这就是个异或运算嘛。从袋子里拿出两个数进行异或运算,再把异或运算的结果放入袋子,所以这个题目我们可以抽象成如下情况:

数集中有a个0,b个1,每次从数集中拿出两个数进行异或,再将异或的结果放回到数集中【经过一次这种操作,数集中的数的个数会减1】,经过多次操作之后,最终得到一个数,请问这个数是1的概率?

一次这种操作是一次异或,多次异或放在一起就是异或和问题,异或和符合交换律与结合律,所以最终结果只和1的个数有关,若1的个数为偶

数,最终结果为0,若1的个数为奇数,最终结果为1。

P r o b l e m 2 Problem2 Problem2 不用任何判断语句和比较操作,返回两个数的最大值
问题分析:

这个题目有意思,既然不用判断和比较,那就只能利用计算操作了。但是涉及到比大小,那必然要作差,取两个int型数据 a , b a,b a,b,作差得 c = a − b c = a -b c=ab,如果 c > 0 c>0 c>0,则 a a a大,如果 c < 0 c<0 c<0,则 b b b大。不用能判断,那我们考虑一下位运算:

如果 c < 0 c <0 c<0,则 c c c的符号位为1,对其进行无符号位移(向右移动31位),最终得到结果1。

如果 c > 0 c>0 c>0,则 c c c的符号位为0,对其进行无符号位移(向右移动31位),最终得到结果0。

我们记较大值为 m a x max max c < 0 c < 0 c<0时, m a x = a ∗ 0 + b ∗ 1 max = a*0 + b*1 max=a0+b1 c > 0 c > 0 c>0时, m a x = a ∗ 1 + b ∗ 0 max = a*1 + b*0 max=a1+b0。现在我们把无符号位移之后的 c c c a , b a,b a,b 两个数的系数的关系表建立起来:

cAB
101
010

很容易看出,B = C,A = C ^1。【注意,这里的C是 c c c向右无符号位移31位得到的结果】

详细代码:
//不用任何判断语句和比较操作,返回两个数的最大值
unsigned int sign(unsigned int C) {return C >> 31; //C最后只有0和1两个结果
}
//与1做异或
int flip(unsigned int C) {return C ^ 1;
}
int getMax(int a, int b) {int c = a - b;//强制转换为无符号整数再右移unsigned int C = static_cast<unsigned int>(c);int returnA = flip(sign(C));int returnB = sign(C);return a * returnA + b * returnB;
}

其实这个代码存在一定风险,因为int c = a - b可能会导致溢出情况,同学们可自行思考没有溢出问题的代码,实在暂时没想出来的的可以私信我【手动狗头】。

P r o b l e m 3 Problem3 Problem3 找到缺失的数字

现在存在一个长度为n的数组,也就是说这个数组里有n个数,但是小明只提取出了n-1个数,现在想知道缺失的那个数的大小是多少?

问题分析:

这个问题比较简单,很多同学会第一时间想到哈希表,先遍历这个长度为n的数组,建立数字本身:此数字出现的次数哈希表,之后再遍历一次数组,每遍历一个数字,都在哈希表中减少一次这个数字出现的次数,遍历结束之后,出现次数为-1的那个数字就是缺失的数。

但是这种方法比较复杂,需要遍历两遍数组,还要建立哈希表,查询哈希表,更改哈希表,不仅浪费时间,空间也消耗巨大【建哈希表需要空间】。所以我们想借助一下此篇文章强调的位运算的思想来找更简单的解法。

重新回顾一下这个题目,数组的数字情况有三种:完整的数组,缺失的一个数字,缺失一个数字之后的数组。由此可以联想到异或运算的第四条性质【区间上的异或和】,我们记数组中的数字如下:
a 1 , a 2 , a 3 , a 4 , . . . , a n a_1,a_2,a_3,a_4,...,a_n a1,a2,a3,a4,...,an
记缺失的数字为 a i a_i ai,则剩余的数字为
a 1 , . . . , a i − 1 , a i + 1 , . . . , a n a_1,...,a_{i-1},a_{i+1},...,a_n a1,...,ai1,ai+1,...,an
数组中所有数字的异或和为
A = a 1 ∧ a 2 ∧ . . . ∧ a n A = a_1 \wedge a_2 \wedge ...\wedge a_n A=a1a2...an
缺失了一个数字之后的数组中所有数字异或和为
B = a 1 ∧ . . . ∧ a i − 1 ∧ a i + 1 ∧ . . . ∧ a n B = a_1 \wedge ... \wedge a_{i-1} \wedge a_{i+1}\wedge...\wedge a_n B=a1...ai1ai+1...an
异或运算满足结合律,所以我们很容易知道:
A = B ∧ a i A = B \wedge a_i A=Bai
所以
a i = A ∧ B a_i = A \wedge B ai=AB
以上过程就能直接利用异或运算的性质找出缺失的数字了。

我们直接给出代码:

int findMissingNumber(vector<int> Numbers,vector<int> missingNumbers) {int A = 0;  //存储所有数字异或和for (int i : Numbers) {A = A ^ i;}int B = 0;for (int i : missingNumbers) {B = B ^ i;}return A ^ B;
}
P r o b l e m 4 Problem4 Problem4 找到出现了奇数次的数

数组中有1种数出现了奇数次,其他的数都出现了偶数次,返回出现了奇数次的数。

问题分析:

出现偶数次的数有什么特殊之处呢?我们知道 n ∧ n = 0 n\wedge n = 0 nn=0,所以偶数次的数对最终异或和并没有贡献,只有奇数次的数会对异或和有影响,题目中说明了奇数次的数只有一种,所有最终异或和就是出现奇数次的数本身。

很简单的,以下是解题代码:

int findOddTimesNumber(vector<int> Numbers) {int A = 0;  //存储所有数字异或和for (int i : Numbers) {A = A ^ i;}return A;
}
B r i a n K e r n i g h a n Brian Kernighan BrianKernighan算法

B r i a n K e r n i g h a n BrianKernighan BrianKernighan算法主要是用来获得二进制中最右侧的1,看下面这个例子你就明白了:

现在有一个二进制数(用8个位的数来作例子) 01101000 01101000 01101000,我们想得到最右侧的1,也就是 00001000 00001000 00001000,该进行哪些步骤呢?先简单思考下:

得到最右侧的1,也就是最右侧的1前面的数全部变为0(0->0,1->0),该怎么变呢?现在我们来尝试一下:

  • 与自身作任何操作(与(且),或,异或)都不能使得 01101000 01101000 01101000变成 00001000 00001000 00001000,即使与0作与运算能使 0110 0110 0110变成 0000 0000 0000,但是后面的 1000 1000 1000也会被影响成 0000 0000 0000
  • 先对数取反得到 10010111 10010111 10010111,这时前面的 1001 1001 1001 0110 0110 0110做与运算就可以得到 0000 0000 0000,但是后面的 1000 1000 1000 0111 0111 0111做与运算变成了 0000 0000 0000,我们得想办法不让 1000 1000 1000发生变化。
  • 由于 1000 1000 1000是最右侧的1,所以 1000 1000 1000取反的结果 0111 0111 0111只比 1000 1000 1000小1,因此我们把 10010111 10010111 10010111加上1再和原来的数 01101000 01101000 01101000做与运算,最后会得到 00001000 00001000 00001000

总结一下:

  1. 先对原来的数取反,以便最右侧1之前的数和取反之后的数做与运算能得到 0...0 0...0 0...0
  2. 对于最右侧1以及之后的二进制数 100..0 100..0 100..0,取反之后的数比 100..0 100..0 100..0小1,我们对取反之后的数加上1就可以得到 100..0 100..0 100..0,相同两个数求与自然得到 100..0 100..0 100..0

所以 B r i a n k e r n i g h a n Briankernighan Briankernighan算法:对原来的数取反并加1,再和原来的数做与运算,最终得到最右侧的1。

int Briankernighan(int a) {return a & (~a + 1);
}
P r o b l e m 5 Problem5 Problem5 返回2种出现了奇数次的数

数组中有两种数出现了奇数次,其他的数都出现了偶数次,返回这2种出现了奇数次的数。

问题分析:

这道题算是 P r o b l e m 4 Problem4 Problem4的拓展,我们记数组中的所有数为:
a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an
重复出现的记为1个数。

记2种出现了奇数次的数为 a i , a j a_i,a_j ai,aj,根据 P r o b l e m 4 Problem4 Problem4,我们知道,数组中所有数(算上重复的数)的异或和为 a i ∧ a j a_i \wedge a_j aiaj,但是我们要的是 a i a_i ai a j a_j aj,该怎么找呢?

已知 a i ∧ a j a_i \wedge a_j aiaj是求不出来 a i a_i ai a j a_j aj的,比如二进制数 01001100 0100 1100 01001100可以由 11110000 , 10111100 11110000,10111100 11110000,10111100异或出来,也可以由 00001100 , 01000000 00001100,01000000 00001100,01000000异或出来,但是我们发现,其中的某个1要么在 a i a_i ai中,要么在 a j a_j aj中,不可能同时出现在 a i , a j a_i,a_j ai,aj中。借助上面的 B r i a n K e r n i g h a n Brian Kernighan BrianKernighan 算法,我们拿最右侧的1作为标志来区分 a i , a j a_i,a_j ai,aj

还是拿异或和为二进制数 01001100 01001100 01001100作为例子,它的最右侧的1为 00000100 00000100 00000100,我们从右往左数,第三位为1,这个第三位为1就是标志!!!

正如刚刚讨论的,要么 a i a_i ai的第三位为1,要么 a j a_j aj的第三位为1,但是分析到现在还是求不出 a i , a j a_i,a_j ai,aj,现在我们考虑一下将所有数进行划分,很显然,原来的所有数也可以被划分为两部分:

​ 第三位为1的部分与第三位为0的部分,并且,每一部分都对应一个 a i a_i ai a j a_j aj

对含有 a i a_i ai的部分求异或和不就是 a i a_i ai么, a j a_j aj同理。

详细代码:
pair<int,int> findTwoOddTimesNumbers(vector<int> Numbers) {int eorAll = 0; //a_i ^ a_jfor (int i : Numbers) {eorAll = eorAll ^ i;}int rightOne = Briankernighan(eorAll); //得到最右侧的1int eor1 = 0; //对应位置是0的数的异或和int eor2 = 0; //对应位置是1的数的异或和for (int i : Numbers) {if (i & rightOne == 0) {eor1 = eor1 ^ i;}}eor2 = eorAll ^ eor1;return { eor1, eor2 };
}

相关文章:

异或运算的高级应用和Briankernighan算法

本篇文章主要回顾一下计算机的位运算&#xff0c;处理一些位运算的巧妙操作。 特别提醒&#xff1a;实现位运算要注意溢出和符号扩展等问题。 先看一个好玩的问题&#xff1a; $Problem1 $ 黑白球概率问题 袋子里一共a个白球&#xff0c;b个黑球&#xff0c;每次从袋子里拿…...

音视频入门基础:WAV专题(9)——FFmpeg源码中计算WAV音频文件每个packet的duration和duration_time的实现

一、引言 从文章《音视频入门基础&#xff1a;WAV专题&#xff08;6&#xff09;——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道&#xff0c;通过FFprobe命令可以显示WAV音频文件每个packet&#xff08;也称为数据包或多媒体包&#xff09;的信息&#xff0…...

AI写的论文查重率高吗?分享6款实测AI论文生成免费网站

在当今学术研究和论文写作领域&#xff0c;AI技术的迅猛发展为研究人员提供了极大的便利。特别是AI论文自动生成助手&#xff0c;它们不仅能够提高写作效率&#xff0c;还能帮助生成高质量的论文内容。以下是六款经过实测且免费的AI论文生成网站推荐&#xff1a; 一、千笔-AIP…...

【专题】2024年8月中国企业跨境、出海、国际化、全球化行业报告汇总PDF合集分享(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p37584 在全球化浪潮汹涌澎湃的当下&#xff0c;中国企业积极探索海外市场&#xff0c;开启了出海跨境的新征程。本报告合集旨在全面梳理出海跨境全球化行业的发展态势&#xff0c;涵盖多个领域的深度洞察。 从游戏、快消品、医疗器…...

[算法]单调栈解法

目录 739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 84. 柱状图中最大的矩形 - 力扣&#xff08;LeetCode&#xff09; 739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a; 通常是一维数…...

构建数据安全防线:MySQL数据备份策略的文档化实践

在数据驱动的商业环境中&#xff0c;数据备份策略是确保数据安全和业务连续性的关键。MySQL&#xff0c;作为广泛使用的数据库管理系统&#xff0c;其数据备份策略的文档化对于规范备份流程、提高恢复效率和满足合规要求至关重要。本文将深入探讨如何在MySQL中实现数据备份的策…...

4. GIS前端工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…...

软件测试-Selenium+python自动化测试

目录 会用到谷歌浏览器Chrome测试,需要下载一个Chromedriver(Chrome for Testing availability)对应自己的浏览器版本号选择。 一、元素定位 对html网页中的元素进行定位,同时进行部分操作。 1.1一个简单的模板 from selenium import webdriver from selenium.webdrive…...

SpringBoot与Minio的极速之旅:解锁文件切片上传新境界

目录 一、前言 二、对象存储&#xff08;Object Storage&#xff09;介绍 &#xff08;1&#xff09;对象存储的特点 &#xff08;2&#xff09;Minio 与对象存储 &#xff08;3&#xff09;对象存储其他存储方式的区别 &#xff08;4&#xff09;对象存储的应用场景 三、…...

Java 7.3 - 分布式 id

分布式 ID 介绍 什么是 ID&#xff1f; ID 就是 数据的唯一标识。 什么是分布式 ID&#xff1f; 分布式 ID 是 分布式系统中的 ID&#xff0c;它不存在于现实生活&#xff0c;只存在于分布式系统中。 分库分表&#xff1a; 一个项目&#xff0c;在上线初期使用的是单机 My…...

144. 腾讯云Redis数据库

文章目录 一、Redis 的主要功能特性二、Redis 的典型应用场景三、Redis 的演进过程四、Redis 的架构设计五、Redis 的数据类型及操作命令六、腾讯云数据库 Redis七、总结 Redis 是一种由 C 语言开发的 NoSQL 数据库&#xff0c;以其高性能的键值对存储和多种应用场景而闻名。本…...

基于单片机的自动浇花控制写设计任务书

一、内容要求&#xff1a; 任务 随着社会的进步&#xff0c;人们的生活质量越来越高。在家里养养盆花可以陶冶情操&#xff0c;丰富生活。同时盆花可以通过光合作用吸收二氧化碳&#xff0c;净化室内空气&#xff0c;在有花木的地方空气中阴离子聚集较多&#xff0c;所以空气…...

从零到精通:用C++ STL string优化代码

目录 1:为什么要学习string类 2:标准库中的string类 2.1:string类(了解) 2.2:总结 3:string类的常用接口 3.1:string类对象的常见构造 3.1.1:代码1 3.1.2:代码2 3.2:string类对象的遍历操作 3.2.1:代码1(begin end) 3.2.2:代码2(rbegin rend) 3.3:string类对象的…...

鸿蒙轻内核M核源码分析系列五 时间管理

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 持续更新中…… 在鸿蒙轻内核源码分析上一篇文章中&#xff0c;我们剖析了中断的源码&#xff0c;简单提到了Tick中断。本文会继续分析Tick和时间相关的源…...

Python Opencv鼠标回调

使用 OpenCV 的 cv2.setMouseCallback() 方法来捕捉鼠标事件&#xff0c;并实现以下功能&#xff1a; 实时在鼠标指针附近显示其位置的像素坐标。通过左键双击&#xff0c;将像素坐标记录到数组中。通过右键点击&#xff0c;取消上一次添加的坐标。 下面是实现代码的示例&…...

Ubuntu环境的MySql下载安装

下载压缩包 此文章下载的mysql版本位5.7.29 sudo wget https://downloads.mysql.com/archives/get/p/23/file/mysql-server_5.7.29-1ubuntu18.04_amd64.deb-bundle.tar解压缩 sudo tar -xvf mysql-server_5.7.29-1ubuntu18.04_amd64.deb-bundle.tar命令解释 -x&#xff1a;…...

Android系统去掉WIFI模块

先说应用场景&#xff0c;有些特定设备&#xff0c;不能连接wifi。需要隐藏的模块&#xff0c;QS面板模块的wifi,还有设置里面的wifi.由于QS属于SystemUI&#xff0c;熟悉SystemUI之后&#xff0c;就可以直接去SystemUi那里找&#xff0c;找到QSTitle 默认配置的地方。 一、…...

代码随想录 -- 二叉树 -- 翻转二叉树

226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 递归比较简单 class Solution(object):def invertTree(self, root):if rootNone:returnnode rootif node.left or node.right:tempnode.leftnode.leftnode.rightnode.righttempself.invertTree(node.left)self.inve…...

Node.js之文件复制

1.方式一&#xff1a;readFile // 导入fs模块 const fs require("fs") // 导入process模块 const process require("process")// 读取文件内容 let data fs.writeFileSync(./test.txt) // 写入文件内容 fs.writeFileSync(./test1.txt, data) 2.方式二&…...

新手c语言讲解及题目分享(十六)--文件系统专项练习

在我刚开始学习c语言的时候就跳过了这一章节&#xff0c;但在后面慢慢发现这一章节还是比较重要的,如果你报考了计算机二级c语言的话&#xff0c;你应该可以看到后面的三个大题有时会涉及到这章。所以说这章还是非常重要的。 目录 前言 一.打开文件 1.Fopen( )函数返回值 2&…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

WebRTC调研

WebRTC是什么&#xff0c;为什么&#xff0c;如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...

【记录坑点问题】IDEA运行:maven-resources-production:XX: OOM: Java heap space

问题&#xff1a;IDEA出现maven-resources-production:operation-service: java.lang.OutOfMemoryError: Java heap space 解决方案&#xff1a;将编译的堆内存增加一点 位置&#xff1a;设置setting-》构建菜单build-》编译器Complier...

6.9本日总结

一、英语 复习默写list11list18&#xff0c;订正07年第3篇阅读 二、数学 学习线代第一讲&#xff0c;写15讲课后题 三、408 学习计组第二章&#xff0c;写计组习题 四、总结 明天结束线代第一章和计组第二章 五、明日计划 英语&#xff1a;复习l默写sit12list17&#…...

java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟

众所周知 摄像头取流推流显示前端延迟大 传统方法是服务器取摄像头的rtsp流 然后客户端连服务器 中转多了&#xff0c;延迟一定不小。 假设相机没有专网 公网 1相机自带推流 直接推送到云服务器 然后客户端拉去 2相机只有rtsp &#xff0c;边缘服务器拉流推送到云服务器 …...

【2D与3D SLAM中的扫描匹配算法全面解析】

引言 扫描匹配(Scan Matching)是同步定位与地图构建(SLAM)系统中的核心组件&#xff0c;它通过对齐连续的传感器观测数据来估计机器人的运动。本文将深入探讨2D和3D SLAM中的各种扫描匹配算法&#xff0c;包括数学原理、实现细节以及实际应用中的性能对比&#xff0c;特别关注…...