目录
为什么要循环?循环中的与运算他的进位值具体代表什么呢?他是谁的进位值呢?
在计算机科学和编程中,位运算是一种高效的运算方式,尤其是在资源受限的环境下。最近,我遇到了一个有趣的问题:如何仅使用位运算实现两个整数的加法?这个问题不仅仅是一个编程练习,更深入地理解了计算机如何在底层处理数据。
题目连接:不用加减乘除做加法_牛客题霸_牛客网 (nowcoder.com)
问题是这样的:不使用传统的加法运算符(+
),如何计算两个整数的和?关键在于理解二进制加法的原理,以及如何用位运算(特别是异或^?
和 与&?
运算,以及位移操作)来模拟这一过程。
import java.util.*;
public class Solution {
public int Add(int num1,int num2) {
// add表示进位值
int add = num2;
// sum表示总和
int sum = num1;
// 当不再有进位的时候终止循环
while (add != 0) {
int temp = sum ^ add;
// 进位值是用与运算产生的
add = (sum & add) << 1;
// 更新sum为新的和
sum = temp;
}
return sum;
}
}
二进制加法遵循着简单的规则:
在二进制加法中,只有当两个位都是1时,才会产生进位。现在,让我们分析位运算符如何应用于这个规则:
异或运算(XOR):异或运算符 ^
在二进制加法中用于计算不包括进位的和。它符合以下规则:
与运算(AND):与运算符 &
在二进制加法中用于计算进位。它的规则是:
左移运算:当我们知道哪些位产生了进位后,需要将这些进位左移一位,因为在二进制中,进位总是影响下一个更高的位。比如,二进制中的 10(十进制中的2)是由低位上的进位产生的。
结合这些规则,加法操作可以这样进行:
当进位为0时,异或运算的结果就是最终的加法结果。这是因为二进制加法本质上就是这样一连串的非进位和和进位的计算过程。
非进位和:这是两个数进行异或(XOR)运算的结果。异或运算在每一位上模拟了加法,但不考虑进位。例如,对于二进制数来说:
非进位和实际上是两个数相加时每一位的结果,如果那一位没有进位的话。
进位:这是两个数进行与(AND)运算然后左移一位的结果。与运算确定哪些位在相加时会产生进位:
左移一位是因为在二进制加法中,进位总是影响下一个更高的位。
合并非进位和和进位:在进行位运算实现加法的过程中,我们首先计算非进位和(通过异或运算)和进位(通过与运算和左移)。这两个结果必须结合起来,以得到最终的加法总和。为此,我们再次执行异或运算,这次是在非进位和与进位之间进行。这个过程可能需要重复进行,因为新的非进位和与进位之间可能还会产生新的进位。我们继续这个过程,直到没有更多的进位产生,此时的非进位和就是最终的加法结果。
通过这种方式,我们可以使用位运算来模拟标准的加法操作。我们已经知道:
现在,我们需要将这两个结果相加。但是,因为我们不能使用传统的加法运算符,我们必须重复使用这两种位运算(异或和与运算)来实现这一点。
让我们将这个过程分解为几个步骤:
首次计算:
sum = num1 ^ num2
:这是非进位的加和。add = (num1 & num2) << 1
:这是进位的结果。重复计算:现在我们有了一个新的 sum
(非进位加和)和 add
(进位)。但是,我们不能仅仅将它们相加,因为在 add
中可能存在新的进位。因此,我们重复相同的过程:用异或运算处理非进位加和,用与运算处理新的进位。
更新 sum
和 add
:
sum
是当前 sum
和 add
的异或运算结果。add
是当前 sum
和 add
的与运算结果左移一位。循环直到无进位:这个过程持续进行,直到 add
为0。当 add
为0时,表示没有更多的进位需要处理,此时 sum
包含了最终的加法结果。
每一轮循环,我们都在处理新产生的进位。最终,当所有的进位都被处理完毕(即 add
为0),我们得到最终的加和结果。这是通过位运算模拟加法过程的关键所在。
在二进制加法中,进位是一个非常重要的概念。当你将两个二进制数相加时,如果两个相应的位都是1,就会产生一个进位。这个进位不是加在当前的位上,而是加在下一个更高的位上。这就是为什么在与运算之后需要将结果左移一位,因为这样可以将进位移到正确的位置。
让我们用一个具体的例子来说明这一点:
假设我们有两个二进制数 1011
和 1101
。
第一步:异或运算
1011 ^ 1101 = 0110
(这是不考虑进位的加和)第一步:与运算
1011 & 1101 = 1001
(这里只有两个位都是1时结果才是1,表示这些位置上会产生进位)左移进位
1001
左移一位得到 10010
。这表示原本在第0位和第3位上的进位现在应该加到第1位和第4位上。num1 = 0110
,num2 = 10010
现在的关键问题是:我们有了一个不考虑进位的结果(0110
),以及一个表示哪里需要进位的值(10010
)。这两个结果还需要合并来得到最终的加法结果。这就是为什么需要循环的原因。在每次循环中,我们使用异或运算来合并非进位和,使用与运算和左移来计算新的进位。只有当没有更多的进位需要处理时(即进位结果为0),这个过程才会停止。
继续以上面的例子为例,进行下一轮计算:
第二步:异或运算
0110 ^?10010 = 10100
(合并非进位和和进位)第二步:与运算
0110 & 10010 = 00010
(进位产生)num1 = 10100
,num2 = 100
下面简化步骤:
第三轮计算:
非进位和:10100 ^ 100 = 10000 (二进制 10000,十进制 16)
进位:(10100 & 100) << 1 = 1000 (二进制 1000,十进制 8)
更新 num1 = 10000
,num2 = 1000
第四轮计算:
非进位和:10000 ^ 1000 = 11000 (二进制 11000,十进制 24)
进位:(10000 & 1000) << 1 = 0 (二进制 0,十进制 0)
更新 num1 = 11000
,num2 = 0
结束:num2
变为0,循环结束。最终结果是 num1 = 11000 (二进制 11000,十进制 24)
。