大整數四則運算(vector與數組兩種版本實現)


每逢大整數四則運算,都會怯懦,雖是算法競賽必會的東西,也零散的學過,簡單的總結過,但不成體系的東西心里一直沒底。

所以今天消耗了大量的卡路里,啃了幾套模板之后終於總結成了一套自己的模板

再也不用擔心大整數啦

基礎

1. 高精度加法

高精度加法等同於算術加法,做單個的加法運算之后存下進位

  • A和B都為正整數
  • vector中下標為0存的是低位(以下都是)
vector<int> add(vector<int> &A,vector<int> &B){
    if(A.size() < B.size()) return add(B,A);//這是為了保證A的位數比B大
    vector<int> C;
    int t = 0;
    for(int i = 0;i < A.size();i++){
        t += A[i];
        if(i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }
    if(t) C.push_back(t);
    //清除前綴0,為了防止000+0等輸入情況
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}

2. 高精度減法

減法同加法原理一樣,也是計算一位,下面用了t這個變量存下向更高一位借的1

  • A和B必須為正整數,結果返回|A-B|
vector<int> sub(vector<int> &A,vector<int> &B){
    vector<int> C;
    for(int i= 0,t = 0;i<A.size();i++){
        t = A[i] - t;
        if(i < B.size()) t -= B[i];
        C.push_back((t+10)%10);
        if(t < 0) t = 1;
        else t = 0;
    }
    //清除前綴0
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}

3. 高精度乘低精度

高精度的每一位與低精度數字進行相乘,因為相乘最大為81,所以結果對10取余得到的數字就是結果在當前位的結果,然后對結果除以10保存更高一位的進位數字

  • A存放正整數,b也為正整
vector<int> mul(vector<int> &A,int b){
    vector<int> C;
    int t = 0;
    for(int i = 0;i < A.size() || t; i++){
        if(i < A.size()) t += A[i] * b;
        C.push_back(t%10);
        t /= 10;
    }
    return C;
}

4. 高精度除以低精度

在計算除法時,會從被除數的最高位算起,每一位的計算結果就是當前余數對除數做除法的結果。然后計算下一位(更小的一位,設第x位)時,要更新余數即 余數*10 + 被除數的x位。

  • A 存放正整數,b也為正整數,r為余數
vector<int> div(vector<int> & A,int b,int &r){
    vector<int> C;
    r = 0;
    for(int i = A.size() - 1;i >= 0;i--){
        r = r * BASE + A[i];
        C.push_back(r/b);
        r %= b;
    }
    reverse(C.begin(),C.end());
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}

什么?乘低精度還不夠?除低精度太low?那我們考慮一些不太常見的情況(openjudge百煉2980)

5. 高精度乘高精度

首先說一下乘法計算的算法,從低位向高位乘,在豎式計算中,我們是將乘數第一位與被乘數的每一位相乘,記錄結果之后,用第二位相乘,記錄結果並且左移一位,以此類推,直到計算完最后一位,再將各項結果相加,得出最后結果。

​ 計算的過程基本上和小學生列豎式做乘法相同。為了編程方便,並不急於處理進位,而是先將所有位上的結果計算之后,再從前往后以此處理進位。

​ 總結一個規律: 即一個數的第i 位和另一個數的第j 位相乘所得的數,一定是要累加到結果的第i+j 位上。這里i, j 都是從數字低位開始從0 開始計數。
ans[i+j] = a[i]*b[j];

​ 另外注意進位時要處理,當前的值加上進位的值再看本位數字是否又有進位;前導清零。

vector<int> mul( vector<int> &A, vector<int> &B) {
    int la = A.size(),lb = B.size();
    vector<int> C(la+lb+10,0);//提前申請結果所需的空間
    for(int i=0;i<la;i++){
        for(int j=0;j<lb;j++){
            C[i+j] += A[i] * B[j];
        }
    }
    for(int i=0;i<C.size();i++){
        if(C[i] >= 10){
            C[i + 1] += C[i] / 10;
            C[i] %= 10;
        }
    }
    //處理前導0
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}

6. 高精度除以高精度

大數除法是四則運算里面最難的一種。不同於一般的模擬,除法操作不是模仿手工除法,而是利用減法操作來實現的。其基本思想是反復做減法,看從被除數里面最多能減去多少個除數,商就是多少。逐個減顯然太慢,要判斷一次最多能減少多少個整數(除數)的10的n次方。

以7546除以23為例:

​ 先用7546減去23的100倍,即減去2300,可以減3次,余下646,此時商就是300 (300=100*3);

​ 然后646減去23的10倍,即減去230,可以減2次,余下186,此時商就是320 (320=300+10*2);

​ 然后186減去23,可以減8次,余下2,此時商就是328 (328=320+1*8);

​ 因為2除以23的結果小於1,而我們又不用計算小數點位,所以不必再繼續算下去了。

計算結果中沒有小數:

vector<int> div(vector<int> A,vector<int> B){
    int la = A.size(),lb = B.size();
    int dv = la - lb; // 相差位數
    vector<int> C(dv+1,0);//提前申請結果所需空間
    //將除數擴大,使得除數和被除數位數相等
    reverse(B.begin(),B.end());
    for(int i=0;i<dv;i++)B.push_back(0);
    reverse(B.begin(),B.end());
    lb = la;
    for(int j=0;j<=dv;j++){
        while(!cmp(A,B)){//這里用到一個比較函數,cmp返回的是A是否比B小,此處判斷的是A是否大於等於B,該循環當A無法再進行減法時結束
            A = sub(A,B);
            C[dv-j]++;//答案里相應的那一位數字++
        }
        B.erase(B.begin());//縮小被除數
    }
    while(C.size()>1 && C.back() == 0)C.pop_back();
    return C;
}

好了,基礎部分到此結束,將上面的內容封裝好之后,我們就可以比較完美的在比賽中使用了。

綜合

  • 該結構體是借用紫書上的模板,BASE是數組一位上面可以存數的值。也可以理解為是一個BASE進制數,WIDTH對應的是BASE的寬度
  • 因為大整數乘大整數所用計算方法的特殊性,BASE應當設置為10,WIDTH設置為1,相應的重載輸出流里面的sprinf函數中也應該控制為1位而不是8位
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct BigInteger{
    //BASE為vector數組中一位中最大存儲的數字,前面都是以10計算的
    //WIDTH為寬度
    static const int BASE = 10000;
    static const int WIDTH = 4;
    vector<int> s;
    //正數為1,負數為-1
    int flag = 1;
    
    //構造函數
    BigInteger(int num = 0){*this = num;}
    BigInteger(string str){*this = str;}
    BigInteger(const BigInteger& t){
        this->flag = t.flag;
        this->s = t.s;
    }
    //賦值函數
    BigInteger operator = (int num){
        s.clear();
        do {
            s.push_back(num % BASE);
            num /= BASE;
        }while(num > 0);
        return *this;
    }
    BigInteger operator = (string &str){
        s.clear();
        int x,len = (str.length()-1)/WIDTH + 1;
        for(int i=0;i<len;i++){
            int end = str.length() - i*WIDTH;
            int start = max(0,end - WIDTH);
            sscanf(str.substr(start,end-start).c_str(),"%d",&x);
            s.push_back(x);
        }
        return *this;
    }

    //基本比較函數 A < B
    bool cmp( vector<int> &A, vector<int> & B){
        if(A.size() != B.size())return A.size() < B.size();
        for(int i=A.size()-1;i>=0;i--){
            if(A[i] != B[i]){
                return A[i] < B[i];
            }
        }
        return false;
    }
    //比較函數如果小於則返回真
    bool operator < ( BigInteger & b){
        return cmp(s,b.s);
    }
    bool operator > ( BigInteger& b){
        return b < *this;
    }
    bool operator <= ( BigInteger &b){
        return !(b < *this);
    }
    bool operator >= ( BigInteger &b){
        return !(*this < b);
    }
    bool operator == ( BigInteger &b){
        return !(b < *this) && (*this < b);
    }
    //基本四則運算
    vector<int> add(vector<int> &A, vector<int> &B);
    vector<int> sub(vector<int> &A, vector<int> &B);
    vector<int> mul(vector<int> &A, int b);
    vector<int> mul(vector<int> &A, vector<int> &B);
    vector<int> div(vector<int> &A, int b);
    vector<int> div(vector<int> A, vector<int> B);

    //重載運算符
    BigInteger operator + (BigInteger &b);
    BigInteger operator - (BigInteger &b);
    BigInteger operator * (BigInteger &b);
    BigInteger operator * (int b);
    BigInteger operator / (BigInteger & b);
    BigInteger operator / (int b);
};
//重載<<
ostream& operator << (ostream &out,const BigInteger& x) {
    if(x.flag == -1)out << '-';
    out << x.s.back();
    for(int i = x.s.size() - 2; i >= 0;i--){
        char buf[20];
        sprintf(buf,"%04d",x.s[i]);//08d此處的8應該與WIDTH一致
        for(int j=0;j<strlen(buf);j++)out<<buf[j];
    }
    return out;
}
//重載輸入
istream& operator >> (istream & in,BigInteger & x){
    string s;
    if(!(in>>s))return in;
    x = s;
    return in;
}
vector<int> BigInteger::add( vector<int> &A, vector<int> &B){
    if(A.size() < B.size())return add(B,A);
    int t = 0;
    vector<int> C;
    for(int i=0;i<A.size();i++){
        if(i<B.size())t += B[i];
        t += A[i];
        C.push_back(t%BASE);
        t /= BASE;
    }
    if(t)C.push_back(t);
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
vector<int> BigInteger::sub( vector<int> &A, vector<int> &B){
    vector<int> C;
    for(int i=0,t=0;i<A.size();i++){
        t = A[i] - t;
        if(i<B.size())t -= B[i];
        C.push_back((t+BASE)%BASE);
        if(t < 0)t = 1;
        else t = 0;
    }
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
vector<int> BigInteger::mul(vector<int> &A,int b){
    vector<int> C;
    int t = 0;
    for(int i = 0;i < A.size() || t; i++){
        if(i < A.size()) t += A[i] * b;
        C.push_back(t%BASE);
        t /= BASE;
    }
    return C;
}
//大數乘大數乘法需要將BASE設置為10,WIDTH設置為1
vector<int> BigInteger::mul( vector<int> &A, vector<int> &B) {
    int la = A.size(),lb = B.size();
    vector<int> C(la+lb+10,0);
    for(int i=0;i<la;i++){
        for(int j=0;j<lb;j++){
            C[i+j] += A[i] * B[j];
        }
    }
    for(int i=0;i<C.size();i++){
        if(C[i] >= BASE){
            C[i + 1] += C[i] / BASE;
            C[i] %= BASE;
        }
    }
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
//大數除以整數
vector<int> BigInteger::div(vector<int> & A,int b){
    vector<int> C;
    int r = 0;
    for(int i = A.size() - 1;i >= 0;i--){
        r = r * BASE + A[i];
        C.push_back(r/b);
        r %= b;
    }
    reverse(C.begin(),C.end());
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
//大數除以大數
vector<int> BigInteger::div(vector<int> A,vector<int> B){
    int la = A.size(),lb = B.size();
    int dv = la - lb; // 相差位數
    vector<int> C(dv+1,0);
    //將除數擴大,使得除數和被除數位數相等
    reverse(B.begin(),B.end());
    for(int i=0;i<dv;i++)B.push_back(0);
    reverse(B.begin(),B.end());
    lb = la;
    for(int j=0;j<=dv;j++){
        while(!cmp(A,B)){
            A = sub(A,B);
            C[dv-j]++;
        }
        B.erase(B.begin());
    }
    while(C.size()>1 && C.back() == 0)C.pop_back();
    return C;
}
BigInteger BigInteger::operator + ( BigInteger & b){
    BigInteger c;
    c.s.clear();
    c.s = add(s,b.s);
    return c;
}

BigInteger BigInteger::operator - ( BigInteger & b) {
    BigInteger c;
    c.s.clear();
    if(*this < b){
        c.flag = -1;
        c.s = sub(b.s,s);
    }
    else{
        c.flag = 1;
        c.s = sub(s,b.s);
    }
    return  c;
}
BigInteger BigInteger::operator *(BigInteger & b){
    BigInteger c;
    c.s = mul(s,b.s);
    return c;
}
BigInteger BigInteger::operator *(int b){
    BigInteger c;
    c.s = mul(s,b);
    return c;
}
BigInteger BigInteger::operator /(BigInteger & b){
    BigInteger c;
    if(*this < b){
        return c;
    }
    else{
        c.flag = 1;
        c.s = div(s,b.s);
    }
    return c;
}
BigInteger BigInteger::operator /(int b){
    BigInteger c;
    BigInteger t = b;
    if(*this < t){
        c.s.push_back(0);
    }
    else{
        c.flag = 1;
        c.s = div(s,b);
    }
    return c;
}

另附數組實現版本,適合運算次數較多,但數字位數不太長的情況

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl; }
template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
const int N = 500 + 5;
class BigInt{
public:
    int a[N];
    int len;
public:
    const int BASE = 10000;
    const int WIDTH = 4;
    void mem(){memset(a, 0, sizeof a);}
    void adjust(){while(len > 1 && a[len-1] == 0) len--;}
public:
    BigInt(){len = 1; mem();}
    BigInt(const int);
    BigInt(const char*);
    BigInt(const BigInt&);
public:
    friend istream& operator >> (istream&, BigInt&);
    friend ostream& operator << (ostream&, BigInt&);
public:
    BigInt operator = (const BigInt &) ;
    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;
    BigInt operator ^ (const int &) const;
public:
    int operator % (const int &T) const;
    bool operator > (const BigInt &T) const;
    bool operator < (const BigInt &T) const;
    bool operator <= (const BigInt &T) const;
    bool operator >= (const BigInt &T) const;
    bool operator == (const BigInt &T) const;
};
BigInt::BigInt(const int b){
    int val = b;
    mem();
    len = 0;
    do {
        a[len++] = val % BASE;
        val /= BASE;
    } while(val);
}
BigInt::BigInt(const char *s){
    mem(); int l = strlen(s);
    int index = 0;
    for(int i=l-1;i>=0;i-=WIDTH){
        int t = 0;
        int k = i - WIDTH + 1;
        if(k < 0) k = 0;
        for(int j=k;j<=i;j++){
            t = t * 10 + s[j] - '0';
        }
        a[index++] = t;
    }
    len = index;
    adjust();
}
BigInt::BigInt(const BigInt&b){
    len = b.len;
    memcpy(a, b.a, sizeof a);
}
istream& operator >>(istream &in, BigInt &b){
    char s[N];
    in >> s;
    b = BigInt(s);
    return in;
}
ostream& operator <<(ostream &out, BigInt &b){
    printf("%d", b.a[b.len-1]);
    for(int i=b.len-2;i>=0;i--){
        printf("%04d", b.a[i]);
    }
    return out;
}
BigInt BigInt::operator = (const BigInt &b) {
    len = b.len;
    memcpy(a, b.a, sizeof a);
    return *this;
}
BigInt BigInt::operator + (const BigInt& b)const{
    BigInt res;
    res.len = max(len, b.len);
    int t = 0;
    for(int i=0;i<res.len;i++){
        if(i < len) t += a[i];
        if(i < b.len) t += b.a[i];
        res.a[i] = t % BASE;
        t /= BASE;
    }
    if(t) res.a[res.len++] = t;
    res.adjust();
    return res;
}
BigInt BigInt::operator - (const BigInt& b)const{
    BigInt res;
    for(int i = 0, t = 0;i < len; i++){
        t = a[i] - t;
        if(i < b.len) t -= b.a[i];
        res.a[i] = (t + BASE) % BASE;
        if(t < 0) t = 1;
        else t = 0;
    }
    res.len = len;
    res.adjust();
    return res;
}

BigInt BigInt::operator * (const BigInt& b)const{
    BigInt res;
    for(int i=0;i<len;i++){
        int up = 0;
        for(int j=0;j<b.len;j++){
            up = a[i] * b.a[j] + res.a[i+j] + up;
            res.a[i+j] = up % BASE;
            up = up / BASE;
        }
        if(up != 0) res.a[i+b.len] = up;
    }
    res.len = len + b.len;
    res.adjust();
    return res;
}
BigInt BigInt::operator * (const int &b) const{
    int t = 0;
    BigInt res;
    res.len = len;
    for(int i=0;i<len;i++){
        t = a[i] * b + t;
        res.a[i] = t % BASE;
        t /= BASE;
    }
    if(t) res.a[res.len++] = t;
    res.adjust();
    return res;
}
BigInt BigInt::operator / (const int &b)const{
    BigInt res;
    res.len = len;
    int i, down = 0;
    for(int i=len-1;i>=0;i--){
        down = down * BASE + a[i];
        res.a[i] = down / b;
        down %= b;
    }
    res.adjust();
    return res;
}
BigInt BigInt::operator ^ (const int &n)const{
    int i, d = 0;
    if(n<0) exit(-1);
    if(n == 0) return 1;
    if(n == 1) return *this;
    BigInt res = *this, t = *this;
    for(int b = n-1; b;b>>=1){
        if(b & 1) res = res * t;
        t = t * t;
    }
    return res;
}

int BigInt::operator%(const int &b)const{
    int i, d = 0;
    for(i=len-1;i>=0;i--){
        d = ((d * BASE) % b + a[i]) % b;
    }
    return d;
}
bool BigInt::operator > (const BigInt &T)const{
    if(len > T.len) return true;
    else if(len < T.len) return false;
    int ln = len - 1;
    while(a[ln] == T.a[ln] && ln>=0) ln--;
    if(ln >= 0 && a[ln] > T.a[ln]) return true;
    else return false;
}
bool BigInt::operator < (const BigInt &T)const{
    return T > *this;
}
bool BigInt::operator >= (const BigInt &T)const{
    return !(T > *this);
}
bool BigInt::operator <= (const BigInt &T)const{
    return !(*this > T);
}
bool BigInt::operator == (const BigInt&T)const{
    return !(*this > T) && !(T > *this);
}
int n;
BigInt f[110];
int main(){
    f[0] = 1;
    for(int i=1;i<=100;i++){
        f[i] = f[i-1] * (4 * i - 2) / (i + 1);
    }
    while(~scanf("%d", &n)){
        if(n == -1) break;
        cout << f[n] << endl;
    }
    return 0;
}

總結

模板只提供了正整數的運算,對於含有負整數的運算,只需要進行合理的轉換即可,見下表

A B + - * /
+ + |A|+|B| |A|-|B| |A|*|B| |A|/|B|
+ - |A|-|B| |A|+|B| -(|A|*|B|) -(|A|/|B|)
- + |B|-|A| -(|A|+|B|) -(|A|*|B|) -(|A|/|B|)
- - -(|A|+|B|) |B|-|A| |A|*|B| |A|/|B|

【附:參考資料】

  1. https://www.cnblogs.com/wuqianling/p/5387099.html
  2. ACwing算法基礎課
  3. 算法競賽入門經典
  4. 紅寶書


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM