我正在用leetcode执行此question。
请求:
计算两个整数a和b的总和,但不允许使用+和-运算符。
我不明白它提供的解决方案
有人可以解释此getSum
函数的工作原理吗?
这是JS中的答案:
var getSum=function(a,b) {
const Sum = a^b; //I can't understand how those two line's code can
const carry = (a & b) << 1; //get the sum
if(!carry) {
return Sum
}
return getSum(Sum,carry);
};
console.log(getSum(5,1));
答案 0 :(得分:35)
它基本上是在复制half-adder
将A和B的两个位相加会产生2个输出:和和进位如下所示
╔═══════╤═════════════╗
║ Input │ Output ║
╠═══╤═══╪═══════╤═════╣
║ A │ B │ carry │ sum ║
╟───┼───┼───────┼─────╢
║ 0 │ 0 │ 0 │ 0 ║
╟───┼───┼───────┼─────╢
║ 1 │ 0 │ 0 │ 1 ║
╟───┼───┼───────┼─────╢
║ 0 │ 1 │ 0 │ 1 ║
╟───┼───┼───────┼─────╢
║ 1 │ 1 │ 1 │ 0 ║
╚═══╧═══╧═══════╧═════╝
从表中我们获得输出的逻辑:进位= A和B , sum = A xor B
XOR也称为无进位加法运算符,由represented表示,其中带有+
符号
所以上面的代码片段是这样工作的
const Sum=a^b; // sum = a xor b = a ⊕ b
const carry=(a&b)<<1; // carry = 2*(a and b), since we carry to the next bit
if(!carry){
return Sum; // no carry, so sum + carry = sum
}
return getSum(Sum,carry); // a + b = sum + carry
因此a^b
同时将a和b中的每一位相加,而在Sum
中保留a和b的非进位和。然后,我们必须将进位加到无进位总和上,以得到最终结果,因为我们只有一个半加法器,而不是一个全加法器,它执行a + b = a⊕b +进位
另请参见
答案 1 :(得分:26)
让我们以身作则。想象一下a = 3
和b = 5
以二进制表示法是a = 0011
和b = 0101
异或:
a^b
是XOR运算符。比较两个位时,如果相同则返回0
,如果不同则返回1
。 01^10 => 11
因此,当我们执行a^b
时,结果将是0110
。
AND + SHIFT
a&b
执行逻辑AND操作。仅在a = b = 1
时返回1。
在我们的例子中,结果为0001
<<
将其移动(在右侧添加0
),结果变为0010
,将carry
变量设置为true。 (只有0000
为假)。
下一次迭代:
一切重复,但是现在a = 0110
和b = 0010
(上次执行的Sum
和carry
)
现在a^b = 0100
和(a&b)<<1 = 0100
再次重复。
现在a^b = 0000
和(a&b)<<1 = 1000
再次。
现在a^b = 1000
和(a&b)<<1 = 0000
。现在carry
最终是false
。然后我们返回1000
,它是十进制8
。
自3+5=8
以来一切正常
答案 2 :(得分:2)
int result = p ^ q; // XOR Operator, + without carry 0+0=0, 0+1=1+0=1, 1+1=0
int carry = (p & q) << 1; // Left Shift, 1+1=2
if (carry != 0) {
return getSum(result, carry);
}
return result;
从p = 5,q = 6开始。那么XOR就是
0101
0110
------
0011
因此,“异或”运算结果为(0011),实际上是十进制的3。然后,将p和q相加,得到
0101
0110
-------
0100
通过对5和6进行“与”运算,我们得到4(二进制数为100),现在,如果将该值左移1,我们将得到
0100<<1=1000
因此,第一次递归后得到8(二进制数为1000)。由于结果(进位变量)不为零,请通过异或值和进位值再次递归。
getSum(3, 8);
因此,进行第一个XOR运算,
0011
1000
-------
1011
这次的异或运算产生11(1011二进制),所以我们现在执行AND,
0011
1000
-------
0000
我们得到与3和8的所有零值,因此这一次左移运算符也将产生零值,因为这里没有1,可能会通过左舍入零来给我们一个值。 由于进位变量现在为零,因此我们进行了递归操作,并且XORed值将为Sum,即11(二进制为1011)。
希望您可以执行该程序。您可以通过学习按位运算来了解更多信息,这是机器进行算术运算的方式。
答案 3 :(得分:0)
^
是XOR,按位运算。在单个位上,规则是0 ^ 0 = 0
,0 ^ 1 = 1
,1 ^ 0 = 0
和1 ^ 1 = 0
,在处理多位值时,只需扩展对相应位的执行即可。该名称是“ exclusive or”的缩写,它来自于以下事实:A ^ B
是1
,当且仅当A或B中的任何一个为1
时,两者都不是。但是,谈论它的另一个名称⊕更有趣。 ⊕是+,但略有不同。您会注意到⊕的规则与加法规则相似:0 + 0 = 0
,0 + 1 = 1
,1 + 0 = 1
和1 + 1 = 10
。 {是+,1 ⊕ 1 = 0
除外;也就是说,⊕是+,除非没有携带。这适用于多个位:011 + 001 = 100
,因为您将1
移出了四位,然后又将1
移到了四位。然后,011 ⊕ 001 = 010
,因为您根本不携带。
现在,当进行真正的加法时,什么时候携带?用二进制表示,答案很简单:给定位置有两个1
时,将1
带到下一个位置。这很容易理解为按位与&
。 1 & 1 = 1
,否则0
。对于011 + 001
,不带加法的加法会得到011 ⊕ 001 = 010
,我们可以知道我们需要将1
移出那个位置,因为011 & 001 = 001
。 (a & b) << 1
中的移位变成一个数字“我需要从哪里携带?”放入“我需要在何处添加进位?”:(011 & 001) << 1 = 010
;我需要在两个位置加一个进位。
因此,在getSum
中,我们想知道a + b
。我们不用加a ^ b
就可以算出加法,然后找到需要用(a & b) << 1
加进位的地方。现在,我们只需要将这两个加在一起即可。好了,我们已经有了一个将数字加在一起的功能;它称为getSum
。因此,我们基本上只写了function getSum(a, b) { return getSum(a ^ b, (a & b) << 1); }
,除了在没有东西要携带的情况下确保短路之外,这使我们免于无限递归。