高精度算法-压位

我们之前做过大整数类的运算的题目
大整数乘法
大整数加法
这个方法看似是无敌的,,,
但是那么如果是一个10000^10000位的数据呢?
数组根本开不到这么大的。。。
有这样的题目吗?
传送门

这时候我们就需要压位了。。。
还记得我们存储数字的方式?
举个栗子:1234 + 1234这是之前的存储方式
这里写图片描述
实际上我们一个数组空间(int)是可以储存 2147483647 以下的数字 这样非常浪费 而且以上存储方式需要按位相加最后的计算次数是4
现在考虑这样的存储方式
这里写图片描述
这样相对上面的存储方式来说利用度较高
理解方式:考虑二进制 十进制 十六进制 的计算方式 满2进1 满10进1 满16进1
那么以上的存储方式请理解为100进制的计算 也就是满100进1
按照这样储存 按照(二)的规则计算 就是把1和2 3和4压到了同一个数组空间里 叫做压位
这样存储的数字计算次数就变成了2 很厉害的优化……
那我们为什么不这样呢
没错这是10000进制……
计算次数变成了1 其实仔细看一下就知道压位的思想就是把本来不用高精的计算尽量的扩大
来达到减少计算次数的目的
特殊的输出
在最后的输出答案部分 需要一些处理(下面假设写压P位的高精(也就是把P个数压到同一个数组空间里))
一句话概括:除最高位外,一切不足P位的数字输出时需要在前面补上0使得满足P位
比如压2位 2 + 99 为例 最后会用到两个数组空间(一个放1,一个放1…)但是这两个一代表的意义不同,
第二个可以原样输出因为这是最高位(即整个数的长度modP后得到的剩余部分 这一部分是独立于其他部分的 如果补上0 最后就是0101 是不对的),第一个就需要补上一个0来使得输出101而不是11

模板代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
using namespace std;


const int power = 4;      //每次运算的位数为10的power次方,在这里定义为了方便程序实现  
const int base = 10000;      //10的power次方。  


//要压位的时候,只需改power 和 base即可,如压万位高精,那么power = 4, base = 10000  


const int MAXL = 10001;    //数组的长度。  


char a[MAXL], b[MAXL], aa[MAXL];  
struct num  
{  
    int a[MAXL];  
    num() { memset(a, 0, sizeof(a)); }                      //初始化  
    num(char *s)                                            //将一个字符串初始化为高精度数  
    {  
        memset(a, 0, sizeof(a));  
        int len = strlen(s);  
        a[0] = (len+power-1) / power;                       //数的长度  
        for (int i=0, t=0, w; i < len ;w *= 10, ++i)          
        {  
            if (i % power == 0) { w = 1, ++t; }  
            a[t] += w * (s[i]-'0');  
        }  
        //初始化数组,这里自己模拟一下,应该很容易懂的~  
    }  
    void add(int k) { if (k || a[0]) a[ ++a[0] ] = k; }     //在末尾添加一个数,除法的时候要用到  
    void re() { reverse(a+1, a+a[0]+1); }                   //把数反过来,除法的时候要用到  
    void print()                                            //打印此高精度数  
    {  
        printf("%d", a[ a[0] ]);        
        //先打印最高位,为了压位 或者 该高精度数为0 考虑  
        for (int i = a[0]-1;i > 0;--i)  
        printf("%0*d", power, a[i]);    
        //这里"%0*d", power的意思是,必须输出power位,不够则前面用0补足  
        printf("\n");  
    }  
} p,q,ans,r;  


bool operator < (const num &p, const num &q)              //判断小于关系,除法的时候有用  
{  
    if (p.a[0] < q.a[0]) return true;  
    if (p.a[0] > q.a[0]) return false;  
    for (int i = p.a[0];i > 0;--i)  
    {  
        if (p.a[i] != q.a[i]) return p.a[i] < q.a[i];  
    }  
    return false;  
}  


