1. 程式人生 > >高精度無符號整數演算法

高精度無符號整數演算法

高精度的寫法

我們為了方便後來的操作,可以先使用一個struct封裝內容物。
我們可以在宣告一個struct的時候自動宣告一個vector作為整體型別。

struct Wint:vector<int>
{
};

我們可以寫一個建構函式

	Wint(int n=0)
    {
    	push_back(n);
    }

對於這個建構函式,如果我們不傳n,那麼預設n為0。
同時,我們為了方便寫高精度之間的操作,我們可以先寫一個check函式以幫助我們寫下面的函式。我們檢查下是否在非空的情況下最後一位為0,如果是我們就彈掉(如果是最後一位,額,我們在輸出的時候會解決這個問題,可以提前看下怎麼處理)。我們發現一位大於9,我們就進位,如果最後一位(即最高位)還大於9,我們就新建最後一位。

    Wint& check()
    {
        while(!empty()&&!back())pop_back();
        if(empty())return *this;
        for(int i=1; i<size(); ++i)
        {
            (*this)[i]+=(*this)[i-1]/10;
            (*this)[i-1]%=10;
        }
        while(back()>=10)
        {
            push_back
(back()/10); (*this)[size()-2]%=10; } return *this; }

那麼整體的struct就長這樣

struct Wint:vector<int>
{
    Wint(int n=0)
    {
        push_back(n);
        check();
    }
    Wint& check()
    {
        while(!empty()&&!back())pop_back();
        if(empty
())return *this; for(int i=1; i<size(); ++i) { (*this)[i]+=(*this)[i-1]/10; (*this)[i-1]%=10; } while(back()>=10) { push_back(back()/10); (*this)[size()-2]%=10; } return *this; } };

所有的運算子過載可以參見我的 過載運算子和過載函式 部落格

輸入輸出運算子

我們不要考慮scanf這種讀入~~(因為這樣的實現很複雜我不會),既然高精度的時間複雜度很大(也有可能是我比較蒟蒻)~~,我們就可以考慮流式輸入輸出

istream& operator>>(istream &is,Wint &n)
{
    string s;
    is>>s;
    n.clear();
    for(int i=s.size()-1;i>=0;--i)n.push_back(s[i]-'0');
    return is;
}
ostream& operator<<(ostream &os,const Wint &n)
{
    if(n.empty())os<<0;
    for(int i=n.size()-1;i>=0;--i)os<<n[i];
    return os;
}

至於流式輸入輸出的運算子寫法,可以參考我的部落格過載運算子。

對於輸入流,我們先讀入一串字元,然後從後向前加數(即高位在後,低位在前,這樣方便我們進行加減等運算)。

對於輸出流,我們如果發現為空(一般是隻定義沒有輸入或計算,常用來卡高精)就輸出0;然後從後往前輸出就是高位到低位輸出。

關係運算符

我們不要考慮太多型別與高精度相比較,我們可以先考慮高精度間的邏輯~~,如果我們要比較一個高精度和另一個整數,可以採用stringstream傳(此條因太過毒瘤筆者被Diss到爆而去掉,感興趣的讀者可以自己瞭解下stringstream)~~。

bool operator!=(const Wint &a,const Wint &b)
{
    if(a.size()!=b.size())return 1;
    for(int i=a.size()-1; i>=0; --i)
        if(a[i]!=b[i])return 1;
    return 0;
}
bool operator==(const Wint &a,const Wint &b)
{
    return !(a!=b);
}
bool operator<(const Wint &a,const Wint &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 0;
}
bool operator>(const Wint &a,const Wint &b)
{
    return b<a;
}
bool operator<=(const Wint &a,const Wint &b)
{
    return !(a>b);
}
bool operator>=(const Wint &a,const Wint &b)
{
    return !(a<b);
}

對於不等於,我們可以在判斷完位數後挨個比較位數;對於等於,我們可以返回非不等於。

對於小於,我們可以比較位數,然後從高位向低位比較大小;對於大於,我們可以返回反比較的小於;對於小於等於,我們可以返回非大於;對於大於等於,我們可以返回非小於。

邏輯運算子

此內容已因筆者過於蒟蒻或毒瘤而被Diss到爆炸,現已劃線。

//bool operator!(Wint &n)
//{
//	n.check();
//	return n==0;
//}
//bool operator&&(Wint &a,Wint &b)
//{
//	a.check();b.check();
//	if(!a)return 0;
//	if(!b)return 0;
//	return 1;
//}
//bool operator||(Wint &a,Wint &b)
//{
//	a.check();b.check();
//	if(a>0)return 1;
//	if(b>0)return 1;
//	return 0;
//}

十分的簡單對不對。。。

雙目算術、賦值和自增自減運算子

