寒假新隊員訓練計划。
在講到大數運算前我們先回顧一下我們常用的變量類型的數值范圍
類型名稱 字節數 取值范圍
short int 2 -2^14 ~ 2^14-1
int 4 -2^31 ~ 2^31-1
unsigned int 4 0 ~ 2^32-1
long long 8 -2^63 ~ 2^63-1
unsigned long long 8 0 ~ 2^64-1 0 ~ 18446744073709551615
從中我們可以看到,即使是 unsigned long long ,最大也只能存儲 1e19 左右的數
而如果我們被要求進行遠大於 1e19 的數的運算,那么常規的做法就無法操作
所以我們引入了一個新的概念——大數
我們可以這么定義它:無法用常規整(浮點)型變量存儲,無法進行簡單符號運算的數
如:123456789123456789123456789123456789123456789,它就為一個大數
當然還有一些高進制數也可當做大數,如abc,我們可以將它看成26進制下的數。它的運算規則也和我們下面提及差不多
好了那么現在問你,給你兩個大數,要求你對它進行簡單(加減乘除)運算,你會怎么做呢?
大數運算模擬
首先我們要考慮如何來把這個數讀入並儲存。因為是大數,我們無法用以往的int、long long甚至unsigned long long儲存
所以我們得先用字符數組對它進行儲存。
我們把該大數每一位分解開來分別存到字符數組的每個位置
假設我們用來儲存的字符串為S,則對大數“12345678912345678912345”的儲存方式為
s[1] = '1' , s[2] = '2' , s[3] = '3' , s[4] = '4' , s[5] = '5' , s[6] = '6' , s[7] = '7' , s[8] = '8' , s[9] = '9' , s[10] = '1' , s[11] = '2' ......
好了我們完成了第一步。
接着我們要對它進行運算。
若題目要求的是對兩個大數進行加法運算,那么我們該怎么做呢?(設我們已經用s1、s2數組分別儲存了兩個大數)
我們知道大數是用字符數組儲存的,所以無法像常規一樣直接用 “ + ”來操作。
這時我們可以回想小學數學教的進位加法(從個位開始逐位相加)某一位的結果每大於等於10,則需要向上一位進1
如圖 ——
好了那么現在怎么操作我們已經知道了,而在開始操作之前我們需要注意什么呢?
在開始加前我們要注意兩點
①、我們要進行加法運算,而這時候用來儲存的還是字符數組,所以要先把字符數組轉為整型數組
②、我們是順序儲存的,所以這時候大數的個位就對應字符數組的最后一位,所以我們需要從后往前模擬進位加法
好了上代碼

#include<bits/stdc++.h> using namespace std; const int N = 2e5 + 10; char s1[N] , s2[N]; int a[N] , b[N]; int ans[N]; int main() { while(~scanf("%s %s" , s1 + 1 , s2 + 1)) { memset(a , 0 , sizeof(a)); memset(b , 0 , sizeof(b)); memset(ans , 0 , sizeof(ans)); int len1 = strlen(s1 + 1) , len2 = strlen(s2 + 1); //把讀入的數顛倒過來同時轉為 int 數組儲存 //顛倒的目的便於模擬進位加法,此時a , b 數組的首位代表大數的個位 for(int i = 1 ; i <= len1 ; i ++) a[len1 - i + 1] = s1[i] - '0'; for(int i = 1 ; i <= len2 ; i ++) b[len2 - i + 1] = s2[i] - '0'; // 加一是因為最高位相加有可能使最高位增加,若 99 11 的最高位為2 , 相加后為 110 最高位為 3 int LEN = max(len1 , len2) + 1; for(int i = 1 ; i <= LEN ; i ++) { //保留 相加后的個位部分 ans[i] += (a[i] + b[i]) % 10; // 若第i位結果大於 10 則 i + 1 位進位 1 ans[i + 1] += (a[i] + b[i]) / 10; } // 去前導 0,如 1+7 = 8,則ans[1] = 8,ans[2] = 0,我們最后的答案為8而不是08,所以要把前導0去掉 while(!ans[LEN]) LEN -- ; for(int i = LEN ; i > 0 ; i --) printf("%d" , ans[i]); printf("\n"); } return 0; }
以上代碼只能針對非負整數使用,若要針對所有整數,我們也只要在讀入的時候判斷字符串首位是否有 "-"(負號)然后模擬小學學的進位加法或者進位減法(a + -b == a - b)就可以了
加、 減、乘其實都差不多,我們只要按照小學教的進位思想進行模擬就可以了
而除法則會相對難一些,用小學學的除法運算進行模擬是有些復雜的
所以我也不細講了(懶),這里推薦大數除法 , 其也有對大數加法、減法、乘法的細講,有興趣的同學可以了解一下
大數運算模板
個人感覺呢大數這一塊 知識點並不是很重要。 所以如果你對以上說的一頭霧水,也沒有關系
大數運算即使你不理解、不會寫,但只要現階段會用就很OK了
這里的用指的是套模板。很多大佬都有在網上留下一套大數運算的模板,其中一些是經過很好的優化、改良的
我們可以從中選擇自己看得舒服的模板並學會 how to use。 當然最好是根據自己的需求適當修飾 ,把它變為自己的東西
下面分享一下我常用的大數模板

