前言

个人不太喜欢数组式高精度的代码, 所以重写了。

讨论

思路:结构体 + 运算符重载

讲解

大整数加法

目标详解

对于高精度加法a + b = ans,参照加法的竖式运算,加法运算符重载时,主要任务就是结果ans的两个成员变量a[]和len的赋值。

对于len的赋值,加法运算结果的位数至少是参与运算的a.len和b.len的较大值,若是发生了最高位进位,比如999 + 1 = 1000,则再多一位。

而对于a[]的赋值,基本思路如下:


for(int i = 1; i <= ans.len; i++)
  ans.a[i] = a.a[i] + b.a[i];

当然,还得考虑进位的问题,由于a[]的存储是从低位到高位存的,所以进位到+1位即可:

for(int i = 1; i <= ans.len; i++){
  ans.a[i] = a.a[i] + b.a[i];

  ans.a[i + 1] = ans.a[i] / 10;
  ans.a[i] %= 10;
}

但是这样写的话,进位的数据并没有加到结果中,所以:

for(int i = 1; i <= ans.len; i++){
  ans.a[i] += a.a[i] + b.a[i];  //  此处改为 += 

  ans.a[i + 1] = ans.a[i] / 10;
  ans.a[i] %= 10;
}

最后再考虑最高位的进位情况:


if(ans.a[ ans.len + 1 ])
  ans.len++;
代码
#include<iostream>
using namespace std;

const int N = 1005;	//	高精度数位数上限

struct BigInt{
  int a[N];	//	高精度数每一位,个位数在前
  int len;	//	高精度数位数
  
  //	成员函数:初始化函数
  void init(){
    for(int i = 0; i < N; i++)
      a[i] = 0;
    len = 0;
  }
  
  //	构造函数: 无参
  BigInt(){
    init();
  }
  //	构造函数: 有参
  BigInt(string s){
    len = s.length();
    for(int i = 1; i <= len; i++)
      a[i] = s[len - i] - '0';
  }
  
  //	输出函数
  void show(){
    if(len == 0){
      cout << 0 << endl;
      return;
    }
    for(int i = len; i >= 1; i--)
      cout << a[i];
    cout << endl;
  }
  
  //	运算符重载也可以写在结构体内部
  //	BigInt operator + (const BigInt& b) const{...}
};

//	运算符重载 加法
BigInt operator + (const BigInt& a, const BigInt& b){
  BigInt ans;
  ans.len = max(a.len, b.len);	//	至少
  for(int i = 1; i <= ans.len; i++){
    ans.a[i] += a.a[i] + b.a[i];
    
    ans.a[i + 1] = ans.a[i] / 10;
    ans.a[i] %= 10;
  }
  if(ans.a[ ans.len + 1 ])
    ans.len++;
  return ans;
}

int main(){
  string s1, s2;
  cin >> s1 >> s2;
  
  BigInt a(s1), b(s2);
  BigInt c = a + b;
  
  c.show();
  
  return 0;
}

大整数减法

目标详解

对于减法运算,会涉及到大小和正负问题,为了方便,我们可以提前判断大小关系,始终保证大减小。

  1. 高精度大小比较 对于两个高精度数a和b,判断二者大小关系:

首先应当先看位数,位数大的肯定大。 若位数相同,则从高位往低位逐个比较,第一次发现不同时即可判断出大小关系。 若始终都相同,则两数相等 2. 高精度减法 对于高精度减法a - b = ans,参照减法的竖式运算,减法运算符重载时,主要任务就是结果ans的两个成员变量a[]和len的赋值。

对于len的赋值,减法运算中结果的位数至多为参与运算的a.len和b.len的较大值,可能会减少较多,比如1000 - 999 = 1。

而对于a[]的赋值,基本思路如下:

for(int i = 1; i <= ans.len; i++)
  ans.a[i] = a.a[i] - b.a[i];

考虑借位的问题,由于a-b得到ans不能改动a和b的值,所以借位要发生在ans上(也可以减法之前将a赋值到ans上):

for(int i = 1; i <= ans.len; i++){
  if(a.a[i] < b.a[i]){
    ans.a[i] += 10;
    ans.a[i + 1]--;
  }
  ans.a[i] += a.a[i] - b.a[i];  //  改为+=
}

但仍有特殊情况,考虑190-99的十位的减法,最终写法:

for(int i = 1; i <= ans.len; i++){
  if(ans.a[i] + a.a[i] < b.a[i]){
    ans.a[i] += 10;
    ans.a[i + 1]--;
  }
  ans.a[i] += a.a[i] - b.a[i];
}

