每日一练——大数加减乘除运算实现(网易笔试题)
来源:互联网 发布:磁力链接软件 编辑:程序博客网 时间:2024/05/30 19:32
前几天做网易笔试题时最后一道题是设计一个大数类,实现加减运算,因为做这道题时只有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 64typedef 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+Cbig_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-Cbig_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 >= num2string 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 >= num2string 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;}
1 0
- 每日一练——大数加减乘除运算实现(网易笔试题)
- 大数运算(加减乘除)
- 大数运算(加减乘除)
- c语言版大数计算器(实现大数的加减乘除运算)
- 大数运算实现加减乘除四则运算
- 20171020—每日一练
- 20171019—每日一练
- 大数运算(加减乘除模)
- 每日一题(15)——Digit Root(大数)
- 每日一题(15)——Digit Root(大数)
- #每日一题#网易2016实习研发工程师笔试题
- 每日一练------条件运算符(成绩分级)
- 每日一练------奖金计算(条件运算符)
- 大数运算——加减乘除和求平方根
- 网易笔试题(一)
- vim每日一练(三)——查找匹配
- C++每日一练(STL算法——sort)
- C++每日一练(STL算法——find)
- 英文歌曲:Iridescent (变形金刚第三部主题曲)
- The VMware Authorization Service is not running错误的处理方式
- 跟我一起写Makefile(15)--使用make更新函数库文件(函数库文件的成员+函数库成员的隐含规则+函数库文件的后缀规则)
- count_if 源码剖析
- 严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderLis
- 每日一练——大数加减乘除运算实现(网易笔试题)
- 多线程编程入门(17):线程同步工具之CountDownLatch
- hdu 5806 NanoApe Loves Sequence Ⅱ
- 数学在机器学习中的重要性
- 占位
- BestCoder Round #86
- Unity学习笔记 4th —— 现在让我们来学一学材质制作
- HTML5的新增标签
- 带你从头到尾梳理大图片加载OOM处理问题