#include<bits/stdc++.h> #define MAXN 999999999 #define MAXSIZE 5000 #define DLEN 9 using namespace std; class BigInt { private: int a[500]; int len; bool sign; protected: BigInt add(const BigInt&,const bool)const; BigInt subtract(const BigInt&,const bool)const; public: BigInt() {sign=false;len=1;memset(a,0,sizeof(a));} BigInt(const int); BigInt(const char*); BigInt(const BigInt&); BigInt& operator=(const BigInt&); friend istream& operator>>(istream&,BigInt&); friend ostream& operator<<(ostream&,const BigInt&); friend BigInt abs(const BigInt&); BigInt operator+(const BigInt&)const; BigInt operator-(const BigInt&)const; BigInt operator*(const BigInt&)const; BigInt operator/(const BigInt&)const; BigInt operator/(const int&)const; BigInt operator^(const int&)const; int operator%(const int&)const; bool operator<(const BigInt&)const; bool operator>(const BigInt&)const; bool operator<=(const BigInt&)const; bool operator>=(const BigInt&)const; bool operator==(const BigInt&)const; inline int size() {return DLEN*(len-1)+to_string(a[len-1]).size();} }; BigInt::BigInt(const int b) { int c,d=abs(b); len=0; sign=b<0; memset(a,0,sizeof(a)); while(d>MAXN) { c=d-(d/(MAXN+1))*(MAXN+1); d=d/(MAXN+1); a[len++]=c; } a[len++]=d; } BigInt::BigInt(const char *in) { const char *s; if(in[0]=='-') s=in+1,sign=true; else s=in,sign=false; int t,k,index,L,i; memset(a,0,sizeof(a)); L=strlen(s); len=L/DLEN; if(L%DLEN) len++; index=0; for(i=L-1;i>=0;i-=DLEN) { t=0; k=i-DLEN+1; if(k<0) k=0; for(int j=k;j<=i;j++) t=t*10+s[j]-'0'; a[index++]=t; } } BigInt::BigInt(const BigInt& T):len(T.len),sign(T.sign) { memset(a,0,sizeof(a)); for(int i=0;i<len;i++) a[i]=T.a[i]; } BigInt& BigInt::operator=(const BigInt& n) { sign=n.sign; len=n.len; memset(a,0,sizeof(a)); for(int i=0;i<len;i++) a[i]=n.a[i]; return *this; } istream& operator>>(istream& in,BigInt& b) { char ch[MAXSIZE]; in>>ch; b=BigInt(ch); return in; } ostream& operator<<(ostream& out,const BigInt& b) { out<<(b.sign?"-":"")<<b.a[b.len-1]; for(int i=b.len-2;i>=0;i--) out<<setw(DLEN)<<setfill('0')<<b.a[i]; return out; } BigInt abs(const BigInt& T) { BigInt t(T); t.sign=false; return t; } BigInt BigInt::add(const BigInt& T,const bool flag)const { BigInt t(*this); t.sign=flag; int big; big=T.len>len?T.len:len; for(int i=0;i<big;i++) { t.a[i]+=T.a[i]; if(t.a[i]>MAXN) { t.a[i+1]++; t.a[i]-=MAXN+1; } } if(t.a[big]!=0) t.len=big+1; else t.len=big; return t; } BigInt BigInt::subtract(const BigInt& T,const bool flag)const { BigInt t(*this); t.sign=flag; for(int i=0;i<t.len;i++) { if(t.a[i]<T.a[i]) { int j=i+1; while(t.a[j]==0) j++; t.a[j--]--; while(j>i) t.a[j--]+=MAXN; t.a[i]+=MAXN+1-T.a[i]; } else t.a[i]-=T.a[i]; } while(t.a[t.len-1]==0&&t.len>1) t.len--; return t; } BigInt BigInt::operator+(const BigInt& T)const { BigInt ret; if(sign^T.sign) { BigInt t1(*this),t2(T); if(abs(t1)<abs(t2)) swap(t1,t2); ret=t1.subtract(t2,t1.sign); if(ret.a[ret.len-1]==0&&ret.len==1) ret.sign=false; } else ret=this->add(T,sign); return ret; } BigInt BigInt::operator-(const BigInt& T)const { BigInt ret; if(sign^T.sign) ret=this->add(T,sign); else { BigInt t1(*this),t2(T); bool sn; if(abs(t1)<abs(t2)) { sn=t1.sign^1; swap(t1,t2); } else sn=t1.sign; ret=t1.subtract(t2,sn); if(ret.a[ret.len-1]==0&&ret.len==1) ret.sign=false; } return ret; } BigInt BigInt::operator*(const BigInt& T)const { BigInt ret; long long up; long long temp,temp1; for(int i=0;i<len;i++) { up=0; for(int j=0;j<T.len;j++) { temp=(long long)a[i]*T.a[j]+ret.a[i+j]+up; if(temp>MAXN) { temp1=temp%(MAXN+1); up=temp/(MAXN+1); ret.a[i+j]=temp1; } else { up=0; ret.a[i+j]=temp; } } if(up!=0) ret.a[i+T.len]=up; } ret.sign=sign^T.sign; ret.len=len+T.len; while(ret.a[ret.len-1]==0&&ret.len>1) ret.len--; return ret; } BigInt BigInt::operator/(const BigInt& T)const { BigInt r(*this),ret(0); while(r>=T) { BigInt down(1); while(r-T*down>=0) down=down*10; down=down/10; r=r-T*down; ret=ret+down; } ret.sign=sign^T.sign; return ret; } BigInt BigInt::operator/(const int &b)const { BigInt ret; bool sign1=b<0; long long down=0; for(int i=len-1;i>=0;i--) { ret.a[i]=(a[i]+down*(MAXN+1))/b; down=a[i]+down*(MAXN+1)-(long long)ret.a[i]*b; } ret.sign=sign^sign1; ret.len=len; while(ret.a[ret.len-1]==0&&ret.len>1) ret.len--; return ret; } int BigInt::operator%(const int &b)const { int d=0; for(int i=len-1;i>=0;i--) d=(((long long)d*(MAXN+1))%b+a[i])%b; return sign?-d:d; } BigInt BigInt::operator^(const int &n)const { BigInt ret(1),t(*this); int m=n; while(m) { if(m&1) ret=ret*t; t=t*t; m>>=1; } return ret; } bool BigInt::operator<(const BigInt& T)const { if(sign&&!T.sign) return true; if(!sign&&T.sign) return false; //Ö»ÓÐsignÏàͬ²ÅÄÜ×÷±È½Ï~~ if(len!=T.len) return sign^(len<T.len); for(int ln=len-1;ln>=0;ln--) if(a[ln]!=T.a[ln]) return sign^(a[ln]<T.a[ln]); return false; } bool BigInt::operator>(const BigInt& T)const { return T<*this; } bool BigInt::operator<=(const BigInt& T)const { if(*this==T) return true; return *this<T; } bool BigInt::operator>=(const BigInt& T)const { return T<=*this; } bool BigInt::operator==(const BigInt& T)const { if(sign!=T.sign||len!=T.len) return false; for(int i=len-1;i>=0;i--) if(a[i]!=T.a[i]) return false; return true; } int main() { BigInt a , b; while(cin >> a >> b) { cout << a + b << '\n'; cout << a - b << '\n'; cout << a * b << '\n'; cout << a / b << '\n'; } return 0; }
Java大數類
Java是一門對大數很友好的編程語言,所以掌握好java的大數類是很有幫助的
如果你完全沒接觸 Java 也沒有關系,因為編程語言的語法差別並不會很大(所以你只要花幾分鍾看一下java的入門語法就可以了)
並且這里我們只提及 Java 的大數類,所以應該是難不倒大家的。
好了假設你已經會了java的基本語法,然后我們開始進入正題
在Java 中有兩個類:BigInteger、BigDecimal 分別表示大整數類和大浮點數類。其理論上能表示無限大的數(只要內存不爆)
balabalabala,好了讓我們看看how to use 大數類。
直接上代碼吧(因為 BigInteger 和 BigDecimal 的大多用法都相同,所以這里我只拿 BigInteger 做舉例)