Wint& operator+=(Wint &a,const Wint &b)
{
    if(a.size()<b.size())a.resize(b.size());
    for(int i=0;i!=b.size();++i)a[i]+=b[i];
    return a.check();
}
Wint operator+(Wint a,const Wint &b)
{
    return a+=b;
}
Wint& operator++(Wint &n)
{
	n+=1;
	return n.check();
}
Wint operator++(Wint &n,int flag)
{
	Wint tmp=n;
	n+=1;
	n.check();
	return tmp;
}

對於加法,我們先實現+=,如果a的size小,我們就要開空間,然後依次相加最後check下即可。
對於+可以返回a+=b。

Wint& operator-=(Wint &a,Wint b)
{
    if(a<b)swap(a,b);
    for(int i=0; i!=b.size(); a[i]-=b[i],++i)
        if(a[i]<b[i])
        {
            int j=i+1;
            while(!a[j])++j;
            while(j>i)
            {
                --a[j];
                a[--j]+=10;
            }
        }
    return a.check();
}
Wint operator-(Wint a,const Wint &b)
{
    return a-=b;
}
Wint& operator--(Wint &n)
{
	n-=1;
	return n.check();
}
Wint operator--(Wint &n,int flag)
{
	Wint tmp=n;
	n-=1;
	n.check();
	return tmp;
}

對於減法,我們先實現-=,如果a小我們就要交換a和b(我們寫的是高精度無符號整數),然後後依次相加最後check下即可。
對於-可以返回a-=b。

Wint operator*(const Wint &a,const Wint &b)
{
    Wint n;
    n.assign(a.size()+b.size()-1,0);
    for(int i=0; i!=a.size(); ++i)
        for(int j=0; j!=b.size(); ++j)
            n[i+j]+=a[i]*b[j];
    return n.check();
}
Wint& operator*=(Wint &a,const Wint &b)
{
    return a=a*b;
}

對於乘法,我們需要先實現*,在新建的返回高精中,第i+j位加上 a i b j a_i * b_j 。然後對於*=返回a=a*b即可

Wint divmod(Wint &a,const Wint &b)
{
    Wint ans;
    for(int t=a.size()-b.size(); a>=b; --t)
    {
        Wint d;
        d.assign(t+1,0);
        d.back()=1;
        Wint c=b*d;
        while(a>=c)
        {
            a-=c;
            ans+=d;
        }
    }
    return ans;
}
Wint operator/(Wint a,const Wint &b)
{
    return divmod(a,b);
}
Wint& operator/=(Wint &a,const Wint &b)
{
    return a=a/b;
}

學過Python的讀者應該知道在Python中有一個divmod函式(在Python 2.3之前不允許處理複數),這個函式在Python中把除數和餘數運算結果結合起來,返回一個包含商和餘數的元組(a//b,a%b)。
而在C++中我們寫的這個函式就是除法。assign是C++string類的成員函式,用於拷貝、賦值操作,它們允許我們順次地把一個string物件的部分內容拷貝到另一個string物件上。
然後寫/,返回divmod(a,b);/=返回a=a/b。

Wint& operator%=(Wint &a,const Wint &b)
{
    divmod(a,b);
    return a;
}
Wint operator%(Wint a,const Wint &b)
{
    return a%=b;
}

常用函式

Wint pow(const Wint &n,const Wint &k)
{
    if(k.empty())return 1;
    if(k==2)return n*n;
    if(k.back()%2)return n*pow(n,k-1);
    return pow(pow(n,k/2),2);
}
//下面是非遞迴寫法
Wint pow(Wint n,Wint k)
{
	Wint ans(1);
	while(k>0)
	{
		if(k%2>0)
			ans*=n;
		n*=n;
		k/=2;
	}
	return ans;
}

其實快速冪實現的思想非常好想,所以就這麼寫了出來。

回顧

那麼整體的寫法如下

struct Wint:vector<int>
{
    Wint(int n=0)
    {
        push_back(n);
        check();
    }
    Wint& check()
    {
        while(!empty()&&!back())pop_back();
        if(empty())return *this;
        for(int i=1; i<size(); ++i)
        {
            (*this)[i]+=(*this)[i-1]/10;
            (*this)[i-1]%=10;
        }
        while(back()>=10)
        {
            push_back(back()/10);
            (*this)[size()-2]%=10;
        }
        return *this;
    }
};
istream& operator>>(istream &is,Wint &n)
{
    string s;
    is>>s;
    n.clear();
    for(int i=s.size()-1; i>=0; --i)n.push_back(s[i]-'0');
    return is;
}
ostream& operator<<(ostream &os,const Wint &n)
{
    if(n.empty())os<<0;
    for(int i=n.size()-1; i>=0; --i)os<<n[i];
    return os;
}
bool operator!=(const Wint &a,const Wint &b)
{
    if(a.size()!=b.size())return 1;
    for(int i=a.size()-1; i>=0; --i)
        if(a[i]!=b[i])return