大整数又称为高精度整数。
1. 大整数的存储
使用数组即可。
注意:
- 整数的高位存储在数组的高位,整数的低位存储在数组的低位。
- 由于字符串“
%s
”读入会实际上是逆位存储的,因此要在读入之后需要进行反转。
为了方便随时获取大整数的长度,一般都会定义一个int
型变量len
来记录其长度,并和数组合成一个结构体:
struct bign{
int d[1000];
int len;
};
注意:
- 在定义结构体变量之后,需要马上初始化结构体,我们使用构造函数
- 而且一定要确保所有位数在一开始都全部被赋值为0,不然后面运算会出错。
struct bign{
int d[1000];
int len;
bign(){
memset(d,0,sizeof(d));
len = 0;
}
};
而在输入大整数时,一般都是先用字符串读入,然后再把字符串另存为至bign结构体:
bign change(char str[]){
bign a;
a.len = strlen(str);
for(int i=0;i<a.len;i++){
a.d[i] = str[a.len -i -1] - '0';
}
return a;
}
如果要比较两个bign变量的大小,思路是:
- 比长度;(要保证没有前导0)
- 依次比高位。
代码如下:
int compare(bign a,bign b){
if(a.len > b.len) return 1;
else if(a.len < b.len) return -1;
else{
for(int i=a.len-1;i>=0;i--){
if(a.d[i] > b.d[i]) return 1;
else if(a.d[i] < b.d[i]) return -1;
}
return 0;
}
}
接下来主要介绍四个运算:
1. 高精度与高精度之间的加法;
2. 高精度与高精度之间的减法;
3. 高精度与低精度之间的乘法;
4. 高精度与低精度之间的除法。
至于高精度与高精度之间的乘除,考试一般不涉及。
2. 大整数(高精度整数)的四则运算
2.1 高精度加法
bign add(bign a,bign b){
bign c; //存放新的整数
int carry = 0; //carry是进位
for(int i=0;i<a.len || i<b.len;i++){
int temp = a.d[i] + b.d[i] + carry;
c.d[c.len++] = temp % 10;
carry = temp / 10;
}
//如果最后进位不为0,则直接赋给结果的最高位!!!
if(carry != 0){
c.d[c.len++] = carry;
}
return c;
}
注意:
- 如果最后进位不为0,则直接赋给结果的最高位
- 上述是两个正数的加法,如果是负数,则是减法了。
2.2 高精度减法
bign sub(bign a,bign b){
bign c;
for(int i=0;i<a.len||i<b.len;i++){
if(a.d[i] < b.d[i]){ //如果不够减
a.d[i+1]--;
a.d[i] += 10;
}
c.d[c.len++] = a.d[i] - b.d[i];
}
while(c.len -1 >= 1 && c.d[c.len - 1] == 0){
c.len--; //去除高位的0,同时至少保留一位最低位
}
return c;
}
注意:
- 减法后高位可能有多余的0,要忽略它们,但也要保证结果至少有一位数,即0。
- 这里只是大数减小数,如果是小数减大数,要先大数减小数,再取负号。⭐⭐⭐⭐⭐
2.3 高精度与低精度的乘法💢
bign multi(bign a,int b){
bign c;
int carry = 0; //进位
for(int i=0;i<a.len;i++){
int tmp = a.d[i] * b + carry;
c.d[c.len++] = tmp % 10; //个位作为该位的结果
carry = temp / 10; //高位作为新的进位
}
while(carry != 0){
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
注意:
- 是将低精度整数看作整体,因为高精度的话就会导致溢出的~。
- 还有就是正负数,先保存符号,后面再运算。
2.4 高精度与低精度的除法💢
bign divide(bign a,int b,int& r){ //高精度除法,r是余数
bign c;
c.len = a.len; //被除数的每一位和商的每一位是一一对应的,因此先令长度相等
for(int i=a.len-1;i>=0;i++){ //从高位开始
r = r * 10 + a.d[i]; //和上一位遗留的余数结合
if(r < b){ //不够除,该位为0
c.d[i] = 0;
}else{ //够除
c.d[i] = r / b; //商
r = r % b; //获得新的余数
}
/**也可以直接这样写
//这里面包含了两种情况
c.d[i] = r / b;
r = r % b;
**/
}
while(c.len-1 >= 1 && c.d[c.len-1] == 0){
c.len--; //去除高位的0,同时至少保留一位最低位
}
return c;
}
注意:
- 由于被除数的每一位和商的每一位都是一一对应的,因此先令长度相等;
- 从高位开始;
- 要注意最后的高位可能有多余的0,要忽视它们,但也要保证结果至少有一位数。
2.5 高精度与高精度的乘法
模板的思想很简单:
一开始并不考虑进位,只是单纯地得到每一位上的数字。然后再统一地进行进位操作。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e4+10;
struct bign{
int d[maxn];
int len;
bign(){
fill(d,d+maxn,0);
len=0;
}
};
char str1[1010];
char str2[1010];
bign change(char str[]){
int len = strlen(str);
bign a;
for(int i=0;i<len;i++){
a.d[i] = str[len-1-i]-'0';
a.len++;
}
return a;
}
bign multi(bign a,bign b){
bign c;
c.len=a.len+b.len;//初始化长度
for(int i=0;i<a.len;i++){
for(int j=0;j<b.len;j++){
c.d[i+j]+=a.d[i]*b.d[j];
}
}
//然后进行进位
for(int i=0;i<c.len;i++){//注意这个长度
c.d[i+1] += c.d[i]/10;
c.d[i] = c.d[i]%10;
}
while(c.len-1>=1&&c.d[c.len-1]==0){
c.len--;
}
return c;
}
int main(){
freopen("input.txt","r",stdin);
while(scanf("%s %s",str1,str2)!=EOF){
bign a = change(str1);
bign b = change(str2);
bign c = multi(a,b);
for(int i=0;i<c.len;i++){
printf("%d",c.d[c.len-1-i]);
}
printf("\n");
}
return 0;
}
2.6 高精度与高精度的除法
对于高精度与高精度的除法,需要考虑到多少倍够除的情况,这里使用的思想就是不断地做高精度减法,这样就能实现了。
具体参考王道书上的写法。
2.7 高精度与低精度的取余
//大数取余
LL Qpow_mod(){
LL ans=0;
int n=strlen(a);
for(int i=0;i<n;i++)
ans=(ans*10+(a[i]-'0'))%mod;
return ans;
}
int main(){
scanf("%s",a);
LL b=Qpow_mod();
printf("%lld\n",b);
return 0;
}
3. 题型训练
- 【PAT B1017】A除以B
- 【PAT A1023】Have Fun with Numbers
- a+b——华科
- N的阶乘——清华
- ⭐进制转换2——清华
- LeetCode 2. Add Two Numbers
- 【高精度加法】PAT A1024 Palindromic Number
- LeetCode 989. Add to Array-Form of Integer(高精度与int类型整数的加法,有两种做法)