高精度无符号整数算法

版权声明:原创文章版权归本文作者所有,转载文章版权归原作者所有,未经许可请勿转载! https://blog.csdn.net/Peter_Matthew/article/details/83142807

高精度的写法

我们为了方便后来的操作,可以先使用一个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);
}

对于不等于,我们可以在判断完位数后挨个比较位数;对于等于,我们可以返回非不等于。

扫描二维码关注公众号,回复: 3612381 查看本文章

对于小于,我们可以比较位数,然后从高位向低位比较大小;对于大于,我们可以返回反比较的小于;对于小于等于,我们可以返回非大于;对于大于等于,我们可以返回非小于。

逻辑运算符

此内容已因笔者过于蒟蒻或毒瘤而被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 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);
}
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;
}
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;
}
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;
}
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;
}
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;
//}

高精度的用法

我们在之前的定义写法中写的方式可以方便的帮助我们写。
例如洛谷高精度算法试炼场中的几道题
我们在main函数中这么写

int main()
{
    Wint a,b;
    //可以把b改成int型,仍能正常使用(题切不了)
    cin>>a>>b;
    //LuoguP1601 cout<<a+b<<endl;
    //LuoguP2142 if(a-b<0)cout<<"-";cout<<a-b<<endl;
    //LuoguP1303 cout<<a*b<<endl;
    return 0;
}
//LuoguP1255
int main()
{
	int n;
	Wint f[3];
    cin>>n;
    if(n==0)
    {
        printf("0\n");
        return 0;
    }
    f[0]=(Wint)1;
    f[1]=(Wint)1;
    for(int i=2;i<=n;i++)
    {
        f[i%3]=f[(i-1)%3]+f[(i-2)%3];
    }
    cout<<f[n%3]<<endl;
}
//LuoguP1604
//我们先定义一个进制,然后修改check和输入输出即可
int jz;
struct Wint:vector<int>
{
    ...
    Wint& check()
    {
        while(!empty()&&!back())pop_back();
        if(empty())return *this;
        for(int i=1; i<size(); ++i)
        {
            (*this)[i]+=(*this)[i-1]/jz;
            (*this)[i-1]%=jz;
        }
        while(back()>=jz)
        {
            push_back(back()/jz);
            (*this)[size()-2]%=jz;
        }
        return *this;
    }
};
istream& operator>>(istream &is,Wint &n)
{
    string s;
    is>>s;
    n.clear();
    for(int i=s.size()-1; i>=0; --i)
    {
    	if(s[i]>='A')n.push_back(s[i]-'A'+10);
    	else 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)
    {
    	char tmp;
    	if(n[i]>9)tmp='A'+n[i]-10;
        else tmp=n[i]+'0';
        os<<tmp;
    }
    return os;
}
//最后main函数里
int main()
{
    Wint a,b;
    cin>>jz;
    cin>>a>>b;
    cout<<a+b<<endl;
    return 0;
}

知识共享许可协议

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

猜你喜欢

转载自blog.csdn.net/Peter_Matthew/article/details/83142807