3
5
2015
0

高精度压位储存

最近想到要整理下以前学过的一些知识。首先先说说高精度压位储存的知识吧,具体实现是参照RXDoi神牛的,也是得到了他的不少帮助,这里表示感谢!

 

(需要模板的或者不想听废话的就直接翻到后面看我代码就行~)

 

首先,我们知道,高精度算法的最基本的思想是将Int、long long等类型储存不下的数按位存入数组中,这样逐位存可以让空间上没有问题。然而,问题来了,这样的时间效率显然是不够好的。

 

我们来看一道题:hdu1042。题目意思是计算n!,n<=10000。

我们来考虑一般的方法。首先可以大概估出n=10000时位数约为40000左右,我们可以开一个略大于这个值的数组来储存结果。然后计算过程(伪代码)如下:

For i=1 to n do

{

       k=0;

For j=0 to 40000 do

{

              t=a[j]*i+k;

              k=t/10;

              a[j]=t%10;

}

}

//即列竖式人脑算的过程,为了将高精度数(数组a)乘上i,则对于数组a每一位都应乘i.

//这里k是记录每一次乘上i后需要进位的数值,这样下一次加上k即为进位。

 

显然这种方法的时间复杂度应该是O(nm){m为n!的最大位数} 于是会超时。

 

矛盾的关键就在于每次乘i都要对于每一位都要存,那么位数少一些不就行啦?

于是我们想到了一种压位储存的方法。记Base=10000,然后a数组的每一位都存的是不大于Base的数。其实意思就是Base进制(这里即万进制)。对于Base的取法一般10000或100000为宜。我这里取10000.那么这下a数组就不必开到40000了只需开到10000即可。

这样大大加快了效率,就能过了。(当然实际上可以做的更好,每次记录下当前的位数,就不用每次都循环到10000这么多)

 

下面详细介绍下我的写法。

主要准备介绍的基本操作有:高精度结构体的定义,读入,输出,加(减同理),乘,单精度除,乘方

整个高精度数我是写成一个结构体:

struct BigInt

{

int Len,s[MaxL];

//Len为高精度数的位数,s数组存每一位的数。(每一位都是小于Base的)

BigInt() {Len=1;memset(s,0,sizeof(s));}

//定义一个高精度数的时候的初始化

    void clear() {while (Len>1&&s[Len-1]==0) Len--;}

    //每次操作完毕的clear操作

void from_str(string c)

    {

        int L=c.length();Len=L/4+(L%4!=0);

        for (int i=0;i<Len;i++)

            for (int j=max(L-(i+1)*4,0);j<L-i*4;j++) s[i]=s[i]*10+c[j]-'0';

}

//转换string的数为高精度数

    void get(int x)

    {

           Len+=2;

           s[0]+=x;

           for (int i=0;i<Len;i++) s[i+1]+=s[i]/Base,s[i]%=Base;

           clear();

}

//转换int的数为高精度数(其实改写一下就是高精度数+单精度数的加法)

void read() {cin>>c;from_str(c);}

//读入一个数……

    void print()

    {

        for (int i=Len-1;i>=0;i--)

        {

            if (i!=Len-1&&s[i]<1000) putchar('0');

            if (i!=Len-1&&s[i]<100) putchar('0');

            if (i!=Len-1&&s[i]<10) putchar('0');

            printf("%d",s[i]);

        }

        puts("");

}

//输出,每一位如果缺0要补0.

};

然后是加、乘、单精度除、乘方(具体的直接贴代码了)

BigInt operator + (BigInt A,BigInt B)

{

    A.Len=max(A.Len,B.Len);

    for (int i=0;i<A.Len;i++) A.s[i]+=B.s[i];

    for (int i=0;i<A.Len;i++) A.s[i+1]+=A.s[i]/Base,A.s[i]%=Base;

    A.Len++;A.clear();return A;

}

BigInt operator * (BigInt A,BigInt B)

