大整数的加减乘除
大整数的加减乘除
背景
415. 字符串相加 - 力扣(LeetCode) (leetcode-cn.com)
43. 字符串相乘 - 力扣(LeetCode) (leetcode-cn.com)
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
-
示例 1:
输入: num1 = “2”, num2 = “3” 输出: “6”
-
示例 2:
输入: num1 = “123”, num2 = “456” 输出: “56088”
说明:
- num1 和 num2 的长度小于110。
- num1 和 num2 只包含数字 0-9。
- num1 和 num2 均不以零开头,除非是数字 0 本身。
- 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
大整数的字符串加法
- 模拟 思路与算法
本题我们只需要对两个大整数模拟「竖式加法」的过程。竖式加法就是我们平常学习生活中常用的对两个整数相加的方法,回想一下我们在纸上对两个整数相加的操作,是不是将相同数位对齐,从低到高逐位相加,如果当前位和超过 10,则向高位进一位?因此我们只要将这个过程用代码写出来即可。
具体实现也不复杂,我们定义两个指针 i 和 j 分别指向 num1和 num2的末尾,即最低位,同时定义一个变量cur 维护当前是否有进位,然后从末尾到开头逐位相加即可。
string BigIntCalculate::add(string num1, string num2)
{
string str;
int cur = 0, i = num1.size() - 1, j = num2.size() - 1;
while (i >= 0 || j >= 0 || cur != 0) {
if (i >= 0) {
cur += num1[i] - '0';
i--;
}
if (j >= 0) {
cur += num2[j] - '0';
j--;
}
str += to_string(cur % 10);
cur /= 10;
}
reverse(str.begin(), str.end());
return str;
}
大整数的字符串减法
减法同加法一样,可以用模拟「竖式减法」的过程。只不过要先通过比较两个数的位数和大小确定正负,同时定义一个变量cur 维护当前是否有借位。
string BigIntCalculate::subtract(string num1, string num2)
{
if (num1.size() < num2.size())
{
return "-" + subtract(num2, num1);
}
if (num1.size() == num2.size())
{
for (int i = 0; i < num1.size(); i++) {
int a = num1[i] + '0';
int b = num2[i] + '0';
if (a < b) {
return "-" + subtract(num2, num1);//添加负号
}
else if (a > b) {
break;
}
}
}
string str;
int i = num1.size() - 1, j = num2.size() - 1;
int cur = 0;
while (i >= 0 )
{
if (j >= 0) {
cur += (num1[i] - '0') - (num2[j] - '0');
}
else {
cur += num1[i] - '0';
}
i--;
j--;
if (cur < 0)
{
str += to_string(cur+10);
cur = -1;
}
else {
str += to_string(cur);
cur = 0;
}
}
reverse(str.begin(), str.end());
for (int i = 0; i < str.size(); i++)
if (str[i] != '0') return str.substr(i); //多了一步去除前导0
return "0";
}
大整数的字符串乘法
- 方法一:模拟乘法的竖式运算。
- 方法二:大整数乘法的快速算法还可以用(快速傅立叶变换FFT)
快速傅里叶变换(FFT)求解多项式乘法 - 知乎 (zhihu.com)
string BigIntCalculate::multiply(string num1, string num2)
{
int m = num1.size(), n = num2.size();
string s(m + n, '0'); //初始化大小m+n
for (int i = m - 1; i >= 0; --i) //从后向前
for (int j = n - 1; j >= 0; --j)
{
int cur = (num1[i] - '0') * (num2[j] - '0') + (s[i + j + 1] - '0'); //注意累加之前的结果
s[i + j + 1] = cur % 10 + '0'; //这里是=字符
s[i + j] += cur / 10; //注意这里是+=数字
}
for (int i = 0; i < m + n; i++)
if (s[i] != '0') return s.substr(i); //去除前导0
return "0";
}
大整数的字符串除法
先实现一个减法,然后通过累次相减确定商。循环做减法,计数,直到被除数小于或等于0。
简单优化方法可以根据位数之差,将除数扩大10^t^倍进行相减,来确定商在t位次的值,能够减少减法运算的次数。
string BigIntCalculate::divide(string num1, string num2)
{
int m = num1.size(), n = num2.size();
if (m < n) {
return "0";
}
if (num2 == "0") {
return "error";
}
int t = m - n;//位数之差
string div_result(t+1, '0');
string tmp_num2(n + t, '0');
for (int i = 0; i < n; i++) {//将除数扩大10^t倍
tmp_num2[i] = num2[i];
}
string mod_result = num1;
for (int i = 0; i < t + 1; i++) {
int count = 0;
string sub_result = mod_result;
while(true){
string tmp = tmp_num2.substr(0, n + t - i);
//cout << "i = " << i << " tmp = " << tmp << " sub_result = " << sub_result << endl;
sub_result = subtract(sub_result, tmp);
if ( sub_result[0] != '-') {
count++;
mod_result = sub_result;
}
else {
break;
}
}
div_result[i] = '0' + count;
}
if (div_result[0] == '0') {
return div_result.substr(1); //去除前导0
}
else {
return div_result;
}
}
其它警告信息
[C26451 | Microsoft Docs](https://docs.microsoft.com/zh-cn/cpp/code-quality/c26451?f1url=%3FappId%3DDev16IDEF1%26l%3DZH-CN%26k%3Dk(C26451)%26rd%3Dtrue&view=msvc-160) |
受到C语言以及各种C/C++入门书的熏陶,不少人一想到整型,一律使用 int。可能已经注意到,通篇使用int常常会有大片的此类警告:
警告 C26451 算术溢出: 使用 4 字节值上的运算符 + ,然后将结果转换到 8 字节值。在调用运算符 + 之前将值强制转换为宽类型可避免溢出(io.2)。
这是64位和32位整型宽度不对齐的警告。这些警告也许不碍事,只碍眼。有强迫症的,可能按照警告的提示加一个强制类型转换,没有强迫症的人直接就忽略了。但是没有任何一个警告是无缘无故的。这些警告不仅意味着程序中的潜在的溢出问题,还意味着无意义的强制类型转换浪费的效率。
32 位 VS 64 位
32位平台和64位平台最重要的区别是地址和指令的长度不同。在32位平台上,指针是32位的(即sizeof(void)==4),并且,为了契合32位,stl的容器的大小、索引也都是32位的整型。在64位平台上,指针是64位的(即sizeof(void)==8),并且stl的容器的大小和索引也都是64位的。特别指出,64位已经非常普及了,64位编译是必须考虑的问题。
有符号 | 无符号 | |
---|---|---|
32位 | int | unsigned int |
64位 | long long; __int64 | unsigned long long; unsigned __int64 |
为了同时兼容32位和64位,还要避免不必要的类型转换,要使用哪种类型?std中已经用宏和typedef为你写好了:
int => ptrdiff_t
usigned int => size_t
ptrdiff_t的意思是“指针之差”,即两个地址运算之差,对应有符号整型;size_t对应无符号整型,他们的宽度是依赖于编译平台的,在32位平台上是32位的,在64位平台是64位的,他们存在于std库中,用宏检查平台,并typedef自对应平台的整型。
留下评论