int32整型除法的位运算模拟

问题说明:

 int32整型除法:

想要求解的问题如下:
在这里插入图片描述在这里插入图片描述

 数值溢出问题:

  • 对于整形int来说,它的值域为 [ 2 31 , 2 31 1 ] [-2^{31},2^{31}-1] 。每次进行运算时,可能会产生溢出。特别地,int型最小值,在取反的时候,也会溢出。
  • 对于无符号型数,也要警惕其产生溢出(比如unsigned加法产生溢出,会导致两个unsigned之和小于二者任意一个)。

解法:

 搜索的思想 二分法求解:

思路:
 求解|A| ÷ \div |B|的整体思路就是找到一个数C,使得C是满足Cx|B| \leqslant |A|的最大的非负整数。
 因此,完全可以使用二分搜索法,在区间[0,A]中搜寻C的存在。时间复杂度是O(log(|A|))级别。
代码:

class Solution {
public:
    unsigned const Max_of_Unsigned=0xffffffffu;
    int const Min_Nega_Int=0x80000000;
    int const MaX_Posi_Int=0x7fffffff;
public:
    int divide(int dividend, int divisor) 
    {
        bool isNegative=false;
        if((dividend<0&&divisor>0)||(dividend>0&&divisor<0))
            isNegative=true;
        unsigned res=true_divide(to_Abs_Unsigned(dividend),to_Abs_Unsigned(divisor));

        if(isNegative)
        {
            return to_Negative_Signed(res);
        }
        else
        {
            if(res==0x80000000u)
                return 0x7fffffff;
            return (int)res;
        }
        
    }
    /**
     * 将一个int型变量x,转换为值为该int型绝对值|x|,的unsigned型变量的方法
     */
    public:unsigned to_Abs_Unsigned(int x)
    {
        if(x>=0)
            return (unsigned)x;
        if(x==0x80000000)
            return 0x80000000u;
        return (unsigned)(~x+1);
    }

    /**
     * 将一个unsigned型的变量转换为,值为其相反数-x,的型为int的变量
     */ 
    public:int to_Negative_Signed(unsigned x)
    {
        if(x==0x80000000u)
            return 0x80000000;
        x=~x+1;
        return *(int*)(&(x));
    }

    /**
     * 将除数与被除数用unsigned表示,保证不超出范围,然后调用该函数使用二分法解题
     * 参数:div和dis都是不大于0x80000000的数
    */
    public:unsigned true_divide(unsigned div,unsigned dis)
    {
        cout<<"\t"<<div<<"\t"<<dis<<endl;
        unsigned max=div;
        unsigned min=0u;

        if(max<dis)
            return 0u;
        if(dis==1u)
            return div;
        
        unsigned mid;
        while(max>min+1)
        {
            //0x80000000>=max>min+1 因此加法不会溢出
            mid=(max+min)>>1;
            unsigned check=multiply(std::max(mid,dis),std::min(mid,dis));
            if(check==div)
                return mid;
            else if(check<div)
            {
                min=mid;
            }
            else
            {
                max=mid-1;
            } 
        }
        cout<<"\tmax: "<<max<<"\tmin: "<<min<<endl;
        if(max==min)
            return max;
        else
        {
            if(multiply(std::max(max,dis),std::min(max,dis))<=div)
                return max;
            else
            {
                return min;
            }
        }
        
    }

    //返回两个unsigned代表的数之积,如果超过unsigned表示范围,则返回0xffffffffu
    public:unsigned multiply(unsigned x,unsigned y)
    {
        unsigned acc=x;
        unsigned res=0u;
        unsigned judge=y;
        unsigned unchecked_sum=0u;


        while(judge>0u)
        {
            if(judge&0x1u!=0u)
            {
                unchecked_sum=res+acc;
                if(unchecked_sum<res||unchecked_sum<acc)
                    return Max_of_Unsigned;
                res=unchecked_sum;
            }
            judge=judge>>1;

            if(acc>=0x80000000u&&judge>0u)
                return Max_of_Unsigned;

            acc=acc<<1;
        }
        return res;
    }
};

 二进制思想 位运算求解:

思路:

  • 将32位整数C写作其二进制表示 C [ c 31 c 32 . . . c 0 ] C[c_{31}c_{32}...c_{0}] 。可以通过从高位到低位直接遍历一遍 C [ c 31 c 32 . . . c 0 ] C[c_{31}c_{32}...c_{0}] 的每一位,得到C的二进制表达形式,从而C可求。
  • 从第31位开始,对于C的每一位 c i c_{i} 是否为1,仅仅取决于 ( A B C [ . . . c i + 1 c i 0...0 ] ) B \frac{(A-B*C[...c_{i+1}c_{i}0...0])}{B} 是否大于等于 2 i 2^i

原理

  • 在遍历 C [ c 31 c 32 . . . c 0 ] C[c_{31}c_{32}...c_{0}] 的数位时,第 i i 位可以表示最大的数是 2 i 2^i ,而后i-1位可以表示的最大的数是 2 i 1 2^{i}-1 。从而如果已经知道C的后 i i 位值 C [ 0...0 c i c i 1 . . . c 0 ] C[0...0c_{i}c_{i-1}...c_{0}] 要大于等于 2 i 2^i ,那么一定有 c i = 1 c_i=1
  • 因为在遍历到第i位时, i i 前的每一位已经确定,则可以通过 ( A B C [ . . . c i + 1 c i 0...0 ] ) B \frac{(A-B*C[...c_{i+1}c_{i}0...0])}{B} 来确定C的后 i i 位值 C [ 0...0 c i c i 1 . . . c 0 ] C[0...0c_{i}c_{i-1}...c_{0}]
    代码:

class Solution1 {
public:
    int divide(int dividend, int divisor) {
        if (dividend == INT_MIN && divisor == -1) {
            return INT_MAX;
        }
        bool isNegative = (dividend > 0) ^ (divisor > 0);
        unsigned int a = (dividend == INT_MIN) ? 0x80000000 : abs(dividend);
        unsigned int b = (divisor == INT_MIN) ? 0x80000000 : abs(divisor);
        unsigned int result = 0;
        for (int i = 31; i >= 0; i--) {
            if ((a >> i) >= b) {
                result += ((unsigned int)1) << i;
                a -= b << i;
            }
        }
        if (result == 0x80000000) {
            return INT_MIN;
        }
        else {
            return (isNegative) ? -(int)result : (int)result;
        }
    }
};

参考:

 题目来源:leetCode

 解法参考:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43116414/article/details/108448374
今日推荐