Bit Tricks (位运算技巧)

技巧? 是的,你没有看错!聪明的程序员总是喜欢这种东西。一点点让我的生活变得如此轻松,以至于我总是喜欢谈论它们!如果您不熟悉它或感到困难,那么不用担心,您来对地方了!本文旨在使您的旅途更轻松。

相信我,您一定会喜欢这篇文章。因此,让我们开始吧!

位?这些是什么?好吧,对于某些人来说,它是0和1,即二进制数字。直到一天以前,我都一直有这种感觉,我们的一位教授告诉我们,他们具有特殊的含义。二进制数字0和1表示数字电子设备中的两个状态。二进制0代表OFF状态,而二进制1代表ON状态。

您可能在想,当这篇文章是编程文章时,为什么我要谈论电子产品。各位天才程序员不仅是代码,他们还了解计算机系统的内部工作。

在计算机系统中,所有内容都以二进制格式表示,即以0和1的形式表示。因此,现在,当我们将数字存储在计算机的内存中时,计算机实际上将存储该数字的二进制表示形式。数字5将作为二进制101存储在内存中。

让我们谈谈几个数字的位表示形式:

0 = 0 1 = 1 2 = 10 3 = 11 4 = 100 5 = 101 6 = 110 7 = 111 8 = 1000
依此类推。

我希望您已经知道如何将十进制转换为二进制。
因此,继续前进,您是否发现任何模式或特殊之处?

观察结果

好吧,如果您仔细观察,您会发现:

  1. 所有奇数(例如1,3,5等等)都设置了最后一位。我的意思是他们的最后一位是1。
  2. 所有偶数(例如0、2、4、6等)的最后一位均未设置。说最后一位未设置,是指他们的最后一位是0。
  3. 所有为2的幂的数字在其二进制表示中仅设置了最左边的位。(2 = 10、4 = 100、8 = 1000)。
  4. 如果数字N是2的幂,则N-1的二进制表示形式将全为1,并且其二进制表示形式的位数将比N的二进制表示形式少一位。 8 = 1000和7 = 111)

我们可以从上方找到许多这样的观察结果,但是目前,这些观察结果足以让我们继续前进。

现在的问题是,如何轻松地获取数字的二进制表示形式。好吧,如果我们要打印给定数字的二进制形式,则必须了解称为 shifting 的东西。相信我,它们是无害的,它们一定会使我们的工作变得容易,所以很高兴了解它们!

有两个这样的运算符。

  • 左移位运算符,表示为<<
  • 右移位运算符,表示为>>

当我们写:

  • 1 << 0,这意味着将二进制数字1向左移动0位,并且在操作后,结果将为’1’
  • 1 << 1,这意味着将二进制数字1向左移动1位,并且在操作后,结果将为’10’
  • 1 << 2,这意味着将二进制数字1向左移动2个位置,操作后结果为’100’
  • number << 1,当number =十进制为4且二进制为100时,这意味着将二进制表示的数字左移1位,并且在操作后,结果将为’1000’,即十进制为8。

同样,对于右移:

  • 1 >> 0,这意味着将二进制数字1向右移动0位,并且在操作后,结果将为’0’
  • number >> 1,当number等于十进制的4且二进制为100时,这意味着将数字的二进制表示形式右移1个位置,操作后,结果将为’10’,即十进制为2。

所以基本上,如果我写这样的话:

  • number << i 表示将二进制数字形式的所有数字左移i次(或将i位置左移),而
  • number >> i 表示将二进制数的二进制表示形式的所有数字右移i次(或将数字右移i个位置)。

正常的左移表示在数字的二进制表示形式的末尾附加i个零。正常的右移意味着从数字中将最右边的位截断i次,并在开头添加零。

如果您注意到的话,这里有一个非常细微的观察结果,当我们将数字4左移1个位置时,我们得到8,即4乘以2的结果。类似地,当我们将数字4右移1个位置时,我们得到2,即正是我们将4除以2得到的结果。

因此,我们可以声明:

当我们将数字左移1位时,实际上是将数字乘以2。当我们将数字右移1位时,实际上是将数字除以2。


同理,如果我们将数字i左移,那意味着我们将数字乘以2,即i倍。同样,如果我们将数字i右移,那意味着我们将数字除以2,即i倍。

我们还没有完成。我们需要掌握更多操作员的基本知识。我想,您已经了解它们。

  1. OR(表示为|),如果两个操作数中的任何一个都不为零,则返回1,基本上执行联合操作。
  2. AND(表示为&),如果所有操作数都不为零,则返回1,基本上执行相交运算。
  3. XOR(表示为^),对于1的奇数返回1,或者可以说,当两个操作数保持相同的值时,它返回0。

通过示例进行解释,如果您已经知道这三个运算符的工作原理,则可以跳过本部分:

A = 1001,B = 0101
A | B = 1101
A&B = 0001
A ^ B = 1100

