每日一练——大数加减乘除运算实现(网易笔试题)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a987073381/article/details/52139203

前几天做网易笔试题时最后一道题是设计一个大数类,实现加减运算,因为做这道题时只有5分钟,结果我刚把类写出来,考试时间就结束了。其实在去年12月我写过一个RSA的加解密程序,其中就用到了大数的加减乘除运算(当然这只是RSA用到的一小部分),没有把最后一题写上去实在太可惜了(>﹏<。)~,今天把我设计的大数类贴出来,后面顺便附上面试时经常会让手写的加、减、乘代码。

一般的,我们会用一个很大的数组来表示一个大数,数组中的每一个数(0~9)来代表大数中的某一位,比如big_num[1000],那么这个数能够表示1000位大数,后来又人想到用数组中的每个数来表示0~99999999,这样数组只需要1000 / 8 = 125个元素即可实现。那么问题来了,一个unsigned long能表示的范围是0~4294967295也就是十六进制的0~0xFFFFFFFF,为什么我们不充分利用unsigned long能表示范围的的每一个数呢?

#define MAXLEN 64
typedef unsigned char BYTE; 

class big_int
{
public:
    ......
    void set_zero();//大数清零
    void modify_bit();//修正大数位数
    big_int& mov(big_int &obj);//大数赋值大数
    int cmp(big_int &obj);//两个大数进行比较,大于返回1,等于返回0,小于返回-1
    big_int& add(big_int &num1, big_int &num2);//大数加大数 A=B+C
    big_int& add(big_int &num);//大数加大数 A+=B
    big_int& Sub(big_int &num1, big_int &num2);//大数减大数 A=B-C
    big_int& Sub(big_int &num);//大数减大数 A-=B
    big_int& mul(big_int &num1, big_int &num2);//大数乘大数 A=B*C
    big_int& mul(big_int &num);//大数乘大数 A*=B
    big_int& div(big_int &num1, big_int &num2);//大数除大数 A=B/C
    big_int& div(big_int &num);//大数除大数 A/=B
    ......            
public:
    int sign;//正数为1,负数为0
    uint32_t data[MAXLEN];//数组中每一个数代表大数2^32次方进制的每一位
    int length;//大数使用int的长度
    int bit;//大数的二进制位位数
};


//大数加大数A=B+C
big_int& big_int::add(big_int &num1, big_int &num2)
{
    uint32_t carry = 0;
    uint64_t tmp;//用于存放两个无符号整形数(32位)的和,结果可能超过32位

    set_zero();

    length = num1.length > num2.length ? num1.length : num2.length;

    for (int i = 0; i < length; i++)
    {
        tmp = (uint64_t)num1.data[i] + (uint64_t)num2.data[i] + (uint64_t)carry;
        data[i] = (uint32_t)(tmp & 0x00000000FFFFFFFF);//data[i] = tmp % 0x100000000
        carry = (uint32_t)(tmp >> 32);//carry = tmp / 0x100000000)
    }

    if (carry != 0)
    {
        data[length++] = carry;
    }

    modify_bit();

    return *this;
}

//大数减大数 在减之前确保被减数大于减数A=B-C
big_int& big_int::sub(big_int &num1, big_int &num2)
{
    uint32_t carry = 0;
    uint64_t tmp;

    set_zero();

    if (num1.cmp(num2) < 0)//如果被减数小于减数,交换两数再相减,符号位为负
    {
        big_int bi_tmp;
        bi_tmp.mov(num1);
        num1.mov(num2);
        num2.mov(bi_tmp);
        sign = -1;
    }

    int length = num1.length > num2.length ? num1.length : num2.length;

    for (int i = 0; i < length; i++)
    {
        if ((uint64_t)num1.data[i] >= (uint64_t)num2.data[i] + (uint64_t)carry)
	        //如果obj.data[i] = 0xFFFFFFFF, carry = 1,相加会溢出,所以强转为uint64_t
        {
            data[i] = num1.data[i] - num2.data[i] - carry;
            carry = 0;
        }
        else//minuend.data[i] < obj.data[i] + carry
        {
            tmp = num1.data[i] | 0x100000000;//tmp = num1.data[i] + 0x100000000;
            tmp = tmp - num2.data[i] - carry;//tmp一定小于0x100000000
            data[i] = (uint32_t)tmp;
            carry = 1;
        }
    }

    modify_bit();

    return *this;
}


