幂运算 数组_无阻通关位运算——教你如何快速掌握位运算

a217fd24cb553fa1a040a18a62aba8fb.gif

无协干货又来了! 458bdcecf3948a57dda445c45846bbdd.gif位运算

相信位运算大家都不陌生,我们常采用位运算来处理二进制数。但是你真的了解位运算吗?你知道位运算有什么作用吗?你想要写出效率更高,更加底层的代码吗?本次的无线电协会技术推送,我们就来了解一下位运算的“骚操作”。

458bdcecf3948a57dda445c45846bbdd.gif

首先,让我们来回顾一下有哪些位运算符:

1.& 按位与:都为1,则结果为1,否则为0
2. |  按位或:只要有一个为1,则结果为1,否则为0
3.^ 按位异或:值不同为1,否则为0
4.~ 取反:将0变1,将1变0
5.< 6.>>X 右移:该书右移X位,移到右端的低位被舍弃,对于无符号数,高位补0

e11dadcd800364027411f60d0467918f.gif

我们都知道,计算机储存数据是以二进制形式储存,而直接使用十进制数进行运算时要进行两个步骤的转换,进行一些复杂的运算时还需要更多步的转换,但是用位运算时就不用这么多步的转换,所有的运算均在二进制下完成,可以提高运算效率。

458bdcecf3948a57dda445c45846bbdd.gif 0d24c0eb0bbd5f632dccb1b5dbb53837.gif

我们先来了解一下一些常用的位运算符的作用吧!

0d24c0eb0bbd5f632dccb1b5dbb53837.gif01

按位异或

作用1:两变量换值

传统换值,我们需要引入第三个变量temp,那需要额外占用内存,效率低下:

temp=a;

a=b;

b=temp;

而有了按位异或,我们有了一种效率更高的换值方法

a=a^b;

b=a^b;

a=a^b;

只需重复进行三次a^b,即可不用中间变量temp完成换值,原理如下:

第一次取a^b时,求出了a和b不同的情况,并将不同的情况覆盖在a上;

第二次取a^b时,将b与  a和b不同的情况求异或,此时可以得到a的值(请仔细思考这是为何),将a的值写入b中;

第三次取a^b时,同理,将原来a的值(此时b的值就是原来a的值)与 a和b不同的情况求异或,即可得到b,将b的值写入a中;

此时,一次不用额外变量的换值就完成了。

作用2:使指定的位反转

在想要反转的位数上写1,再取异或

如:01111010,想使其后4位反转,可以将它与00001111进行∧运算,即可得到答案:01110101

0 2

按位与

判断奇偶

我们可以用按位与来判断一个数的奇偶性,因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

如:一个数a==27,它的二进制是  0001 1011  ,当他与1取与时,a&1运算所得值为1,1==0显然不成立,所以a不是偶数,此法可以更快速的判断一个数是否是偶数。

0 3

左移

左移常被用来做 * (2 ^ n)的运算,因为直接基于二进制运算,所以左移效率比 * (2 ^ n)高。

如:1<<3的值为8,由于不涉及进制转换,此法效率会更高。

0 4

右移

右移常被用来做 / (2 ^ n)的运算,因为直接基于二进制运算,所以右移效率比 / (2 ^ n)高。

如:8>>3的值为1,由于不涉及进制转换,此法效率会更高。

458bdcecf3948a57dda445c45846bbdd.gif 0d24c0eb0bbd5f632dccb1b5dbb53837.gif

接下来我们来看几个实际的运用场景

以下题目正确使用方法:先看题目不看答案,仔细思考解决方法,再看答案观察自己方法和位运算方法的差异。

0d24c0eb0bbd5f632dccb1b5dbb53837.gif0 1

  求解数组中指定数

数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的数。本题的解决方法有很多,但是使用位运算应该可以说是最快,最高效,最优雅的方法。

直接使数组里的所有单元依次做异或运算,就可以得到最终的那个唯一的数。

0 2

求平均数

求平均数的方法谁都会,直接(x+y)/2不就完事了