num operator + (const num &p, const num &q)               //加法,不用多说了吧,模拟一遍,很容易懂  
{  
    num c;  
    c.a[0] = max(p.a[0], q.a[0]);  
    for (int i = 1;i <= c.a[0];++i)  
    {  
        c.a[i] += p.a[i] + q.a[i];  
        c.a[i+1] += c.a[i] / base;  
        c.a[i] %= base;  
    }  
    if (c.a[ c.a[0]+1 ]) ++c.a[0];  
    return c;  
}  


num operator - (const num &p, const num &q)               //减法,也不用多说,模拟一遍,很容易懂  
{  
    num c = p;  
    for (int i = 1;i <= c.a[0];++i)  
    {  
        c.a[i] -= q.a[i];  
        if (c.a[i] < 0) { c.a[i] += base; --c.a[i+1]; }  
    }  
    while (c.a[0] > 0 && !c.a[ c.a[0] ]) --c.a[0];            
    //我的习惯是如果该数为0,那么他的长度也是0,方便比较大小和在末尾添加数时的判断。  
    return c;  
}  


num operator * (const num &p, const num &q)                   
//乘法,还是模拟一遍。。其实高精度就是模拟人工四则运算!  
{  
    num c;  
    c.a[0] = p.a[0]+q.a[0]-1;  
    for (int i = 1;i <= p.a[0];++i)  
    for (int j = 1;j <= q.a[0];++j)  
    {  
        c.a[i+j-1] += p.a[i]*q.a[j];  
        c.a[i+j] += c.a[i+j-1] / base;  
        c.a[i+j-1] %= base;  
    }  
    if (c.a[ c.a[0]+1 ]) ++c.a[0];  
    return c;  
}  


num operator / (const num &p, const num &q)               //除法,这里我稍微讲解一下  
{  
    num x, y;  
    for (int i = p.a[0];i >= 1;--i)                       //从最高位开始取数  
    {  
        y.add(p.a[i]);             //把数添到末尾(最低位),这时候是高位在前,低位在后  
        y.re();                    //把数反过来,变为统一的存储方式:低位在前,高位在后  
        while ( !(y < q) )         //大于等于除数的时候,如果小于的话,其实答案上的该位就是初始的“0”  
            y = y - q, ++x.a[i];   //看能减几个除数,减几次,答案上该位就加几次。  
        y.re();                    //将数反过来,为下一次添数做准备  
    }  
    x.a[0] = p.a[0];  
    while (x.a[0] > 0 && !x.a[x.a[0]]) --x.a[0];  
    return x;  
}  


int main()  
{  
    while (true)
    {char flag1=getchar();
     if (flag1=='0') break;
     scanf("%s",&a);
     char flag2=getchar();
     while (flag2==' ') flag2=getchar();
     scanf("%s\n",&b);
     memset(aa,0,sizeof(aa));
     aa[0]='0';
     //cout<<a<<" "<<b<<endl;
     reverse(a,a+strlen(a));
     reverse(b,b+strlen(b));
     reverse(aa,aa+strlen(aa));
     p=num(a);
     q=num(b);
     r=num(aa);
     if (flag1=='+'&&flag2=='+')
     {if (q<p)
      {ans=p-q;
       ans.print();
       } else
      {ans=q-p;
       if (r<ans)
       printf("-");
       ans.print();
       }
      }
      else if (flag1=='+'&&flag2=='-')
      {ans=p+q;
       ans.print();
       } else if (flag1=='-'&&flag2=='+')
      {ans=p+q;
       if (r<ans)
       printf("-");
       ans.print();
       } else
      {if (q<p)
       {ans=p-q;
    if (r<ans)
    printf("-");
    ans.print();
        } else
       {ans=q-p;
    ans.print();
        }
       }
      }
   return 0;
}

猜你喜欢

转载自blog.csdn.net/Liukairui/article/details/81295061