高精度算法总结
-
高精度算法的主要原理都是使用字符串或者数组等结构来模拟加减乘除竖式运算的过程
-
如果在读入时用字符串读入数据的话,尽量将字符串中的数据存储到数组中之后再对数组进行计算,避免在直接对字符串进行处理时发生字符和数字之间的转换错误(本文中全部用vector代替数组处理)
-
高精度运算
-
高精度加法:逆序存储、需要处理进位
-
高精度减法:逆序存储、需要处理前导零
-
高精度乘法(两种形式):逆序存储、需要处理进位和前导零
-
高精度除法(四种形式):正序存储、需要处理前导零
-
高精度加法
给定两个正整数(不含前导 0),计算它们的和。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的和。
数据范围
1 <= 整数长度 <= 1e5
代码
#include <iostream>
#include <vector>
using namespace std;
vector<int> max_add(vector<int>& a, vector<int>& b)
{
if (a.size() < b.size()) return max_add(b, a);
vector<int> res;
int t = 0;
for (int i = 0; i < a.size(); i ++ )
{
t += a[i];
if (i < b.size()) t += b[i];
res.push_back(t % 10);
t /= 10;
}
if (t) res.push_back(t); // 在进行加法时可能发生进位
return res;
}
int main()
{
string a, b;
cin >> a >> b;
vector<int> num1, num2;
for (int i = a.size() - 1; i >= 0; i -- ) num1.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i -- ) num2.push_back(b[i] - '0');
auto res = max_add(num1, num2);
for (int i = res.size() - 1; i >= 0; i -- ) cout << res[i];
return 0;
}
高精度减法
给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的差。
数据范围
1 ≤ 整数长度 ≤ 1e5
代码:
#include <iostream>
#include <vector>
using namespace std;
// 比较数组中的两个数的大小
bool cmp(vector<int>& a, vector<int>& b)
{
if (a.size() != b.size()) return a.size() > b.size();
else
for (int i = a.size() - 1; i >= 0; i -- )
if (a[i] != b[i])
return a[i] > b[i];
return true;
}
vector<int> max_sub(vector<int>& a, vector<int>& b)
{
vector<int> res;
int t = 0;
for (int i = 0; i < a.size(); i ++ )
{
t = a[i] - t;
if (i < b.size()) t -= b[i];
res.push_back((t + 10) % 10);
if (t < 0) t = 1;
else t = 0;
}
// 结果可能会有前导零
// 去除前导零
while (res.size() > 1 && res.back() == 0) res.pop_back();
return res;
}
int main()
{
string a, b;
cin >> a >> b;
vector<int> num1, num2;
for (int i = a.size() - 1; i >= 0; i -- ) num1.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i -- ) num2.push_back(b[i] - '0');
vector<int> res;
if (cmp(num1, num2))
res = max_sub(num1, num2);
else
{
printf("-");
res = max_sub(num2, num1);
}
for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);
return 0;
}
高精度乘法
给定两个非负整数(不含前导 0)A 和 B,请你计算A×B的值。
输入格式
共两行,第一行包含整数 A,第二行包含整数B。
输出格式
共一行,包含 A×B的值。
数据范围
1 ≤ A的长度 ≤ 1e5
0 ≤ B ≤ 1e4
本题实际上使用的是高精度乘低精度的代码,随后会给出高精度乘高精度的代码
代码(高精度乘低精度)
#include <iostream>
#include <vector>
using namespace std;
vector<int> max_mul(vector<int>& a, int& b)
{
vector<int> res;
int t = 0;
for (int i = 0; i < a.size(); i ++ )
{
t += a[i] * b;
res.push_back(t % 10);
t /= 10;
}
// 处理进位
if (t) res.push_back(t);
// 处理前导零
// 因为当其中一个乘数为0时,结果为0,所以要处理前导零
while (res.size() > 1 && res.back() == 0) res.pop_back();
return res;
}
int main()
{
string a;
int b;
cin >> a >> b;
vector<int> num1;
for (int i = a.size() - 1; i >= 0; i -- ) num1.push_back(a[i] - '0');
auto res = max_mul(num1, b);
for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);
return 0;
}
代码(高精度乘高精度)
#include <iostream>
#include <vector>
using namespace std;
vector<int> max_mul(vector<int>& a, vector<int>& b)
{
// n位数乘m位数的结果最多为n + m位,这里多开一位,并且全部初始化为0
vector<int> res(a.size() + b.size() + 1, 0);
for (int i = 0; i < a.size(); i ++ )
{
// 实质上相当于a.size()个高精度乘低精度的代码,然后再加在一起
int t = 0;
for (int j = 0; j < b.size(); j ++ )
{
// 在竖式中,求得每位间的乘法之后,还要将每列的数加在一起,所以要加上res[i + j]
t += a[i] * b[j] + res[i + j];
res[i + j] = t % 10; // 第i位和第j位的结果的个位数在第i + j位
t /= 10;
}
if (t) res[i + b.size()] = t; // 处理高精度乘低精度的进位
}
// 处理前导零
while (res.size() > 1 && res.back() == 0) res.pop_back();
return res;
}
int main()
{
string a, b;
cin >> a >> b;
vector<int> num1, num2;
for (int i = a.size() - 1; i >= 0; i -- ) num1.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i -- ) num2.push_back(b[i] - '0');
auto res = max_mul(num1, num2);
for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);
return 0;
}
高精度除法
给定两个非负整数(不含前导 0) A,B,请你计算 A/B的商和余数。
输入格式
共两行,第一行包含整数 A,第二行包含整数 B。
输出格式
共两行,第一行输出所求的商,第二行输出所求余数。
数据范围
1 ≤ A的长度 ≤ 1e5
, 1 ≤ B ≤ 1e4
, B一定不为0
高精度除法有四种形式:
-
高精度除低精度
-
结果为商和余数
-
结果保留n位小数
-
-
高精度除高精度
-
结果为商和余数
-
结果保留n位小数
-
代码(高精度除低精度,结果为商和余数)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
pair<vector<int>, int> max_div(vector<int>& a, int& b)
{
vector<int> s;
int r = 0;
for (int i = 0; i < a.size(); i ++ )
{
r = r * 10 + a[i];
s.push_back(r / b);
r %= b;
}
while (s.size() > 1 && s.front() == 0) s.erase(s.begin());
return make_pair(s, r);
}
int main()
{
string a;
int b;
cin >> a >> b;
vector<int> num1;
// 在除法中,数据正序存储在数组中
for (auto c : a) num1.push_back(c - '0');
auto res = max_div(num1, b);
for (int i = 0; i < res.first.size(); i ++ ) printf("%d", res.first[i]);
puts("");
printf("%d\n", res.second);
return 0;
}
代码(高精度除高精度,结果为商和余数)
本题中此代码会超时
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool cmp(vector<int>& a, vector<int>& b)
{
if (a.size() != b.size()) return a.size() > b.size();
else
for (int i = 0; i < a.size(); i ++ ) // 由于元素正序存储,高位在前,所以比较时正序遍历
if (a[i] != b[i])
return a[i] > b[i];
return true;
}
void sub(vector<int>& a, vector<int> b)
{
// 在相减之前就已经确保a大于等于b,且a和b的长度相等(b有前导零补位)
for (int i = a.size() - 1; i >= 0; i -- )
{
if (b[i] > a[i])
{
-- a[i - 1];
a[i] += 10;
}
a[i] -= b[i];
}
}
pair<vector<int>, vector<int>> max_div(vector<int>& a, vector<int>& b)
{
// 存储商
vector<int> s(a.size() - b.size() + 1, 0);
for (int i = 0; i < s.size(); i ++ )
{
// 对b数组进行前导零补位
vector<int> tempb(i, 0);
for (auto val : b) tempb.push_back(val);
while (cmp(a, tempb))
{
++ s[i];
sub(a, tempb);
}
}
// 对商去除前导零
while (s.size() > 1 && s.front() == 0) s.erase(s.begin());
// 对a数组去除前导零之后就为余数
while (a.size() > 1 && a.front() == 0) a.erase(a.begin());
return make_pair(s, a);
}
int main()
{
string a, b;
cin >> a >> b;
vector<int> num1, num2;
// 在除法中,数据正序存储在数组中
for (auto c : a) num1.push_back(c - '0');
for (auto c : b) num2.push_back(c - '0');
auto res = max_div(num1, num2);
for (int i = 0; i < res.first.size(); i ++ ) printf("%d", res.first[i]);
puts("");
for (int i = 0; i < res.second.size(); i ++ ) printf("%d", res.second[i]);
return 0;
}
在知悉高精度除法保留商和余数的做法之后,保留n
位小数的做法只需要在求得余数之后,对余数继续做除法操作,若余数小于被除数,则对余数末尾补零之后,再进行除法操作,得到的商存储在一个新的数组中,在输出两部分的商时,中间输出一个小数点即可,至于保留几位小数,只需要按要求输出后一个商数组的前几位即可,若要求四舍五入,只需要对i + 1
位做一个判断即可(大于等于5,i
位 + 1;小于5,i
位不变)
在此不再赘述