zoukankan      html  css  js  c++  java
  • 大整数四则运算(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 << "33[32;1m" << #x <<" -> "; err(x); } while (0)
    void err() { cout << "33[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. 红宝书
  • 相关阅读:
    典型并发任务
    第九章使用共享变量实现并发
    第八章goroutine和通道
    第七章接口
    第六章方法
    第一章
    第四章复合数据类型
    第三章基础数据类型
    Django其他
    VUE学习日记(五) ---- 组件定义 component
  • 原文地址:https://www.cnblogs.com/1625--H/p/11141106.html
Copyright © 2011-2022 走看看