引入
首先了解:
1. int 范围为10^9
2. long long 范围数量级为10^18
如果超过该数量级,该怎么办?
——这就是高精度、大数的算法问题
加法
输入两个整数a,b,输出他们的和(<=10的500次方)
核心是加法的核心——》每个数的结果等于这个位置的a,b加上上一个位置的进位c
c[i]+=a[i]+b[i];//+=的原因是,它要加上上一位的进位,上一位的进位早已存储在c[i]里面了
c[i+1]=c[i]/10;//算出进位的数字
c[i]%=10;//这个位置的数字
算法核心代码给出来了,下面就是要实践了。
题目
https://www.luogu.com.cn/problem/P1601
——就是这个题目(输入两个整数a,b,输出他们的和(<=10的500次方)
#include<bits/stdc++.h>using namespace std;char s1[1000],s2[1000];
int a[1000],b[1000],c[1000];int main()
{scanf("%s",s1);//数组本身代表地址,不用加&scanf("%s",s2);int m=strlen(s1);int n=strlen(s2);//确定长度以确保可以把所有字母转化为数字for(int i=0;i<m;i++){a[m-i-1]=s1[i]-'0';//假设输入123,3应该是最先计算的,但它位于最后一位,a和b的位数不一定一样,所以要倒置数组}for(int i=0;i<n;i++){b[n-i-1]=s2[i]-'0';}m=max(m,n)+1;//+1是为了以防进位for(int i=0;i<m;i++){c[i]+=a[i]+b[i];c[i+1]=c[i]/10;c[i]%=10;}//由于可能出现输出的最高位为0,也就是c[m],但也有输出结果为0的情况,所以需要进行修改n=m-1;if(c[n]==0&&m>0){m--;//最大值减1}for(int i=m-1;i>=0;i--)//?{printf("%d",c[i]);}return 0;
}
另一种写法为:
#include<bits/stdc++.h>using namespace std;char s1[1000],s2[1000];
int a[1000],b[1000],c[1000];int main()
{scanf("%s",s1);//数组本身代表地址,不用加&scanf("%s",s2);int m=strlen(s1);int n=strlen(s2);//确定长度以确保可以把所有字母转化为数字for(int i=0;i<m;i++){a[m-i]=s1[i]-'0';//假设输入123,3应该是最先计算的,但它位于最后一位,a和b的位数不一定一样,所以要倒置数组}for(int i=0;i<n;i++){b[n-i]=s2[i]-'0';}m=max(m,n)+1;//+1是为了以防进位for(int i=1;i<=m;i++){c[i]+=a[i]+b[i];c[i+1]=c[i]/10;c[i]%=10;}//由于可能出现输出的最高位为0,也就是c[m],但也有输出结果为0的情况,所以需要进行修改if(c[m]==0&&m>0){m--;//最大值减1}for(int i=m;i>0;i--)//?{printf("%d",c[i]);}return 0;
}
他们的区别在于,在倒置数组时,一个让数组a,b从0开始,一个让数组从1开始计算。
Tips
1. 做加法时,由于要每一位的数字,所以为了简便,先都输入到char数组中,再转化为数字
2. 转化成数字的时候,记得要倒置数组——》加法计算从最低位计算,所以要把本身最后存进去的数字放到第一次计算的位置
3. 记得要更新c(也就是答案数组)的长度
4. 记得最后判断c的最高位为0且其长度不为0(不是0+0=0这种情况)时,不要输出这个最高位.
5. 最后输出要从最高位输出
减法
依旧是高精度题目
先随便列一个减号方程,一位一位的减
然后我们观察到一下规律:
1. 如果a[i]>b[i]则直接相减出结果即可
2. 如果a[i]<b[i],需要借位
a[i+1]--;
答案等于a[i]-b[i]+10;
根据规律,我们可以写出代码:
if(a[i]>b[i]){c[i]-=b[i];
}else
{c[i]=a[i]-b[i]+10;a[i+1]--;
}
但是需要注意一点——a一定要比b大,不然整体输出会是负数,如果不是,可以把a和b进行交换。
除了这个部分,其它都和加法没什么区别
题目:
https://www.luogu.com.cn/problem/P2142
模板题代码:
#include<bits/stdc++.h>
using namespace std;char s1[10090], s2[10090], s3[10090];
int a[10090], b[10090], c[10090];bool compare(char *s1, char *s2) {int len1 = strlen(s1);int len2 = strlen(s2);if (len1 != len2) return len1 > len2; // 如果长度不同,长的数更大for (int i = 0; i < len1; i++) {if (s1[i] != s2[i]) return s1[i] > s2[i]; // 按位比较}return true; // 如果完全相同,返回true
}int main() {scanf("%s", s1);scanf("%s", s2);if (!compare(s1, s2)) { // 如果s2比较大cout << "-";strcpy(s3, s1);strcpy(s1, s2);strcpy(s2, s3);}int m = strlen(s1);int n = strlen(s2);for (int i = 0; i < m; i++) {a[m - i] = s1[i] - '0';}for (int i = 0; i < n; i++) {b[n - i] = s2[i] - '0';}m = max(m, n);for (int i = 1; i <= m; i++) {if (a[i] < b[i]) {a[i + 1]--;a[i] += 10 ;} c[i] = a[i] - b[i];}while (m > 1 && c[m] == 0) m--; // 去除前导零for (int i = m; i > 0; i--) {printf("%d", c[i]);}return 0;
}
乘法
还是分析如果要一步步得去计算a乘b。
从中得出规律
1. 乘数从最低位开始取,没取一位都要和被乘数相乘,也就是被乘数的每一位乘以这位数字。
2. 假如乘以这位数字结果大于10,则需要进位,10位数进位,个位数留下
3. 把最后的结果错位相加即可
我们可以用字母来替代数字从而观察如何错位相加——例如a4a3a2a1乘b3b2b1
通过计算,我们可以发现,假如结果存储在c数组中,那么aibj对应的是ci+j-1的位置
得出代码:
c[i+j-1]+=a[i]*b[j];
c[i+j]+=c[i+j-1]/10;//算出要进位的数字,这个用+=是因为c[i+j-1]通常不止一个
c[i+j-1]%=10;//算出留下来的这一位
题目:
https://www.luogu.com.cn/problem/P1303
代码:
#include<bits/stdc++.h>using namespace std;char s1[2009],s2[2009];
int a[2009],b[2009],c[4009];int main()
{scanf("%s",s1);scanf("%s",s2);int la=strlen(s1);int lb=strlen(s2);for(int i=0;i<la;i++){a[la-i]=s1[i]-'0';}for(int i=0;i<lb;i++){b[lb-i]=s2[i]-'0';}int lc=la+lb;for(int i=1;i<=la;i++){for(int j=1;j<=lb;j++){c[i+j-1]+=a[i]*b[j];c[i+j]+=c[i+j-1]/10;c[i+j-1]%=10;}}while(c[lc]==0&&lc>1){lc--;}for(int i=lc;i>0;i--){printf("%d",c[i]);}return 0;
}
总结
根据题目和代码,我们知晓了如何利用简单方法来计算高精度的题目,还需要注意的是创建数组的大小时,往往设置为设置精度的次方数
例如如果说明数范围为10的10086次方,那么如果是加减法那么所有的数组大小都为【10090】//多一点保险
如果是乘法,那么需要让c数组,也就是存储结果的数组=2倍的【10090】