高精度——压位的思想及应用
本文作者frankchenfu,blogs网址http://www.cnblogs.com/frankchenfu/,转载请保留此文字。
这里我们简单介绍一下高精度的计算。
我们都知道在Cpp/C/Pas
等语言中,整数最大能储存\(2^{64} -1\),超过这个范围就表示不了了(不包括个别支持int128
的编译器)。这个时候,我们如果希望把这些整数存储下来,就需要用到高精度的算法和思想。高精度就是像小学学过的竖式运算一样的(除法除外)。然后就直接模拟即可。除法一位一位地试商即可。
接下来我们发现就是一位一位地加减很慢,我们考虑如何把它加快速度(减小常数)。如果你有学习过关于bitset
的相关知识,那你肯定对压位的方法不陌生。我们把十进制位中的每4~8位并在一起(笔者一般压4位,因为乘法时不会超过int
的范围),然后照样加减,最后并不影响答案,但是要注意输出。对于除法,我们此时发现枚举\(10^4\)到\(10^8\)太慢了,注意到单调性,于是我们考虑二分试商,这样就也可以在\(log_2 BASE\)的时间之内求出来了。
注意初始化、赋值和输出。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef int ll;
struct bign//big number
{
//init
static const int BASE=(int)1e4,POW=4;
static const int MAXLEN=20010;
ll num[MAXLEN];//num[0]=length of the big number
bign(int tp=0)
{
memset(num,tp,sizeof(num));
}
void clear()
{
memset(num,0,sizeof(num));
}
bool operator=(const char ch[])
{
int len=strlen(ch);
for(int i=0;i<len;i++)
num[(len-i-1)/POW+1]=num[(len-i-1)/POW+1]*10+ch[i]-'0';
num[0]=(len-1)/POW+1;
}
//compare
//big number
bool operator<(const bign &rsh)const
{
if(num[0]!=rsh.num[0])
return num[0]<rsh.num[0];
for(int i=num[0];i;i--)
{
if(num[i]!=rsh.num[i])
return num[i]<rsh.num[i];
}
return 0;
}
//int/long long
ll max(ll _x,ll _y)
{
return _x>_y?_x:_y;
}
ll min(ll _x,ll _y)
{
return _x<_y?_x:_y;
}
//operator
//add
void operator+=(const bign &rsh)
{
num[0]=max(num[0],rsh.num[0]);
for(int i=1;i<=num[0];i++)
{
num[i]+=rsh.num[i];
num[i+1]+=num[i]/BASE;
num[i]%=BASE;
}
while(num[num[0]+1]>0)
num[0]++;
}
bign operator+(const bign &rsh)const
{
bign res=*this;res+=rsh;
return res;
}
//subtract
void operator-=(const bign &rsh)
{
for(int i=1;i<=num[0];i++)
{
num[i]-=rsh.num[i];
while(num[i]<0)
{
num[i]+=BASE;
num[i+1]--;
}
}
while(num[num[0]]<=0&&num[0]>0)
num[0]--;
}
bign operator-(const bign &rsh)const
{
bign res=*this;res-=rsh;
return res;
}
//multiply
bign operator*(const bign &rsh)const
{
bign res;
res.num[0]=num[0]+rsh.num[0]-1;
for(int i=1;i<=num[0];i++)
for(int j=1;j<=rsh.num[0];++j)
res.num[i+j-1]+=num[i]*rsh.num[j];
for(int i=1;i<=res.num[0];i++)
{
res.num[i+1]+=res.num[i]/BASE;
res.num[i]%=BASE;
}
while(res.num[res.num[0]+1]>0)
{
res.num[0]++;
res.num[res.num[0]+1]+=res.num[res.num[0]]/BASE;
res.num[res.num[0]]%=BASE;
}
return res;
}
void operator*=(const bign &rsh)
{
bign res=*this;res=res*rsh;
*this=res;
}
//divide
void operator/=(const ll &rsh)
{
for(int i=num[0];i>1;i--)
{
num[i-1]+=(num[i]%rsh*BASE);
num[i]/=rsh;
}
num[1]/=rsh;
while(num[0]>0&&num[num[0]]<=0)
num[0]--;
}
bign operator/(const ll &rsh)const
{
bign temp=*this ;
temp/=rsh;
return temp;
}
void operator/=(const bign &rsh)
{
bign l,r=*this,tmp_one;tmp_one="1";
l.num[0]=1;
while(l<r)
{
bign mid=(l+r+tmp_one)/2;
if(*this<(rsh*mid))
r=mid-tmp_one;
else
l=mid;
}
*this=l;
}
bign operator/(const bign &rsh)const
{
bign res=*this;res/=rsh;
return res;
}
//mod
void operator%=(const bign &rsh)
{
bign res=*this;
res=res-res/rsh*rsh;
*this=res;
}
bign operator%(const bign &rsh)const
{
bign res=*this;res%=rsh;
return res;
}
};
ostream&operator<<(ostream &out,const bign &x)
{
printf("%d",x.num[x.num[0]]);
for(int i=x.num[0]-1;i;i--)
printf("%04d",x.num[i]);
return out;
}