{

    BigInt C;C.Len=A.Len+B.Len;

    for (int i=0;i<A.Len;i++)

        for (int j=0;j<B.Len;j++)

        {

            C.s[i+j]+=A.s[i]*B.s[j];

            if (C.s[i+j]>top) C.s[i+j+1]+=C.s[i+j]/Base,C.s[i+j]%=Base;

        }

    for (int i=0;i<C.Len;i++) C.s[i+1]+=C.s[i]/Base,C.s[i]%=Base;

    C.clear();return C;

}

BigInt operator / (BigInt A,int B)

{

       for(int i=A.Len-1;i>=1;i--) A.s[i-1]+=(A.s[i]%B)*Base,A.s[i]/=B;

A.s[0]/=B;

A.clear();

return A;

}

BigInt Pow(BigInt a,int b)

{

    BigInt res;res.from_str("1");

    for (;b;b>>=1,a=a*a)

    {

        if (b&1) res=res*a;

        if (b==1||!(res<=A)) return res;

    }

    return res;

}

 

提供一些模板题及我的代码:

Hdu1002 计算a+b,注意输出格式。

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
 
const int Base=10000,top=100000000;
 
string c;int n;
struct BigInt
{
    int Len,s[3000];
    BigInt() {Len=1;memset(s,0,sizeof(s));}
    void clear() {while (Len>1&&s[Len-1]==0) Len--;}
    void from_str(string c) 
    {
        int L=c.length();Len=L/4+(L%4!=0);
        for (int i=0;i<Len;i++)
            for (int j=max(L-(i+1)*4,0);j<L-i*4;j++) s[i]=s[i]*10+c[j]-'0';
    }
    void read() {cin>>c;from_str(c);}
    void print()
    {
        for (int i=Len-1;i>=0;i--)
        {
            if (i!=Len-1&&s[i]<1000) putchar('0');
            if (i!=Len-1&&s[i]<100) putchar('0');
            if (i!=Len-1&&s[i]<10) putchar('0');
            printf("%d",s[i]);
        }
    }
};
 
BigInt operator + (BigInt A,BigInt B)
{
    A.Len=max(A.Len,B.Len);
    for (int i=0;i<A.Len;i++) A.s[i]+=B.s[i];
    for (int i=0;i<A.Len;i++) A.s[i+1]+=A.s[i]/Base,A.s[i]%=Base;
    A.Len++;A.clear();return A;
}
 
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    	printf("Case %d:\n",i);
    	BigInt A,B;
    	A.read();B.read();
    	A.print();
    	printf(" + ");
    	B.print();
    	printf(" = ");
		A=A+B;
		A.print();
    	puts("");
    	if(i!=n) puts("");
    }
    return 0;

 

Hdu1042 计算n!。

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
 
const int Base=10000;
const int top=Base*Base;
const int MaxL=15000;

string c;int n;
struct BigInt
{
    int Len,s[MaxL];
    BigInt() {Len=1;memset(s,0,sizeof(s));}
    void clear() {while (Len>1&&s[Len-1]==0) Len--;}
    void from_str(string c) 
    {
        int L=c.length();Len=L/4+(L%4!=0);
        for (int i=0;i<Len;i++)
            for (int j=max(L-(i+1)*4,0);j<L-i*4;j++) s[i]=s[i]*10+c[j]-'0';
    }
    void get(int x)
    {
    	Len+=2;
    	s[0]+=x;
    	for (int i=0;i<Len;i++) s[i+1]+=s[i]/Base,s[i]%=Base;
    	clear();
    }
    void read() {cin>>c;from_str(c);}
    void print()
    {
        for (int i=Len-1;i>=0;i--)
        {
            if (i!=Len-1&&s[i]<1000) putchar('0');
            if (i!=Len-1&&s[i]<100) putchar('0');
            if (i!=Len-1&&s[i]<10) putchar('0');
            printf("%d",s[i]);
        }
        puts("");
    }
};

BigInt operator * (BigInt A,BigInt B)
{
    BigInt C;C.Len=A.Len+B.Len;
    for (int i=0;i<A.Len;i++)
        for (int j=0;j<B.Len;j++) 
        {
            C.s[i+j]+=A.s[i]*B.s[j];
            if (C.s[i+j]>top) C.s[i+j+1]+=C.s[i+j]/Base,C.s[i+j]%=Base;
        }
    for (int i=0;i<C.Len;i++) C.s[i+1]+=C.s[i]/Base,C.s[i]%=Base;
    C.clear();return C;
}
 
