zoukankan      html  css  js  c++  java
  • 不使用运算符计算加减乘除的问题

    问题描述

    给出两个整数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等于最小值,是转不成相应的正数的,

       

  • 相关阅读:
    django用户认证系统——基本设置1
    django用户认证系统——注册3
    django数据库设计
    修改linux最大文件句柄数
    LoadRunner监控Linux
    MySQL设置密码的三种方法
    JMeter学习-021-JMeter 定时器的应用
    mysql-bin.000001文件的来源及处理方法【转】
    js读取解析JSON数据
    关于查询区域标注区域总结
  • 原文地址:https://www.cnblogs.com/kanekiken/p/8607688.html
Copyright © 2011-2022 走看看