恭喜大家!我们从字面上涵盖了位操作操作所使用的所有基本概念。可是等等!我们还没有完成。

让我们尝试一些现实世界中的问题,并了解如何使用这种出色的技术解决它们。位操作将使代码更短,使我们的工作更加轻松。


1.判断数字的二进制表示形式中从左数位置i是否设置位(置1)。

设数字为10(十进制)。10 = 1010(二进制)。我们假定最左边的位是位置1,则从右边2开始的位是1。天真的程序员肯定会将数字从十进制形式转换为二进制形式,然后将其打印在所需位置。

尽管这种方法对于少量数字可以很好地工作,但是假设我们有一个非常大的数字,例如103076。如果我们开始将其从十进制转换为二进制,则会消耗大量时间和内存!但是您不必为此担心。现在,您是一个更好的程序员,并且知道一些技巧可以做到!

记住,我们讨论了移位运算符。我们的方法将包括在数字和左移给定位置的1之间进行按位与运算,以从数字中提取该位置的位。如果我们将数字1左移1,我们得到10。给定的数字是1010。如果我们在数字1010和10之间执行“与”运算,该怎么办?

1010&10 = 10

结果为非零,这表明1010的位置2的位必须为非零,因为当我们将其与1进行与时,结果的第二个位置将为1。

如果该数字为1000,并且我们将其与10相与,则结果将为1000&10 = 0000,清楚地说明该位置2的位未设置。

要检查某个位在给定位置上是设置还是未设置,请执行此操作
结论: 如果 数字 &(1 <<(位置-1))!= 0:设置位


2. 切换数字的所有位:将数字与所有数字进行异或运算。

101101 ^ 111111 => 010010(记住XOR操作,它返回1表示奇数个数)

结论: 通过位1的XOR可以切换原始位。


3.检查给定数字是否为2的幂。

好了,我们已经讨论过了。当数字的二进制表示形式只有一个1时,数字就是2的幂,也是最左边的一位。

我们可以采取另一种方法。假设我们必须检查数字8。8= 1000 和 8–1 = 7 = 0111

如果我们将数字与1相与,则将得到0,如上例所示。

但是如果number = 3,number-1 = 2

3 = 11(bin) 和 2 = 10(bin)=> 11&10 = 10 ,10!= 0

结论: 如果Number&(Number-1)== 0,则Number为2的幂


4.给定一个数字数组。数组只有一个元素。所有其他元素成对出现。在O(1)空间中找到缺少对的一个元素。

您必须将数组元素的出现频率存储在某个位置。但是,问题陈述中的约束表示没有使用任何额外的空间。好吧,如果您不了解位操作的知识,那么您真的不能不使用任何额外的空间。到那时,我想您可能已经猜到了我们应该执行的位操作。是! 是XOR。

我个人认为,XOR是一项非常有用且功能强大的操作。您应该始终首先考虑是否可以使用XOR解决问题。

因此,让我们看看XOR如何解决这个问题:

A = {1,2,3,1,3},您会看到答案应该是2,因为它是唯一缺少配对的元素。
1 = 12 = 103 = 11
1 ^ 1 = 0
2 ^ 2 = 10 ^ 10 = 00
3 ^ 3 = 11 ^ 11 = 00

当我们将元素与自身进行XOR运算时,结果为0。如果我们对所有数组元素进行XOR,那么每个重复对的XOR将为零,并且我们将只剩下一次出现的元素。
1 ^ 2 ^ 3 ^ 1 ^ 3 =(1 ^ 1)^(3 ^ 3)^ 2 = 0 ^ 0 ^ 2 = 2

结论: 元素与自身的异或结果值为零。


5.在数字的二进制表示中找到设置的位数(数字二进制中1出现的次数)。

您可以使用的第一种方法是通过使用移位运算来计数数字的每个设置位。

第二种方法:您可以使用AND操作解决此问题。

有一种称为Brian Kernighan算法的算法。它指出,如果我们对数字1进行“与”运算,我们实际上将其最右边的位设置为0。因此,继续执行直到数字变为零为止。

number = number&(number-1),直到number!= 0,然后在每一步递增计数器。

Example:
Number = 11 = 1011
Number-1=10 = 1010
count =0
Number = 1011 & 1010 => 1010 and count=1
Number =1010 & 1001 => 1000 and count=2
Number = 1000 & 0111=> 0000 and count=3
由于数字现在为零。因此停止。所以在数字11的二进制10111出现了3// 在程序中 输入: 00000000000000000000000000001011 输出: 3
int hammingWeight(int n) {
    int sum = 0;
    while (n != 0) {
        sum++;
        n &= (n - 1);
    }
    return sum;
}

结论: 数字中的设置位数:number =number&(number-1),直到number!= 0,并在每一步递增计数器。最后返回次数。


6.我发现有用的另一个技巧是,如果要查找数字的二进制表示形式的位数,则可以简单地使用以下公式:

number of bits = floor(log(number)/ log(2))+1

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值