不使用运算符计算加减乘除的问题

问题描述

给出两个整数a和b, 求他们的和, 但不能使用 + 等数学运算符

说明

a和b都是 32位 整数么?

  • 是的

我可以使用位运算符么?

  • 当然可以

样例

如果 a=1 并且 b=2,返回3

解题思路

以计算 5+17=22 为例:

在十进制加法中,我们可以分成三步进行: 
1. 只做各位相加不进位,此时相加的结果是 12(个位数 5 和 7 相加不要进位是 2,十位数 0 和 1 相加结果是 1); 
2. 做进位,5+7 中有进位 1,进位的值是 10; 
3. 按照第1步中的方法将进位值与第 1 步结果相加,得到最终结果 22。

下面考虑二进制数的情况(5=101,17=10001): 
仍然分3步: 
1. 各位相加但不计进位,得到结果 10100; 
2. 记下进位,本例中只在最后一位相加时产生进位 1,进位值为二进制的 10; 
3. 按照第1步中的方法将进位值与第 1 步结果相加,得到最终结果 10110,转换成十进制正好是 22。

接下来把二进制的加法用位运算来替代: 
1. 不考虑进位对每一位相加,0+0=0,0+1=1,1+0=1,1+1=0,注意到,这和异或的结果是一样的; 
2. 考虑进位,只有在 1+1 时会向前产生一个进位,此时可以想象成两个数先做位与运算,然后再向左移动一位,只有两个数都是 1 的时候,位与得到的结果是 1,其余都是 0; 
3. 把前两个步骤的结果相加,相加的过程依然是重复前面两步,直到不再产生进位为止。 
以上分析参考自<<剑指Offer>>。

解题代码

class Solution {
public:
    /*
     * @param a: The first integer
     * @param b: The second integer
     * @return: The sum of a and b
     */
    int aplusb(int a, int b) {
        // write your code here, try to do it without arithmetic operators.
       while(b != 0){
           int sum = a ^ b;
           int carry = (a & b) << 1;
           a = sum;
           b = carry;
       }
       return a;
    }
};

扩展进阶

用位运算实现减法运算

实现 a - b 只要实现 a + (-b) 即可,根据二进制数在机器中表达的规则,得到一个数的相反数,就是这个数的二进制数表达取反加1的结果。所以实现减法运算的代码如下:

int aminusb(int a, int b) {
    int temp = aplusb(~b, 1);
    return aplusb(a, temp);

用位运算实现乘法运算

a * b 的结果可以写成 a * 20 * b0 + a * 21 * b1 + … + a * 2i * bi + … + a * 231 * b31,其中,bi 为 0 或 1 代表整数 b 的二进制表达中第 i 位的值。

以 a = 22 = 10110, b = 13 = 1101为例: 
用res来记录每次运算的结果,res = 0

a: 10110 
b: 1101 
b 的最低位为1, 所以res = res + a = 10110,同时 b 右移一位,a左移一位。

a: 101100 
b: 110 
b 的最低位为0, 所以res不变,同时 b 右移一位,a左移一位。

a: 1011000 
b: 11 
b 的最低位为1, 所以res = res + a = 1101110,同时 b 右移一位,a左移一位。

a: 10110000 
b: 1 
b 的最低位为1, 所以res = res + a = 100011110,同时 b 右移一位,a左移一位。

a: 101100000 
b: 000000000 
此时 b 为 0,过程停止,返回 res = 100011110,即 286。

所以实现乘法运算的代码如下:

int amultib(int a, int b) {
    int res = 0;
    while(b != 0){
        if((b & 1) != 0)
            res = aplusb(res, a);
        a <<= 1;
        b >>= 1;
    }
    return res;
}

当然,这只适用于正数做乘法的情况,考虑负数的话代码应该为:

int amultib2(int a, int b) {
    if(a < 0){
        if(b < 0){
            return amultib(aplusb(~a, 1), aplusb(~b, 1));
        }else{
            int temp1 = aplusb(~a, 1);
            int temp2 = amultib(temp1, b);
            return aplusb(~temp2, 1);
        }
    }else{
        if(b < 0){
            int temp1 = aplusb(~b, 1);
            int temp2 = amultib(a, temp1);
            return aplusb(~temp2, 1);
        }else{
            return amultib(a, b);
        }
    }
}

用位运算实现除法运算

用位运算实现除法运算,其实就是乘法的逆运算。a / b 就是看 a 最多能减去多少个 b,所以正数除法的代码如下:

int adivb(int a, int b) {
    int res = 0;
    for(int i = 31; i >= 0; i = aminusb(i, 1))
        if((a >> i) >= b){
            res |= (1 << i);
            a = aminusb(a, b << i);
        }
    return res;
}

考虑到负数的情况,代码应该为:

int adivb2(int a, int b) {
    if( b == 0){
        cout << "除数不能为0" << endl;
        exit(-1);
    }
    if(a < 0){
        if(b < 0)
            return adivb(aplusb(~a, 1), aplusb(~b, 1));
        else{
            int temp1 = aplusb(~a, 1);
            int temp2 = adivb(temp1, b);
            return aplusb(~temp2, 1);
        }
    }else{
        if(b < 0){
            int temp1 = aplusb(~b, 1);
            int temp2 = adivb(a, temp1);
            return aplusb(~temp2, 1);
        }else
            return adivb(a, b);
    }
}

以上除法分析可以算绝大多数的情况,但32位整数的最小值为-2147483648,最大值为2147483647,最小值的绝对值比最大值的绝对值大1,所以,如果a或b等于最小值,是转不成相应的正数的,

   

原文地址:https://www.cnblogs.com/kanekiken/p/8607688.html