最后,考虑位数的变化:

while(!ans.a[ ans.len ] && ans.len > 0)
  ans.len--;

注意:由于a[0]值为0,要小心ans.len减为负数,发生在相等的两数相减时。

代码
#include<iostream>
using namespace std;

const int N = 1005;	//	高精度数位数上限

struct BigInt{
  int a[N];	//	高精度数每一位
  int len;	//	高精度数位数
  
  //	成员函数:初始化函数
  void init(){
    for(int i = 0; i < N; i++)
      a[i] = 0;
    len = 0;
  }
  
  //	构造函数: 无参
  BigInt(){
    init();
  }
  //	构造函数: 有参
  BigInt(string s){
    len = s.length();
    for(int i = 1; i <= len; i++)
      a[i] = s[len - i] - '0';
  }
  
  //	输出函数
  void show(){
    if(len == 0){
      cout << 0 << endl;
      return;
    }
    for(int i = len; i >= 1; i--)
      cout << a[i];
    cout << endl;
  }
};

//	运算符重载 大于等于
bool operator >= (const BigInt& a, const BigInt& b){
  if(a.len != b.len)
    return a.len > b.len;
  
  for(int i = a.len; i >= 1; i--)
    if(a.a[i] != b.a[i])
      return a.a[i] > b.a[i];
  
  return true;	//	a b 相等
}
//	运算符重载 减法  大减小
BigInt operator - (const BigInt& a, const BigInt& b){
  BigInt ans;
  ans.len = max(a.len, b.len);	//	至多
  
  for(int i = 1; i <= ans.len; i++){
    if(ans.a[i] + a.a[i] < b.a[i]){
      ans.a[i] += 10;
      ans.a[i + 1]--;
    }
    ans.a[i] += a.a[i] - b.a[i];
  }
  
  while(!ans.a[ ans.len ] && ans.len > 0)
    ans.len--;
  return ans;
}

int main(){
  string s1, s2;
  cin >> s1 >> s2;
  
  BigInt a(s1), b(s2);
  BigInt c;
  
  if(a >= b)
    c = a - b;
  else{
    c = b - a;
    cout << '-';
  }
  
  c.show();
  
  return 0;
}

大整数乘法

目标详解

对于高精度乘法a * b = ans,参照乘法的竖式运算,乘法运算符重载时,主要任务就是结果ans的两个成员变量a[]和len的赋值。

对于len的赋值,乘法运算中结果的位数至少为a.len + b.len - 1,若发生最高位进位则再多一位。

而对于a[]的赋值,由于a*b实际为a的每一位乘以了b的每一位,再分别算入结果的对应为中,所以基本思路:

for(int i = 1; i <= a.len; i++)
  for(int j = 1; j <= b.len; j++)
    ans.a[?] = a.a[i] * b.a[j];

至于对应哪一位,需要自行总结感受,应为i + j - 1。此外再考虑进位,由于乘法中ans的下标在不停变化,因此应注意ans.a[x]本来就有值的情况:

for(int i = 1; i <= a.len; i++)
  for(int j = 1; j <= b.len; j++){
    int k = i + j - 1;
    ans.a[k] += a.a[i] * b.a[j];  //  注意+=
    ans.a[k + 1] += ans.a[k] / 10;  // 注意+=
    ans.a[k] %= 10;
  }

最后再考虑最高位的进位情况:

if(ans.a[ ans.len + 1 ])
ans.len++;
代码
#include<iostream>
using namespace std;

const int N = 2005;	//	高精度数位数上限

struct BigInt{
  int a[N];	//	高精度数每一位
  int len;	//	高精度数位数
  
  //	成员函数:初始化函数
  void init(){
    for(int i = 0; i < N; i++)
      a[i] = 0;
    len = 0;
  }
  
  //	构造函数: 无参
  BigInt(){
    init();
  }
  //	构造函数: 有参
  BigInt(string s){
    len = s.length();
    for(int i = 1; i <= len; i++)
      a[i] = s[len - i] - '0';
  }
  
  //	输出函数
  void show(){
    if(len == 0){
      cout << 0 << endl;
      return;
    }
    for(int i = len; i >= 1; i--)
      cout << a[i];
    cout << endl;
  }
};

//	运算符重载 乘法
BigInt operator * (const BigInt& a, const BigInt& b){
  BigInt ans;
  ans.len = a.len + b.len - 1;	//	至少
  
  for(int i = 1; i <= a.len; i++)
    for(int j = 1; j <= b.len; j++){
      int k = i + j - 1;
      ans.a[k] += a.a[i] * b.a[j];
      
      ans.a[k + 1] += ans.a[k] / 10;
      ans.a[k] %= 10;
    }
  
  if(ans.a[ ans.len + 1 ])
    ans.len++;
  
  return ans;
}

