高精度算法
在C++中,数据的大小是有上限的,若要进行大位数整数的运算,就需要借助高精度运算算法。
注意
-
因为数据位数太大,所以只能用
string
类型变量来接收输入的数据。 -
由于处理进位方便、在数组尾部插入操作比在头部插入复杂度低等原因,所有数据应以倒序存储。
string s1,s2;
cin >> s1 >> s2;
vector<int> A,B;
for(int i = s1.size() - 1; i >= 0;i--) A.push_back(s1[i]-'0');//注意要减'0'!!!
for(int i = s2.size() - 1; i >= 0;i--) B.push_back(s2[i]-'0');
注意: 因为string
中每个元素都是字符型,所以数据在倒序存入vector时需要-'0'
。
- 可以用
vector
数组来存储输入的数据,这样就不需要再维护一个数据的长度变量,而是可以直接调用vector.size()
方法获取,在进行数组操作时也会更加灵活方便。- 在今后的算法学习中,如果一个数据长度不定而又需要使用它的长度,可以尝试使用
vector
数组存储。
- 在今后的算法学习中,如果一个数据长度不定而又需要使用它的长度,可以尝试使用
1. 高精度加法
模板
vector<int> add(vector<int> &A, vector<int> &B)
{
//始终保持形参A的位数形参大于B的位数
if (A.size() < B.size()) return add(B, A);
//C存储高精度加法和
vector<int> C;
//本来需要两个局部变量 一个存储进位 一个存储一位和
//但是这两个变量可以合并,只需要存储进位t
int t = 0;
for (int i = 0; i < A.size(); i ++ )
{
t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
//处理最高位进位
if (t) C.push_back(t);
return C;
}
注意
- 为了简化判断条件,可以始终保持形参A的位数形参大于B的位数。
- 可以将进位和一位和合并成一个变量。
- 不要忘记处理最高位进位
2. 高精度减法
模板
// C = A - B, 满足A >= B, A >= 0, B >= 0
vector<int> sub(vector<int> &A, vector<int> &B)
{
vector<int> C;
for (int i = 0, t = 0; i < A.size(); i ++ )
{ //同理,把借位和一位差合并成一个变量
t = A[i] - t;
if (i < B.size()) t -= B[i];
//当t小于0时,push_back t + 10
//当t大于等于0时,push_back t
//push_back (t + 10) % 10 巧妙地把上述两种情况结合起来了
C.push_back((t + 10) % 10);
//判断借位情况
if (t < 0) t = 1;
else t = 0;
}
//处理前导0
while (C.size() > 1 && C.back() == 0)
C.pop_back();
return C;
}
注意
- 使用这个模板时一定要保证形参A >= 形参B。为此,应在主程序中添加判断语句。
- 不要忘记处理减法的前导0
3. 高精度乘法
模板
// C = A * b, A >= 0, b >= 0
vector<int> mul(vector<int> &A, int b)
{
vector<int> C;
int t = 0;
//这里合并了两个循环
for (int i = 0; i < A.size() || t; i ++ )
{
if (i < A.size()) t += A[i] * b;
C.push_back(t % 10);
t /= 10;
}
/* 原本下面还需要一个循环
while(t){
C.push_back(t % 10);
t /= 10;
}
*/
//处理前导0
while (C.size() > 1 && C.back() == 0)
C.pop_back();
return C;
}
注意
- 乘法也可能会出现前导0!当有一个乘数是0时就会出现。
- 当两个循环的循环体大部分相同时,可以试试将两个循环合并。
4. 高精度除法
模板
// A / b = C ... r, A >= 0, b > 0
vector<int> div(vector<int> &A, int b, int &r)
{
vector<int> C;
//除法要有两个返回值(商和余数),而C++的函数只能有一个返回值,所以余数只能以引用传递的方式返回
r = 0;
//要注意,除法和前面的运算不同,是从最高位开始出结果的。所以循环顺序和前面的运算相反。
for (int i = A.size() - 1; i >= 0; i -- )
{
r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}
//为了和前面的运算输出(倒序输出)保持一致,要把商序列倒序输出
reverse(C.begin(), C.end());
//前导0处理
while (C.size() > 1 && C.back() == 0)
C.pop_back();
return C;
}
注意
- 除法的运算顺序和其他几种运算不太一样,需要注意。