大数的基本运算

寒假新队员训练计划。

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

类型名称                    字节数      取值范围
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;
}
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 << '\n';
        cout << a - b << '\n';
        cout << a * b << '\n';
        cout << a / b << '\n';
    }
    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的(理直气壮!)

posted @ 2020-01-25 02:28  GsjzTle  阅读(686)  评论(0编辑  收藏  举报