但是大家有没有想过一个问题,x+y的值有没有可能会溢出?我们知道,(x+y)/2的值肯定不会溢出(要是溢出的话一开始赋值就是错的),但是x+y的值很可能会溢出,特别是在单片机这种寸土寸金的地方,溢出更是常态,于是我们可以采取以下位运算,既能避免溢出,又能高效的进行运算:

(x & y) + ((x ^ y) >> 1)

此方法的原理有些复杂,大致是先用与运算对数值做部分平均值的提取,然后用异或并右移运算获得余下部分的平均值,因此这两部分的平均值相加后就得出了原来两数的平均值。

0 3

求一个数是否是二的幂

相信大部分人使用的方法都是循环,就是让那个数反复处于2 ,看最终得到的答案是否为1,如果是1,则是2的幂。但是使用循环要进行大量的运算,能否用更高的效率得出答案呢?

让我们先来观察一下2幂的数的普遍规律:2e7b49312ce7011b50b76519a1de9ade.png

容易发现,只要一个数是2的幂,那么它一定是最高位为1,剩下的位均为0

那么也就是说,将这个数减去1,得到的一定是形如“01111”一样的数,那么我们只需要让我们要求的数和我们要求的数减一求与,再判断是否为0即可,既:

n&(n-1)==0

若该语句为真,则是2的幂,若不为真,则不是2的幂

让我们来用一个不是2的幂的数验证一下:

11 这个数的二进制为:1011

将这个数减去1,可以得到:1010

将两个数做与运算,可以得到:1==0,显然不成立

所以在判断一个数是否为2的幂时,效率最高的方法就是执行n&(n-1)==0语句,既规避了循环,还极大的提高了效率。

0 4

判断一个数是否是4的幂

同理,本题我们也可以用普通的方法:将这个数反复除以4,看最后答案是否为1

但是我们也可以仿照上题的例子,使用位运算来提高计算效率。

首先,我们按照上例,观察4的幂的规律:

fc72b9a5af80c051b7759ff1a96e00f6.png

我们发现,所有的是4的幂的数,首先它必须是2的幂,其次它的二级制的”1”都一定在奇数位上。所以我们可以:

先让这个数经过2的幂的检验,即执行语句:n&(n-1)==0

然后再找一个数,这个数必须满足以下条件:

它必须足够大,大到可以填满所有位

它的所有奇数位必须为1,偶数位必须为0

很容易找到这个数,就是:

01010101010101010101010101010101

(不用数了,一共有三十二位)

这样用起来比较麻烦,反正不管是16进制还是10进制都要在计算机当中涉及一次转换,那我们干脆用16进制把他表示出来:

0x55555555

然后我们将这个数和我们要求的数做一次与运算,再判断和原数是否相等,即可判断这个数是否是4的幂,即:

n*0x55555555 == n

执行完这两条语句后,即可判断出这个数是否是4的幂,即执行:

n&(n-1) == 0

n*0x55555555 == n

这里其实有一个小问题,既然n*0x55555555 == n可以直接筛选出这个数是否是4的幂,为什么还需要n&(n-1) == 0呢?

其实这个道理很简单,奇数位上是1并不是该数是4的幂的充分条件,假设我们有一个数:

101,它完全满足奇数位上为1,但是它并不是4的幂。

事实上,一个数是4的幂的充分条件应该为:该数的最高位为奇数位,且剩下的位上全部是0,所以,我们应该先判断该数是否是2的幂,将所有非最高位上有1的数全部“筛去“,此时再执行n*0x55555555 == n语句就能得到正确答案。

458bdcecf3948a57dda445c45846bbdd.gif总结 “位运算的好处诸多,唯一的缺点应该就是可读性较差,如果能够掌握那么几种位运算的方法,应该能在实际编程中提高程序的运行效率,特别是在单片机这种“寸土寸金“的地方,使用位运算更是能锦上添花。 ”

e593a0ab86999241c115bddfcb6df9f9.png


文案:李乾诚

排版:李国烜

初审:黄敏雪

复审:罗舒琪

终审:唐小煜老师

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值