算法复习——高精度
本文主要内容:
- 高精度加法
- 高精度减法
- 高精度乘法(高精度乘低精度、高精度乘高精度)
- 高精度除法(高精度除以低精度)
主要功能:实现大数的四则运算。
共同思路:将大数存储在字符串中(倒序或者不倒序),然后按位模拟手工计算,将结果填入答案数组中。
注意:这里所讨论的数均为非负整数
1 高精度加法
- 功能:
1.实现大数相加(废话)- 时间复杂度:
o(n)- 基本思路:
1.将两个大数倒序按位存入数组
2.从大数低位到高位相加,模拟手工计算过程,将每一位的结果存入答案数组
3.结果数组倒序输出(因为是从低位到高位来存的)
4.具体实现方式请看代码- 注意事项:
1.注意若用来表进位的变量t在for循环结束后如果大于0,则应把t插入答案数组末尾- 经典例题:
1.模板题:AcWing 791. 高精度加法
//高精度加法函数
//表示传入以字符串形式表示的两个大数a,b
void add(string a, string b)
{
int A[N], B[N], C[N];
int lenA = a.size(), lenB = b.size(), lenC = 0;;//C[]为答案数组
//两个大数倒序存入数组
for(int i = lenA - 1; i >= 0; i--)A[lenA - 1 - i] = a[i] - '0';
for(int i = lenB - 1; i >= 0; i--)B[lenB - 1 - i] = b[i] - '0';
int t = 0; //用作按位相加的结果及进位
int len = max(lenA, lenB);
//模拟手工计算
for(int i = 0; i < len; i++)
{
t += A[i];
t += B[i];
C[lenC++] = t % 10;
t /= 10;
}
if(t)C[lenC++] = t; //若t大于0,则把t计入答案数组
//从后往前输出结果
for(int i = lenC - 1; i >= 0; i--)
cout << C[i];
}
2 高精度减法
- 功能:
1.实现大数相减(废话)- 时间复杂度:
o(n)- 基本思路:
1.将两个大数倒序按位存入数组
2.按照两数大小选择相应的减法函数sub的实参
3.从大数低位到高位,模拟手工减法计算过程,将每一位的结果存入答案数组
4.结果数组倒序输出(因为是从低位到高位来存的)
5.具体实现方式请看代码(把整个程序的代码搬过来了)- 注意事项:
1.只用实现值较大的数减值较小的数的减法函数,小值数减大值数可转化为大值数减小值数,再在结果前添上一个负号即可,这样可以避免分类讨论
2.注意答案数组要去前缀0- 经典例题:
1.模板题:AcWing 792. 高精度减法
//1<=大数的长度<=100000
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
int A[N], B[N], C[N];
string a, b;
int lenA, lenB, lenC;
//比较函数:比较两个字符串的大小
bool cmp(string a, string b)
{
return (a.size() == b.size())? a >= b : a.size() > b.size();
}
//高精度减法函数
void sub(int A[], int B[], int len) //这里的len是大值数A的长度
{
int t = 0;
for(int i = 0; i < len; i++)
{
t += A[i];
t -= B[i];
C[lenC++] = (t % 10 + 10) % 10; //t有可能为负数
if(t < 0)t = -1;//t<0说明补了一位,则相邻更高位数-1
else t = 0;
}
while(C[lenC - 1] == 0 && lenC - 1 > 0)lenC--; //去前缀0
}
int main()
{
cin >> a >> b;
lenA = a.size(), lenB = b.size();
for(int i = 0; i < lenA; i++)A[lenA - 1 - i] = a[i] - '0';
for(int i = 0; i < lenB; i++)B[lenB - 1 - i] = b[i] - '0';
if(cmp(a, b)) //2.注意把len传过去,不同顺序不同len
sub(A, B, lenA);
else
{
sub(B, A, lenB);
cout << "-";
}
for(int i = lenC - 1; i >= 0; i--)
cout << C[i];
return 0;
}
3 高精度乘法
注意: 其实高精度乘高精度算法也可以实现高精度乘低精度,但是时间复杂度要大一些,所以我觉得还是有必要掌握高精度乘低精度的算法。
3.1 高精度乘低精度
- 功能:
1.实现大数与小数相乘(废话)- 时间复杂度:
o(n)- 基本思路:
1.将大数倒序按位存入数组
2.将大数从低位到高位与小数相乘,模拟手工乘法计算过程,将每一位的结果存入答案数组
3.结果数组倒序输出(因为是从低位到高位来存的)- 注意事项:
1.大数所有位都处理完后若t>0,则要用一个循环将t正确放入答案数组中(因为t有可能大于9,所以用循环)
2.若输入的a或者b有一个为0,则最终求得的答案可能为连续个0,所以在输出答案数组前要加个判断。- 经典例题:
1.模板题:AcWing 793. 高精度乘法
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
int A[N], C[N], lenA, lenC;
int b;
void mul(string a, int b)
{
lenA = a.size();
for(int i = 0; i < lenA; i++)A[i] = a[lenA - 1 - i] - '0';
int t = 0;
for(int i = 0; i < lenA; i++)
{
t += A[i] * b;
C[lenC++] = t % 10;
t /= 10;
}
while(t)
{
C[lenC++] = t % 10;
t /= 10;
}
//两个数均大于0,则
if(a != "0" && b)
for(int i = lenC - 1; i >= 0; i--)
cout << C[i];
else cout << 0;
}
int main()
{
string a;
cin >> a >> b;
mul(a, b);
return 0;
}
3.2 高精度乘高精度
- 功能:
1.实现大数与大数相乘(废话)- 时间复杂度:
o(n^2)- 基本思路:
1.将两个大数倒序按位存入数组
2.从低位到高位,模拟手工乘法计算过程,答案数组相应位置加等于两个大数的某两位相乘的结果
3.从低位到高位处理答案数组,使之变为一个十进制数
4.从后往前,将答案数组转化为一个用字符串表示的十进制数,返回结果- 注意事项:
1.若输入的num1或者num2有一个为0,则最终求得的答案可能为连续个0,所以在函数开始前判断两个字符串是否有一个为0,若有则直接返回0。- 经典例题:
1.模板题:LeetCode 43. 字符串相乘- 拓展练习:
1.模拟:Acwing1481. 多项式乘积
//字符串num1和num2的长度不大于110
//num1和num2不以0开头
string multiply(string num1, string num2) {
if(num1 == "0" || num2 == "0")return "0";
int A[120] = {0}, B[120] = {0}, C[240] = {0};
int lenA, lenB, lenC;
lenA = num1.size(), lenB = num2.size();
//倒序存入数组
for(int i = lenA - 1; i >= 0; i--)A[lenA - 1 - i] = num1[i] - '0';
for(int i = lenB - 1; i >= 0; i--)B[lenB - 1 - i] = num2[i] - '0';
//模拟手工计算乘法过程,直接将结果放入答案数组中,答案数组的每一位都可存一个int数
for(int i = 0; i < lenA; i++)
for(int j = 0; j < lenB; j++)
C[i + j] += A[i] * B[j];
//按位处理答案数组,使之变为一个十进制数
int t = 0;
while((lenC <= lenA + lenB - 2) || t) //这样写既可以保证t被正确地放入答案数组中
{
t += C[lenC];
C[lenC] = t % 10;
t /= 10;
lenC++;
}
//将数组中的数转化为字符串输出
string res;
for(int i = lenC - 1; i >= 0; i--)
res += (char)('0' + C[i]);
return res;
}
4 高精度除法
- 功能:
1.实现大数与小数相除(废话)- 时间复杂度:
o(n)- 基本思路:
1.将大数正序按位存入数组
2.从高位到低位,利用一个变量r,模拟手工除法计算过程
3.从前往后,输出答案数组,输出余数- 注意事项:
1.正序存大数
2.答案数组要去前缀0- 经典例题:
1.模板题:AcWing 794. 高精度除法
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
int A[N], C[N], lenA, lenC;
void div(string a, int b)
{
lenA = a.size();
for(int i = 0; i < lenA; i++)A[i] = a[i] - '0';
int r = 0; //r保存余数
for(int i = 0; i < lenA; i++)
{
r = r * 10 + A[i];
C[lenC++] = r / b;
r %= b;
}
int start = 0;
while(C[start] == 0 && start + 1 < lenC)start++;
for(int i = start; i < lenC; i++)
cout << C[i];
cout << endl << r;
}
int main()
{
int b;
string a;
cin >> a >> b;
div(a, b);
return 0;
}
关于高精度数除以高精度数的算法目前我还不会,后续可能会更新~