深入学习高精度运算(chasem)
高精度运算是算法学习中一个比较重要的内容
实际就是模拟我们平时运算的过程
有一定的实用性,虽然python不用模拟直接就能实现
下面我们逐个学习高精度的加减乘除
1.高精度加法
高精度运算,数字可以达到几百位上千位,只能用字符串来接受。
接收完数字后,高位在前,低位在后。
我们先看下我们平时的加法计算过程
需要个位,十位,这样对应位置对齐,而且加完以后数字还可能边长,这样的话最高位在下标0除,没地方进位了。
我们不妨把字符串倒过来,这样,既方便进位,又可以让个位十位扥个位置自然对齐。
加的过程中,要注意处理进位,然后输出的时候注意结果可能变长了。
加完以后倒序输出即可。
下面是代码
//大整数加法(字符数组接收) 完整版
/*
1.字符串接受两个大整数
2.转化成整数数组(方便计算) 倒序存储(对应位对齐)
3.逐位计算 (注意进位)
4.倒序输出
*/
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int a[999]={0},b[999]={0};
char s1[999],s2[999];
cin>>s1>>s2;//字符串接收
int len1=strlen(s1);
int len2=strlen(s2);
for(int i=0;i<len1;i++)//转换(倒序)
a[len1-1-i]=s1[i]-'0';
for(int i=0;i<len2;i++)
b[len2-1-i]=s2[i]-'0';
int len=max(len1,len2);
for(int i=0;i<len;i++)
{
a[i]+=b[i];
if(a[i]>=10)//处理进位
{
a[i]-=10;
a[i+1]++;
}
}
if(a[len]==1) //结果可能变长一位
len++;
for(int i=len-1;i>=0;i--) //倒序输出
{
cout<<a[i];
}
return 0;
}
2.高精度减法
减法和加法类似,减的时候要出处理借位,还要注意减完结果可能出现很多前缀0,需要去掉。
//大整数减 完整版
/*
1.字符串接受两个大整数
2.转化成整数数组(方便计算) 倒序存储(对应位对齐)
3.逐位计算 (注意借位)
4.倒序输出
*/
#include<bits/stdc++.h>
using namespace std;
int main() {
int a[999]= {0},b[999]= {0},flag=0;
string s1,s2,s3;
cin>>s1>>s2;//字符串接收
if(s1.length()<s2.length() || (s1.length()==s2.length()&&s1<s2) ) {
flag=1;
swap(s1,s2);
}
int len1=s1.length(),len2=s2.length();
for(int i=0; i<len1; i++) //转换(倒序)
a[len1-1-i]=s1[i]-'0';
for(int i=0; i<len2; i++)
b[len2-1-i]=s2[i]-'0';
//len 取较大的长度 避免 100 - 1 中间借位 出现负数
int len=max(len1,len2);
for(int i=0; i < len; i++) {
a[i]-=b[i];
if(a[i]<0) { //处理借位
a[i]+=10;
a[i+1]--;
}
}
while(a[len-1]==0) //去掉前缀0
len--;
if(flag==1) {
cout<<"-";
}
for(int i=len-1; i>=0; i--) { //倒序输出
cout<<a[i];
}
return 0;
}
3.高精度乘法
先观察一下,乘法过程
这是一个 嵌套循环的过程,a的第 i 位 和 b 的 第j位相乘,结果应该加到 c 的 第 i+j 位。
因为c的每个位置要加的数字比较多,所以最后统一处理进位。
实际代码如下:
//大整数乘法 完整版
/*
1.字符串接受两个大整数
2.转化成整数数组(方便计算) 倒序存储(对应位对齐)
3.逐位计算 (注意借位)
4.倒序输出
*/
#include<bits/stdc++.h>
using namespace std;
int main() {
//字符串接收
int a[999]= {0},b[999]= {0},c[999]= {0},flag=0;
string s1,s2,s3;
cin>>s1>>s2;
int len1=s1.length(),len2=s2.length();
for(int i=0; i<len1; i++) //转换 + 倒序
a[len1-1-i]=s1[i]-'0';
for(int i=0; i<len2; i++)
b[len2-1-i]=s2[i]-'0';
//做乘法运算
for(int i=0; i<len1; i++) {
for(int j=0; j<len2; j++)
c[i+j]+=a[i]*b[j];
}
//结果的最大长度
int len=len1+len2;
//处理进位
for(int i=0; i<len; i++) {
if(c[i]>=10)
{
c[i+1]+=c[i]/10;
c[i]%=10;
}
}
while(c[len-1]==0)//去掉前缀 0
len--;
//倒序输出
for(int i=len-1; i>=0; i--) {
cout<<c[i];
}
return 0;
}
4.高精度除法
4.1高精度除法1(高精度/低精度)
还是先看我们的计算过程
实际过程中我们是拿高精度的一部分r 和低精度做除法,商是 r / b ,然后 r % b在和下一位拼接继续做除法。
//高精度除法(高精度/低精度)
#include <bits/stdc++.h>
using namespace std;
int main() {
string a;
int b;
cin >> a >> b;
int r = 0, la = a.size(); // r 为除法过程中 中间产生的被除数
string ans;
for (int i = 0; i < la; i++) {
r = r * 10 + (a[i] - '0');
ans += (char)(r / b + '0');//商直接连接到结果中
r %= b;
}
while (ans[0] == '0' && ans.size() > 1) ans.erase(ans.begin()); //去掉前缀0
cout<<ans;
return 0;
}
4.2高精度除法2(高精度/高精度)
高精度之间除法直接处理比较困难,我们可以把它转换成减法 a-b看减多少次会变成<=0 的数字 这个次数就是商。
这个东西 我们先把高精度运算封装一下,然后实现起来会比较简单
5.高精度运算封装
上面的代码思路都比较简单直接,容易理解,但是太长了,用来解决复杂一些的问题是会不太方便。
接下来我们把他们简化一下,封装成函数,记下来,那么以后我们解决高精度问题就方便多了。
//高精度计算(封装)
#include <bits/stdc++.h>
using namespace std;
string add(string a, string b) {
int la = a.size(), lb = b.size(), f = 0;
string ans;
while (la || lb || f) {
int t = (la>0?a[--la]-'0':0) + (lb>0?b[--lb]-'0':0) + f;
f = t / 10;
t %= 10;
ans += (char)(t + '0');
}
reverse(ans.begin(), ans.end());
return ans;
}
//高精度减法
string minu(string a, string b) {
int flag=0;
//保证大数减小数
if(a.size() < b.size()||(a.size()==b.size()&&a<b))
{
flag=1;
swap(a,b);
}
int la = a.size(), lb = b.size(), f = 0;
string ans;
// a,b没处理完或者还有进位没处理就循环
while (la || lb || f) {
int t = (la>0?a[--la]-48:0) - (lb>0?b[--lb]-48:0) + f;
f = t / 10;
t %= 10;
ans.insert(ans.begin(), (char)(t + 48));//计算结果 串到 字符串头部
}
if(flag == 1)
ans.insert(ans.begin(),'-');
return ans;
}
//高精度乘
string mul(string a, string b) {
int la = a.size(), lb = b.size(), r[10000] = {0};
string ans;
for (int i = 0; i < la; i++)
for (int j = 0; j < lb; j++)
r[i + j] += (a[la - i - 1] - 48) * (b[lb - j - 1] - 48);
//进位
int l = la + lb;
for (int i = 0; i < l; i++) {
r[i + 1] += r[i] / 10;
r[i] %= 10;
ans.insert(ans.begin(), (char)(r[i] + 48));
}
while (ans[0] == '0' && ans.size() > 1) ans.erase(ans.begin());
return ans;
}
//高精度 / 低精度
string div(string a, int b) {
int r = 0, la = a.size();
string ans;
for (int i = 0; i < la; i++) {
r = r * 10 + (a[i] - '0');
ans += (char)(r / b + '0');
r %= b;
}
while (ans[0] == '0' && ans.size() > 1) ans.erase(ans.begin());//去掉多余的0
return ans;
}
//高精度 / 高精度
string div1(string a, string b) {
string ans = "0";
while(a[0]!='-' && a!="0")
{
a=minu(a,b);
while (a[0] == '0' && a.size() > 1) a.erase(a.begin());//去掉多余的0
ans=add(ans,"1");
}
return ans;
}
int main() {
string a,b;
cin>>a>>b;
cout << div1(a,b);
return 0;
}
6.相关题目推荐
上面的封装函数理解清楚,记下来以后,我们就可以轻松解决高精度问题了。
题单
P1601 A+B
P1303 A*B
P1480 A/B
P1255 数楼梯
P2388 阶乘之和
P1760 通天之汉诺塔
如发现有问题,欢迎评论区指正。
如需要详细答疑或者信奥学习咨询,可微Mr_chasem