//两个大数相乘
big_int& big_int::mul(big_int &num1, big_int &num2)
{
    uint32_t carry;
    uint64_t tmp;//两个32位二进制数相乘结果不会超过64位
    big_int bi_tmp;

    set_zero();

    for (int i = 0; i < num2.length; i++)
    {
        carry = 0;
        bi_tmp.set_zero();

        for (int j = 0; j < num1.length; j++)
        {
            tmp = (uint64_t)num1.data[j] * (uint64_t)num2.data[i]
	            + (uint64_t)carry;
            bi_tmp.data[bi_tmp.length++] = (uint32_t)(tmp & 0x00000000FFFFFFFF);
            //等价于bi_tmp.data[bi_tmp.length++] = tmp % 0x100000000
            carry = (uint32_t)(tmp >> 32);//等价于carry = tmp / 0x100000000)
        }
        if (carry != 0)
        {
	        bi_tmp.data[bi_tmp.length++] = carry;
        }

        bi_tmp.left_move_len(i);
        add(bi_tmp);
    }

    modify_bit();

    return *this;
}

//大数除大数,采用试商的方法
big_int& big_int::div(big_int &num1, big_int &num2)
{
    big_int bi_tmp, bi_dividend;
    int len;
    uint64_t dividend_num;
    uint32_t div_num;

    bi_dividend.mov(num1);

    set_zero();

    while (bi_dividend.cmp(num2) > 0)
    {
        if (bi_dividend.data[bi_dividend.length-1] > num2.data[num2.length-1])
        {
	        len = bi_dividend.length - num2.length;
	        //这里采用五入的试商方法
	        div_num = bi_dividend.data[bi_dividend.length-1] / (num2.data[num2.length-1]+1);
        }
        else if (bi_dividend.length > num2.length)
        {
            len = bi_dividend.length - num2.length - 1;
            dividend_num = (uint64_t)bi_dividend.data[bi_dividend.length-1];
            dividend_num = (dividend_num << 32) + bi_dividend.data[bi_dividend.length-2];

            if (num2.data[num2.length-1] == 0xFFFFFFFF)
            {
	            div_num = (uint32_t)(dividend_num >> 32);//dividend_num / 0x100000000 等价于 dividend_num >> 32
            }
            else
            {
	            div_num = (uint32_t)(dividend_num / (uint64_t)(num2.data[num2.length-1]+1));
            }
        }
        else//被除数最高位等于除数最高位 被除数位数等于除数位数
        {
            add(1);//被除数除以除数的商一定为1
            break;
        }
        bi_tmp.mov(div_num);
        bi_tmp.left_move_len(len);
        add(bi_tmp);
        bi_tmp.mul(num2);
        bi_dividend.sub(bi_tmp);
    }

    if (bi_dividend.cmp(num2) == 0)
    {
        add(1);
    }

    modify_bit();

    return *this;
}

在面试和笔试中当然不能这么玩,附上面试时让手写的带符号的加减乘代码。

//无符号加法,且num1 >= num2
string unsigned_add(string num1, string num2)
{
    int len1 = num1.size();
    int len2 = num2.size();
        
    int carry = 0;
    string result = "";
    int tmp;

    while (num1.size() != num2.size())//加0补齐
    {
        num2 = '0' + num2;
    }

    for (int i = len1 - 1; i >= 0; i--)
    {
        tmp = num1[i] - '0' + num2[i] - '0' + carry;
        result += tmp % 10 + '0';
        carry = tmp / 10;
    }

    if (carry)
    {
        result += carry + '0';
    }

    reverse(result.begin(), result.end());
    
    return result;
}

