大數的基本運算


寒假新隊員訓練計划。

在講到大數運算前我們先回顧一下我們常用的變量類型的數值范圍

類型名稱                    字節數      取值范圍
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的(理直氣壯!)


免責聲明!

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



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