• 大数的基本运算


    寒假新队员训练计划。

    在讲到大数运算前我们先回顾一下我们常用的变量类型的数值范围

    类型名称                    字节数      取值范围
    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("
    ");
         }
        return 0;
    }
    View Code

     以上代码只能针对非负整数使用,若要针对所有整数,我们也只要在读入的时候判断字符串首位是否有 "-"(负号)然后模拟小学学的进位加法或者进位减法(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 << '
    ';
            cout << a - b << '
    ';
            cout << a * b << '
    ';
            cout << a / b << '
    ';
        }
        return 0;
    }
    View Code

     

    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进制后 按字符串输出
        }
    }  
    View Code

     

    总结

    大数运算其实只要能算出正确结果就好了,至于怎么写代码运算呢?我觉得还是不要写了,套模板或用Java处理是完全OK的(理直气壮!)

    凡所不能将我击倒的,都将使我更加强大
  • 相关阅读:
    在 ASP.NET 2.0 中上载文件
    ASP.NET(C#)FileUpload实现上传限定类型和大小的文件到服务器<from Copying>
    aspnetupload 上传组件在VS2008中使用详细说明
    基于asp.net 的文件上传和下载~~~转
    设置竖直的分割符【使用div】 (根据屏幕的大小自适应)
    分隔线
    UGUI事件系统
    U3D音频系统
    Unity启动事件-监听:InitializeOnLoad
    VS生成桌面应用程序
  • 原文地址:https://www.cnblogs.com/StarRoadTang/p/12232754.html
Copyright © 2020-2023  润新知