//无符号减法,且num1 >= num2
string unsigned_sub(string num1, string num2)
{
    int len1 = num1.size();
    int len2 = num2.size();
        
    int carry = 0;
    string result = "";

    while (num1.size() != num2.size())//加0补齐
    {
        num2 = '0' + num2;
    }

    for (int i = len1 - 1; i >= 0; i--)
    {
        if (num1[i] >= num2[i] + carry)
        {
            result += num1[i] - num2[i] - carry + '0';
            carry = 0;
        }
        else
        {
            result += 10 + num1[i] - num2[i] - carry + '0';
            carry = 1;
        }
    }

    while (result[result.size() - 1] == '0')//去掉多余的0
    {
        result = result.substr(0, result.size() - 1);
    }
    reverse(result.begin(), result.end());
    
    return result;
}

//无符号乘法
string unsigned_mul(string num1, string num2)
{
    int len1 = num1.size();
    int len2 = num2.size();
    int cur, carry, tmp;

    string result = "";

    while (result.size() != len1 + len2)
    {
        result = '0' + result;
    }

    for (int i = len1 - 1; i >= 0; i--)
    {
        cur = len1 - 1 - i;
        carry = 0;

        for (int j = len2 - 1; j >= 0; j--)
        {
             tmp = result[cur] - '0' + (num1[i] - '0') * (num2[j] - '0') + carry;
             result[cur] = tmp % 10 + '0';
             carry = tmp / 10;
             cur++;
        }
        if (carry)
        {
            result[cur++] = carry + '0';
        }
    }

    while (result[result.size() - 1] == '0')//去掉多余的0
    {
        result = result.substr(0, result.size() - 1);
    }
    reverse(result.begin(), result.end());
    
    return result;
}

//把符号位从string中分离开来
int separate(string &num)
{
    if (num[0] == '-')
    {
        num = num.substr(1, num.size());
        return -1;
    }
    else
    {
        return 1;
    }
}

//带符号的加法
string add(string num1, string num2)
{
    int sign1, sign2, tmp;
    sign1 = separate(num1);
    sign2 = separate(num2);

    string result;
    if (num1.size() < num2.size() || num1.size() == num2.size() && num1.compare(num2) < 0)
    {
        num1.swap(num2);
        tmp = sign1;
        sign1 = sign2;
        sign2 = tmp;
    }

    if (sign1 == -1 && sign2 == 1)
    {
        result = '-' + unsigned_sub(num1, num2);
    }
    else if (sign1 == 1 && sign2 == -1)
    {
        result = unsigned_sub(num1, num2);
    }
    else if (sign1 == 1 && sign2 == 1)
    {
        result = unsigned_add(num1, num2);
    }
    else
    {
        result = '-' + unsigned_add(num1, num2);
    }

    return result;
}

//带符号的减法
string sub(string num1, string num2)
{
    int sign1, sign2, tmp;
    sign1 = separate(num1);
    sign2 = separate(num2);

    string result;
    if (num1.size() < num2.size() || num1.size() == num2.size() && num1.compare(num2) < 0)
    {
        num1.swap(num2);
        tmp = sign1;
        sign1 = sign2 * (-1);
        sign2 = tmp * (-1);
    }

    if (sign1 == -1 && sign2 == 1)
    {
        result = '-' + unsigned_add(num1, num2);
    }
    else if (sign1 == 1 && sign2 == -1)
    {
        result = unsigned_add(num1, num2);
    }
    else if (sign1 == 1 && sign2 == 1)
    {
        result = unsigned_sub(num1, num2);
    }
    else
    {
        result = '-' + unsigned_sub(num1, num2);
    }

    return result;
}

//带符号的乘法
string mul(string num1, string num2)
{
    int sign1, sign2, tmp;
    sign1 = separate(num1);
    sign2 = separate(num2);

    string result;
    if (num1.size() < num2.size() || num1.size() == num2.size() && num1.compare(num2) < 0)
    {
        num1.swap(num2);
        tmp = sign1;
        sign1 = sign2;
        sign2 = tmp;
    }

    result = unsigned_mul(num1, num2);
    
    if (sign1 * sign2 == -1)
    {
        result = '-' + result;
    }

    return result;
}



猜你喜欢

转载自blog.csdn.net/a987073381/article/details/52139203