int main()
{
	while(scanf("%d",&n)==1)
	{
		BigInt A;
		A.get(1);
		for(int i=1;i<=n;i++)
		{
			BigInt B;
			B.get(i);
			A=A*B;
		}
		A.print();
	}
	return 0;
}

 

BZOJ1213 关于高精度开根,因为结果是整数,所以只需要二分答案然后判断就行了。

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
 
const int Base=10000,top=100000000;
 
string c;int n;
struct BigInt
{
    int Len,s[3000];
    BigInt() {Len=1;memset(s,0,sizeof(s));}
    bool operator <= (const BigInt& B) const
    {
        if (Len!=B.Len) return Len<B.Len;
        for (int i=Len-1;i>=0;i--) if (s[i]!=B.s[i]) return s[i]<B.s[i];
        return 1;
    }
    void clear() {while (Len>1&&s[Len-1]==0) Len--;}
    void from_str(string c) 
    {
        int L=c.length();Len=L/4+(L%4!=0);
        for (int i=0;i<Len;i++)
            for (int j=max(L-(i+1)*4,0);j<L-i*4;j++) s[i]=s[i]*10+c[j]-'0';
    }
    void read() {cin>>c;from_str(c);}
    void print()
    {
        for (int i=Len-1;i>=0;i--)
        {
            if (i!=Len-1&&s[i]<1000) putchar('0');
            if (i!=Len-1&&s[i]<100) putchar('0');
            if (i!=Len-1&&s[i]<10) putchar('0');
            printf("%d",s[i]);
        }
        puts("");
    }
} A,L,R,res,Mid;
 
BigInt operator + (BigInt A,BigInt B)
{
    A.Len=max(A.Len,B.Len);
    for (int i=0;i<A.Len;i++) A.s[i]+=B.s[i];
    for (int i=0;i<A.Len;i++) A.s[i+1]+=A.s[i]/Base,A.s[i]%=Base;
    A.Len++;A.clear();return A;
}
BigInt operator * (BigInt A,BigInt B)
{
    BigInt C;C.Len=A.Len+B.Len;
    for (int i=0;i<A.Len;i++)
        for (int j=0;j<B.Len;j++) 
        {
            C.s[i+j]+=A.s[i]*B.s[j];
            if (C.s[i+j]>top) C.s[i+j+1]+=C.s[i+j]/Base,C.s[i+j]%=Base;
        }
    for (int i=0;i<C.Len;i++) C.s[i+1]+=C.s[i]/Base,C.s[i]%=Base;
    C.clear();return C;
}
BigInt Pow(BigInt a,int b)
{
    BigInt res;res.from_str("1");
    for (;b;b>>=1,a=a*a) 
    {
        if (b&1) res=res*a;
        if (b==1||!(res<=A)) return res;
    }
    return res;
}
BigInt Shr(BigInt A)
{
    for (int i=A.Len-1;i;i--) {A.s[i-1]+=(A.s[i]&1)*Base;A.s[i]>>=1;}
    A.s[0]>>=1;A.clear();return A;
}
void Inc(BigInt &A) 
{
    A.s[0]++;for (int i=0;i<A.Len;i++) A.s[i+1]+=A.s[i]/Base,A.s[i]%=Base;
    A.Len++;A.clear();
}
void Dec(BigInt &A)
{
    A.s[0]--;for (int i=0;i<A.Len;i++) if (A.s[i]<0) A.s[i]+=Base,A.s[i+1]--;
    A.clear();
}
 
int main()
{
    scanf("%d",&n);
    A.read();
    L.from_str("0");
    R.Len=(A.Len*4/n)/4+1;for (int i=0;i<R.Len;i++) R.s[i]=9999;
    while (L<=R)
    {
        Mid=Shr(L+R);
        if (Pow(Mid,n)<=A) res=Mid,L=Mid,Inc(L);else R=Mid,Dec(R);
    }
    res.print();
}

 

其中Shr,Inc,Dec分别是除二,加一,减一。

 

Category: 算法讲解 | Tags: 高精度 | Read Count: 1348

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com