int main(){
  string s1, s2;
  cin >> s1 >> s2;
  
  BigInt a(s1), b(s2);
  BigInt c = a * b;
  
  c.show();
  
  return 0;
}

大整数截取

目标详解

在实际高精度题目中,由于数据量较大,常常要求截取高精度数据的末几位。

由于截取本质上相当于模10的幂(截取3位就是模1000),所以常通过重载模运算符来实现。

若要截取某个高精度数据的后1000位,则需要对10^1000取余,数据量太大。

所以高精度截取中,高精度数a % 1000的含义就是截取后3位,并不是对1000求余。(废话)

对于高精度数据截取a % p = ans,确定ans的a[]和len即可。

对于位数长度len,至多为p位,考虑1001截取后三位。

而对于a[],复制即可:

for(int i = 1; i <= ans.len; i++)
  ans.a[i] = a.a[i];

然后考虑位数变化:

while(!ans.a[ ans.len ] && ans.len > 0)
  ans.len--;

注意: a[0]的值为0,要避免ans.len减为负数的情况,比如1000取末3位。

代码
#include<iostream>
using namespace std;

const int N = 1005;	//	高精度数位数上限

struct BigInt{
  int a[N];	//	高精度数每一位
  int len;	//	高精度数位数
  
  //	成员函数:初始化函数
  void init(){
    for(int i = 0; i < N; i++)
      a[i] = 0;
    len = 0;
  }
  
  //	构造函数: 无参
  BigInt(){
    init();
  }
  //	构造函数: 有参
  BigInt(string s){
    len = s.length();
    for(int i = 1; i <= len; i++)
      a[i] = s[len - i] - '0';
  }
  
  //	输出函数
  void show(){
    if(len == 0){
      cout << 0 << endl;
      return;
    }
    for(int i = len; i >= 1; i--)
      cout << a[i];
    cout << endl;
  }
};

//	运算符重载 模   并非取模,实际为截取后p位
BigInt operator % (const BigInt& a, int p){
  BigInt ans;
  ans.len = p;
  for(int i = 1; i <= ans.len; i++)
    ans.a[i] = a.a[i];
  
  while(!ans.a[ ans.len ] && ans.len > 0)
    ans.len--;
  return ans;
}

int main(){
  string s;
  int p;
  cin >> s >> p;
  
  BigInt a(s);
  a = a % p;
  
  a.show();
  
  return 0;
}

大整数除法

目标详解

1、思路 首先将大数存储为数组。然后有多种方法可以采用:

朴素算法:循环减(模拟除),一直到余数小于除数 连减法:每次按位截取用来滚动减,记录到结果数组里 2、连减法 方便起见我们以两个数 3450除以17模拟: 3450有4位,17有2位,于是从4-2+1 = 3开始(即3450的4位置为第一次个位数)

步骤1:取出3450的前2位34,开始连减得到d[3] = 34/17=2,r=0

步骤2:取出下一位5,累加到r(5),得到d[2] = 5/17=0,r=0,

步骤3:取下一位0累加到r(50),得到d[1] = 50/17 = 2,r=16

最后结果,商为202,余数为16

代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N=2005;
struct BigInt {
  int a[N], len;
  void init() {
    for(int i=1; i<=N-1; i++)
      a[i] = 0;
    len = 0;
  }
  BigInt(){
    init();
  }

  BigInt(string s) {
    init();
    len = s.length();
    for(int i=1; i<=len; i++)
      a[i] = s[len-i] - '0';
  }

  void show() {
    if(len == 0) {
      printf("0");
      return;
    }
    for(int i=len; i>=1; i--)
      printf("%d", a[i]);
  }

  bool less(const BigInt &b1) const {
    if(len == b1.len) {
      for(int i=len; i>=1; i--) {
        if(a[i] == b1.a[i]) continue;
        return a[i] < b1.a[i];
      }
      return false;
    } else
      return len < b1.len;
  }

  bool operator < (const BigInt &b1) const {
    return less(b1);
  }

  //位除,结果为商,自己变为余数
  int dd(const BigInt &n2) {
    int k = 0;//商
    while(!(less(n2))) {//*this >= n2
      for(int i=1; i<=len; i++) {
        if(a[i] < n2.a[i]) {//借位
          a[i] += 10;
          a[i+1]--;
        }
        a[i] -= n2.a[i];
      }
      for(int i=len; i>=1 && a[i]==0; i--) //高位去0
        len--;
      k++;
    }

    return k;
  }

