通过位操作将数字加1


位操作使用按位运算符(如 AND(&)、OR(|)、NOT(~)、XOR(^)、左移(<<) 和右移(>>))对位流进行逻辑运算以获得所需结果。使用按位运算符是有益的,因为我们可以操作单个位,并且它们比其他运算符更快。

问题陈述

给定一个数字。仅使用按位运算符将数字加1。(不要使用像 ‘+’、‘-’、‘*’ 或 ‘/’ 这样的算术运算符)

方法一:使用反码/非运算符

按位补码/反码是使用 NOT(~) 运算符实现的。

对于数字 n,n 的按位补码,即 ~n = -(n+1)。因此,我们可以通过仅取 ~n 的值并忽略符号来得到 n+1。这可以使用 abs() 函数来完成,该函数返回变量的值并忽略符号。

伪代码

procedure inc (num)
   num = |~n| // | | represents the abs() function
end procedure

示例

在下面的程序中,我们通过使用按位补码运算符和绝对值函数来计算数字加1的结果。

#include <bits/stdc++.h>
using namespace std;
// Function for finding the increment of input by 1
int inc(int num){

   // using the abs() function to get the value obtained by applying the ~ operator
   num = abs(~num);
   return num;
}
int main(){
   int input = 7;
   cout << input << " + 1 using bit manipulation = " << inc(input);
   return 0;
}

输出

7 + 1 using bit manipulation = 8

时间复杂度 − O(1),因为没有实现循环。

空间复杂度 − O(1),因为没有使用额外的空间。

方法二

来看一些二进制加法的例子,如下所示:


从上面的例子可以看出,在对1进行二进制加法时,数字右边第一个未设置位的左边二进制位保持不变。但是,右边第一个未设置位被设置,右边所有的位都被反转,即设置位被取消设置,未设置位被设置(0 转换为 1,1 转换为 0)。

因此,我们可以将我们的问题分解成两个基本部分:

  • 设置右边第一个未设置位。

  • 反转右边所有的位。

递归方法伪代码

procedure inc (num)
   if num & 1 == 0
      ans = num | 1
   else
      ans = inc (num >> 1) << 1
   end if
end procedure

示例

在下面的程序中,我们使用递归根据上面解释的思想将数字加1。

#include <bits/stdc++.h>
using namespace std;
// Function for finding the increment of input
int inc(int num){

   // check if the LSB is set or not. If it is unset, it enters if otherwise, it goes to else
   if ((num & 1) == 0) {
   
      // LSB is set
      return num | 1;
   }
   else {
   
      // The number is right shifted and the function is called again
      // After obtaining the return number from called function, the number is left shifted
      return inc(num >> 1) << 1;
   }
}
int main(){
   int input = 14;
   cout << input << " + 1 using bit manipulation = " << inc(input);
   return 0;
}

输出

14 + 1 using bit manipulation = 15

时间复杂度 − O(k),其中 k 是从右边第一个未设置位的位数。

空间复杂度 − O(k),由于递归堆栈空间。

迭代方法伪代码

procedure inc (num)
   m = 1
   while num & m
      num = num & (~m)
      m = m << 1
   end while
   num = num | m
end procedure

示例

在下面的程序中,我们使用一个掩码数字,该数字在每次迭代中都会更改,直到我们得到右边第一个未设置位。发现它后,将设置未设置位,并将右边所有其他位反转。

#include <bits/stdc++.h>
using namespace std;
// Function for finding the increment of input
int inc(int num){

   // declare a mask with value 1 which is used to find the first unset bit from right side
   int m = 1;
   
   // checks if LSB is set. If set enter the loop and repeat till we get to the unset bit
   while (num & m) {
      num &= ~m;
      
      // mask is changed at every iteration to set the unset bit and toggle bits when the loop ends
      m <<= 1;
   }
   
   // mask used to set the unset bit and toggle all the bits on the right-hand side
   num |= m;
   return num;
}
int main(){
   int input = 14;
   cout << input << " + 1 using bit manipulation = " << inc(input);
   return 0;
}

输出

14 + 1 using bit manipulation = 15

时间复杂度 − O(k),其中 k 是从右边第一个未设置位的位数。

空间复杂度 − O(1)

方法三

在这种方法中,我们试图通过减少查找第一个未设置位的时间来降低时间复杂度。在这种方法中,我们将首先找到从右边第一个未设置位,设置它并反转右边所有的位。

伪代码

procedure inc (num)
   pos = log2 (num & ~num)
   add = 1 << pos
   num = num | add
   if pos != 0
      toggle = (1 << pos) - 1
      num = num ^ toggle
   end if
   ans = num
end procedure

示例

在下面的程序中,使用 Log2() 操作来查找未设置位,左移和 OR 来设置位,以及左移和 XOR 来反转位。

#include <bits/stdc++.h>
using namespace std;
// Function for finding the increment of input
int inc(int num){

   // finds the position of the first unset bit from right
   int pos = log2(num & ~num);
   
   // the following 2 operations combined set the unset bit
   int add = 1 << pos;
   num |= add;
   
   // Toggling bits only for the numbers where the first unset bit was not the LSB
   if (pos != 0){
   
      // Using left shift and XOR operations to toggle bits
      int toggle = (1 << pos) - 1;
      num ^= toggle;
   }
   return num;
}

int main(){
   int input = 14;
   cout << input << " + 1 using bit manipulation = " << inc(input);
   return 0;
}

输出

14 + 1 using bit manipulation = 15

时间复杂度 − O(1)

空间复杂度 − O(1)

结论

总之,为了将数字加1,我们可以使用反码概念,或者通过分析加1时位的性质,我们可以遵循上述方法来获得所需的结果。

更新于:2023年9月28日

863 次浏览

启动您的职业生涯

通过完成课程获得认证

开始学习
广告