import java.util.*; import java.math.*; //大數類存在於這個包中 public class Main{ public static void main(String[] args){ Scanner cin = new Scanner(System.in); int e = 10; BigInteger a, b; BigInteger ans_add, ans_sub, ans_mul, ans_div, ans_mod , ans_change , ans_abs , ans_pow; a = cin.nextBigInteger(); b = cin.nextBigInteger(); ans_add = a.add(b); //a+b ans_sub = a.subtract(b); //a-b ans_mul = a.multiply(b); //a*b ans_div = a.divide(b); //a/b ans_mod = a.mod(b); //a%b ans_change = BigInteger.valueOf(e); //轉換、賦值 將e的值賦給ans_change,e為int ans_abs = a.abs(); // a的絕對值 ans_pow = a.pow(e); // a的e次冪 , e 為 int System.out.println("a + b = " + ans_add); System.out.println("a - b = " + ans_sub); System.out.println("a * b = " + ans_mul); System.out.println("a / b = " + ans_div); System.out.println("a % b = " + ans_mod); System.out.println("ans_change = " + ans_change); System.out.println("a的絕對值 = " + ans_abs); System.out.println("a的e次冪 = " + ans_pow); if (a.compareTo(b) == 0) //比較a和b System.out.println("相等"); else System.out.println("不相等"); System.out.println(a.toString()); //將大數a轉字符串輸出 int p = 8; System.out.println(a.toString(p)); //將大數a轉換成p進制后 按字符串輸出 } }
總結
大數運算其實只要能算出正確結果就好了,至於怎么寫代碼運算呢?我覺得還是不要寫了,套模板或用Java處理是完全OK的(理直氣壯!)