  //按位除下去,由于控制了除的起始位,每次的商不会超过10,可以存储到ans里
  BigInt devide(const BigInt &n2, BigInt &r) const {
    BigInt ans;
    if(less(n2)) {
      r.len = len;
      for(int i=1; i<=len; i++)
        r.a[i] = a[i];
      return ans;//n1 < n2
    }

    ans.len = len - n2.len + 1;//可除的最高位
    r.len = n2.len;
    for(int i=1; i<=r.len; i++)
      r.a[i] = a[ans.len-1+i];
    ans.a[ans.len] = r.dd(n2);

    for(int i = ans.len-1; i>=1; i--) {
      for(int j=r.len+1; j>=2; j--)
        r.a[j] = r.a[j-1];
      r.a[1] = a[i];
      r.len++;
      ans.a[i] = r.dd(n2);
    }
    if(ans.a[ans.len] == 0) ans.len--;
    return ans;
  }

};

int main() {
  string s1, s2;
  cin>>s1>>s2;
  BigInt b1(s1), b2(s2), r;
  BigInt b3 = b1.devide(b2, r);
  b3.show();
  cout<<endl;
  r.show();
  cout<<endl;
  return 0;
}

大整数的幂

目标详解

对于高精度幂运算a^b,其中a为高精度数据,b为普通数。

一般而言,由于结果十分大,所以题目常会要求得到末几位即可,而幂运算算完后取末几位与每乘一次就取一次的结果是一样的。

因此,只要借助循环,让b个a相乘,每次乘完后就立马取出末几位作为下一次乘的因子即可。

也就是说,只要实现高精度数的乘法和截取,再控制好幂运算流程,便能得出正确的结果:

BigInt operator * (const BigInt& a, const BigInt& b){...}
BigInt operator % (const BigInt& a, const int& b){...}

但实际上,高精度数据的幂运算依旧可以借助快速幂算法来优化,只要注意随时截取末位就好了:

BigInt quick_pow(BigInt n1, int b){
  BigInt ans("1");
  BigInt x = ans * n1;
  while(b) {
    if(b%2 == 1)
      ans = (ans*x) % P;
    x = (x*x) % P;
    b /= 2;
  }
  return ans;
}

代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=2005, P=1000;
struct BigInt {
  int a[N], len;
  void init() {
    for(int i=1; i<=N-1; i++)
      a[i] = 0;
    len = 0;
  }
  BigInt(){
    init();
  }
  
  BigInt(string s) {
    init();
    len = s.length();
    for(int i=1; i<=len; i++)
      a[i] = s[len-i] - '0';
  }
  
  void show() {
    if(len <= 0) {
      printf("0");
      return;
    }
    for(int i=len; i>=1; i--)
      printf("%d", a[i]);
  }
  
  BigInt operator * (const BigInt &b1) const {
    BigInt ans;
    ans.len = len + b1.len - 1;
    for(int i=1; i<=len; i++)
      for(int j=1; j<=b1.len; j++) {
        int k = i+j-1;
        ans.a[k] += a[i] * b1.a[j];
        ans.a[k+1] += ans.a[k] / 10;
        ans.a[k] = ans.a[k] % 10;
      }
    if(ans.a[ans.len+1]) ans.len++;
    return ans;
  }
  //p位取余
  BigInt operator % (const int p) const {
    BigInt ans;
    ans.len = min(len, p);
    for(int i=1; i<=ans.len; i++)
      ans.a[i] = a[i];

    for(int i=ans.len; i>=1; i--) {
      if(ans.a[i] == 0)
        ans.len--;
      else
        break;
    }
    
    return ans;
  }
};

BigInt quick_pow(BigInt n1, ll b) {
  BigInt ans("1");
  BigInt x = ans * n1;
  while(b) {
    if(b%2 == 1)
      ans = (ans*x) % P;
    x = (x*x) % P;
    b /= 2;
  }
  return ans;
}

int main() {
  string s1;
  ll b;
  cin>>s1>>b;
  BigInt n1(s1);
  BigInt b3 = quick_pow(n1, b);
  b3.show();
  cout<<endl;
  return 0;
  

后言

因为编辑器不是DEV,导致缩进是2格,自己也懒得加空格了,看看吧. 压位优化懒得写, 以后写吧, 改为short应该有优化 作者制作不易,转载请标注作者## 前言 个人不太喜欢数组式高精度的代码, 所以重写了。 blog 思路:结构体 + 运算符重载

讲解

大整数加法

目标详解

对于高精度加法a + b = ans,参照加法的竖式运算,加法运算符重载时,主要任务就是结果ans的两个成员变量a[]和len的赋值。

对于len的赋值,加法运算结果的位数至少是参与运算的a.len和b.len的较大值,若是发生了最高位进位,比如999 + 1 = 1000,则再多一位。

而对于a[]的赋值,基本思路如下:


for(int i = 1; i <= ans.len; i++)
  ans.a[i] = a.a[i] + b.a[i];

当然,还得考虑进位的问题,由于a[]的存储是从低位到高位存的,所以进位到+1位即可:

for(int i = 1; i <= ans.len; i++){
  ans.a[i] = a.a[i] + b.a[i];

  ans.a[i + 1] = ans.a[i] / 10;
  ans.a[i] %= 10;
}

但是这样写的话,进位的数据并没有加到结果中,所以:

for(int i = 1; i <= ans.len; i++){
  ans.a[i] += a.a[i] + b.a[i];  //  此处改为 += 

  ans.a[i + 1] = ans.a[i] / 10;
  ans.a[i] %= 10;
}

最后再考虑最高位的进位情况:


if(ans.a[ ans.len + 1 ])
  ans.len++;
代码
#include<iostream>
using namespace std;

const int N = 1005;	//	高精度数位数上限

struct BigInt{
  int a[N];	//	高精度数每一位,个位数在前
  int len;	//	高精度数位数
  
  //	成员函数:初始化函数
  void init(){
    for(int i = 0; i < N; i++)
      a[i] = 0;
    len = 0;
  }
  
  //	构造函数: 无参
  BigInt(){
    init();
  }
  //	构造函数: 有参
  BigInt(string s){
    len = s.length();
    for(int i = 1; i <= len; i++)
      a[i] = s[len - i] - '0';
  }
  
  //	输出函数
  void show(){
    if(len == 0){
      cout << 0 << endl;
      return;
    }
    for(int i = len; i >= 1; i--)
      cout << a[i];
    cout << endl;
  }
  
  //	运算符重载也可以写在结构体内部
  //	BigInt operator + (const BigInt& b) const{...}
};

//	运算符重载 加法
BigInt operator + (const BigInt& a, const BigInt& b){
  BigInt ans;
  ans.len = max(a.len, b.len);	//	至少
  for(int i = 1; i <= ans.len; i++){
    ans.a[i] += a.a[i] + b.a[i];
    
    ans.a[i + 1] = ans.a[i] / 10;
    ans.a[i] %= 10;
  }
  if(ans.a[ ans.len + 1 ])
    ans.len++;
  return ans;
}

int main(){
  string s1, s2;
  cin >> s1 >> s2;
  
  BigInt a(s1), b(s2);
  BigInt c = a + b;
  
  c.show();
  
  return 0;
}

大整数减法

目标详解

对于减法运算,会涉及到大小和正负问题,为了方便,我们可以提前判断大小关系,始终保证大减小。

  1. 高精度大小比较 对于两个高精度数a和b,判断二者大小关系:

首先应当先看位数,位数大的肯定大。 若位数相同,则从高位往低位逐个比较,第一次发现不同时即可判断出大小关系。 若始终都相同,则两数相等 2. 高精度减法 对于高精度减法a - b = ans,参照减法的竖式运算,减法运算符重载时,主要任务就是结果ans的两个成员变量a[]和len的赋值。

对于len的赋值,减法运算中结果的位数至多为参与运算的a.len和b.len的较大值,可能会减少较多,比如1000 - 999 = 1。

而对于a[]的赋值,基本思路如下:

for(int i = 1; i <= ans.len; i++)
  ans.a[i] = a.a[i] - b.a[i];

考虑借位的问题,由于a-b得到ans不能改动a和b的值,所以借位要发生在ans上(也可以减法之前将a赋值到ans上):

for(int i = 1; i <= ans.len; i++){
  if(a.a[i] < b.a[i]){
    ans.a[i] += 10;
    ans.a[i + 1]--;
  }
  ans.a[i] += a.a[i] - b.a[i];  //  改为+=
}

但仍有特殊情况,考虑190-99的十位的减法,最终写法:

for(int i = 1; i <= ans.len; i++){
  if(ans.a[i] + a.a[i] < b.a[i]){
    ans.a[i] += 10;
    ans.a[i + 1]--;
  }
  ans.a[i] += a.a[i] - b.a[i];
}

最后,考虑位数的变化:

while(!ans.a[ ans.len ] && ans.len > 0)
  ans.len--;

注意:由于a[0]值为0,要小心ans.len减为负数,发生在相等的两数相减时。

代码
#include<iostream>
using namespace std;

const int N = 1005;	//	高精度数位数上限

struct BigInt{
  int a[N];	//	高精度数每一位
  int len;	//	高精度数位数
  
  //	成员函数:初始化函数
  void init(){
    for(int i = 0; i < N; i++)
      a[i] = 0;
    len = 0;
  }
  
  //	构造函数: 无参
  BigInt(){
    init();
  }
  //	构造函数: 有参
  BigInt(string s){
    len = s.length();
    for(int i = 1; i <= len; i++)
      a[i] = s[len - i] - '0';
  }
  
  //	输出函数
  void show(){
    if(len == 0){
      cout << 0 << endl;
      return;
    }
    for(int i = len; i >= 1; i--)
      cout << a[i];
    cout << endl;
  }
};

//	运算符重载 大于等于
bool operator >= (const BigInt& a, const BigInt& b){
  if(a.len != b.len)
    return a.len > b.len;
  
  for(int i = a.len; i >= 1; i--)
    if(a.a[i] != b.a[i])
      return a.a[i] > b.a[i];
  
  return true;	//	a b 相等
}
//	运算符重载 减法  大减小
BigInt operator - (const BigInt& a, const BigInt& b){
  BigInt ans;
  ans.len = max(a.len, b.len);	//	至多
  
  for(int i = 1; i <= ans.len; i++){
    if(ans.a[i] + a.a[i] < b.a[i]){
      ans.a[i] += 10;
      ans.a[i + 1]--;
    }
    ans.a[i] += a.a[i] - b.a[i];
  }
  
  while(!ans.a[ ans.len ] && ans.len > 0)
    ans.len--;
  return ans;
}

int main(){
  string s1, s2;
  cin >> s1 >> s2;
  
  BigInt a(s1), b(s2);
  BigInt c;
  
  if(a >= b)
    c = a - b;
  else{
    c = b - a;
    cout << '-';
  }
  
  c.show();
  
  return 0;
}

大整数乘法

目标详解

对于高精度乘法a * b = ans,参照乘法的竖式运算,乘法运算符重载时,主要任务就是结果ans的两个成员变量a[]和len的赋值。

对于len的赋值,乘法运算中结果的位数至少为a.len + b.len - 1,若发生最高位进位则再多一位。

而对于a[]的赋值,由于a*b实际为a的每一位乘以了b的每一位,再分别算入结果的对应为中,所以基本思路:

for(int i = 1; i <= a.len; i++)
  for(int j = 1; j <= b.len; j++)
    ans.a[?] = a.a[i] * b.a[j];

至于对应哪一位,需要自行总结感受,应为i + j - 1。此外再考虑进位,由于乘法中ans的下标在不停变化,因此应注意ans.a[x]本来就有值的情况:

for(int i = 1; i <= a.len; i++)
  for(int j = 1; j <= b.len; j++){
    int k = i + j - 1;
    ans.a[k] += a.a[i] * b.a[j];  //  注意+=
    ans.a[k + 1] += ans.a[k] / 10;  // 注意+=
    ans.a[k] %= 10;
  }

最后再考虑最高位的进位情况:

if(ans.a[ ans.len + 1 ])
ans.len++;
代码
#include<iostream>
using namespace std;

const int N = 2005;	//	高精度数位数上限

struct BigInt{
  int a[N];	//	高精度数每一位
  int len;	//	高精度数位数
  
  //	成员函数:初始化函数
  void init(){
    for(int i = 0; i < N; i++)
      a[i] = 0;
    len = 0;
  }
  
  //	构造函数: 无参
  BigInt(){
    init();
  }
  //	构造函数: 有参
  BigInt(string s){
    len = s.length();
    for(int i = 1; i <= len; i++)
      a[i] = s[len - i] - '0';
  }
  
  //	输出函数
  void show(){
    if(len == 0){
      cout << 0 << endl;
      return;
    }
    for(int i = len; i >= 1; i--)
      cout << a[i];
    cout << endl;
  }
};

//	运算符重载 乘法
BigInt operator * (const BigInt& a, const BigInt& b){
  BigInt ans;
  ans.len = a.len + b.len - 1;	//	至少
  
  for(int i = 1; i <= a.len; i++)
    for(int j = 1; j <= b.len; j++){
      int k = i + j - 1;
      ans.a[k] += a.a[i] * b.a[j];
      
      ans.a[k + 1] += ans.a[k] / 10;
      ans.a[k] %= 10;
    }
  
  if(ans.a[ ans.len + 1 ])
    ans.len++;
  
  return ans;
}

int main(){
  string s1, s2;
  cin >> s1 >> s2;
  
  BigInt a(s1), b(s2);
  BigInt c = a * b;
  
  c.show();
  
  return 0;
}

大整数截取

目标详解

在实际高精度题目中,由于数据量较大,常常要求截取高精度数据的末几位。

由于截取本质上相当于模10的幂(截取3位就是模1000),所以常通过重载模运算符来实现。

若要截取某个高精度数据的后1000位,则需要对10^1000取余,数据量太大。

所以高精度截取中,高精度数a % 1000的含义就是截取后3位,并不是对1000求余。(废话)

对于高精度数据截取a % p = ans,确定ans的a[]和len即可。

对于位数长度len,至多为p位,考虑1001截取后三位。

而对于a[],复制即可:

for(int i = 1; i <= ans.len; i++)
  ans.a[i] = a.a[i];

然后考虑位数变化:

while(!ans.a[ ans.len ] && ans.len > 0)
  ans.len--;

注意: a[0]的值为0,要避免ans.len减为负数的情况,比如1000取末3位。

代码
#include<iostream>
using namespace std;

const int N = 1005;	//	高精度数位数上限

struct BigInt{
  int a[N];	//	高精度数每一位
  int len;	//	高精度数位数
  
  //	成员函数:初始化函数
  void init(){
    for(int i = 0; i < N; i++)
      a[i] = 0;
    len = 0;
  }
  
  //	构造函数: 无参
  BigInt(){
    init();
  }
  //	构造函数: 有参
  BigInt(string s){
    len = s.length();
    for(int i = 1; i <= len; i++)
      a[i] = s[len - i] - '0';
  }
  
  //	输出函数
  void show(){
    if(len == 0){
      cout << 0 << endl;
      return;
    }
    for(int i = len; i >= 1; i--)
      cout << a[i];
    cout << endl;
  }
};

//	运算符重载 模   并非取模,实际为截取后p位
BigInt operator % (const BigInt& a, int p){
  BigInt ans;
  ans.len = p;
  for(int i = 1; i <= ans.len; i++)
    ans.a[i] = a.a[i];
  
  while(!ans.a[ ans.len ] && ans.len > 0)
    ans.len--;
  return ans;
}

int main(){
  string s;
  int p;
  cin >> s >> p;
  
  BigInt a(s);
  a = a % p;
  
  a.show();
  
  return 0;
}

大整数除法

目标详解

1、思路 首先将大数存储为数组。然后有多种方法可以采用:

朴素算法:循环减(模拟除),一直到余数小于除数 连减法:每次按位截取用来滚动减,记录到结果数组里 2、连减法 方便起见我们以两个数 3450除以17模拟: 3450有4位,17有2位,于是从4-2+1 = 3开始(即3450的4位置为第一次个位数)

步骤1:取出3450的前2位34,开始连减得到d[3] = 34/17=2,r=0

步骤2:取出下一位5,累加到r(5),得到d[2] = 5/17=0,r=0,

步骤3:取下一位0累加到r(50),得到d[1] = 50/17 = 2,r=16

最后结果,商为202,余数为16

代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N=2005;
struct BigInt {
  int a[N], len;
  void init() {
    for(int i=1; i<=N-1; i++)
      a[i] = 0;
    len = 0;
  }
  BigInt(){
    init();
  }

  BigInt(string s) {
    init();
    len = s.length();
    for(int i=1; i<=len; i++)
      a[i] = s[len-i] - '0';
  }

  void show() {
    if(len == 0) {
      printf("0");
      return;
    }
    for(int i=len; i>=1; i--)
      printf("%d", a[i]);
  }

  bool less(const BigInt &b1) const {
    if(len == b1.len) {
      for(int i=len; i>=1; i--) {
        if(a[i] == b1.a[i]) continue;
        return a[i] < b1.a[i];
      }
      return false;
    } else
      return len < b1.len;
  }

  bool operator < (const BigInt &b1) const {
    return less(b1);
  }

  //位除,结果为商,自己变为余数
  int dd(const BigInt &n2) {
    int k = 0;//商
    while(!(less(n2))) {//*this >= n2
      for(int i=1; i<=len; i++) {
        if(a[i] < n2.a[i]) {//借位
          a[i] += 10;
          a[i+1]--;
        }
        a[i] -= n2.a[i];
      }
      for(int i=len; i>=1 && a[i]==0; i--) //高位去0
        len--;
      k++;
    }

    return k;
  }

  //按位除下去,由于控制了除的起始位,每次的商不会超过10,可以存储到ans里
  BigInt devide(const BigInt &n2, BigInt &r) const {
    BigInt ans;
    if(less(n2)) {
      r.len = len;
      for(int i=1; i<=len; i++)
        r.a[i] = a[i];
      return ans;//n1 < n2
    }

    ans.len = len - n2.len + 1;//可除的最高位
    r.len = n2.len;
    for(int i=1; i<=r.len; i++)
      r.a[i] = a[ans.len-1+i];
    ans.a[ans.len] = r.dd(n2);

    for(int i = ans.len-1; i>=1; i--) {
      for(int j=r.len+1; j>=2; j--)
        r.a[j] = r.a[j-1];
      r.a[1] = a[i];
      r.len++;
      ans.a[i] = r.dd(n2);
    }
    if(ans.a[ans.len] == 0) ans.len--;
    return ans;
  }

};

int main() {
  string s1, s2;
  cin>>s1>>s2;
  BigInt b1(s1), b2(s2), r;
  BigInt b3 = b1.devide(b2, r);
  b3.show();
  cout<<endl;
  r.show();
  cout<<endl;
  return 0;
}

大整数的幂

目标详解

对于高精度幂运算a^b,其中a为高精度数据,b为普通数。

一般而言,由于结果十分大,所以题目常会要求得到末几位即可,而幂运算算完后取末几位与每乘一次就取一次的结果是一样的。

因此,只要借助循环,让b个a相乘,每次乘完后就立马取出末几位作为下一次乘的因子即可。

也就是说,只要实现高精度数的乘法和截取,再控制好幂运算流程,便能得出正确的结果:

BigInt operator * (const BigInt& a, const BigInt& b){...}
BigInt operator % (const BigInt& a, const int& b){...}

但实际上,高精度数据的幂运算依旧可以借助快速幂算法来优化,只要注意随时截取末位就好了:

BigInt quick_pow(BigInt n1, int b){
  BigInt ans("1");
  BigInt x = ans * n1;
  while(b) {
    if(b%2 == 1)
      ans = (ans*x) % P;
    x = (x*x) % P;
    b /= 2;
  }
  return ans;
}

代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=2005, P=1000;
struct BigInt {
  int a[N], len;
  void init() {
    for(int i=1; i<=N-1; i++)
      a[i] = 0;
    len = 0;
  }
  BigInt(){
    init();
  }
  
  BigInt(string s) {
    init();
    len = s.length();
    for(int i=1; i<=len; i++)
      a[i] = s[len-i] - '0';
  }
  
  void show() {
    if(len <= 0) {
      printf("0");
      return;
    }
    for(int i=len; i>=1; i--)
      printf("%d", a[i]);
  }
  
  BigInt operator * (const BigInt &b1) const {
    BigInt ans;
    ans.len = len + b1.len - 1;
    for(int i=1; i<=len; i++)
      for(int j=1; j<=b1.len; j++) {
        int k = i+j-1;
        ans.a[k] += a[i] * b1.a[j];
        ans.a[k+1] += ans.a[k] / 10;
        ans.a[k] = ans.a[k] % 10;
      }
    if(ans.a[ans.len+1]) ans.len++;
    return ans;
  }
  //p位取余
  BigInt operator % (const int p) const {
    BigInt ans;
    ans.len = min(len, p);
    for(int i=1; i<=ans.len; i++)
      ans.a[i] = a[i];

    for(int i=ans.len; i>=1; i--) {
      if(ans.a[i] == 0)
        ans.len--;
      else
        break;
    }
    
    return ans;
  }
};

BigInt quick_pow(BigInt n1, ll b) {
  BigInt ans("1");
  BigInt x = ans * n1;
  while(b) {
    if(b%2 == 1)
      ans = (ans*x) % P;
    x = (x*x) % P;
    b /= 2;
  }
  return ans;
}

int main() {
  string s1;
  ll b;
  cin>>s1>>b;
  BigInt n1(s1);
  BigInt b3 = quick_pow(n1, b);
  b3.show();
  cout<<endl;
  return 0;
  

后言

因为编辑器不是DEV,导致缩进是2格,自己也懒得加空格了,看看吧.

压位优化懒得写, 以后写吧, 改为short应该有优化

作者制作不易,转载请标注作者