【算法笔记】 压位高精度整除法

算法介绍

主要是模拟竖式计算的过程。可以扩展为 BASE 进制数的高精度整除除问题。不考虑负数问题。

记:
BASE为x(一般情形下为10)
被除数为 \(A = a_p x^p + a_{p-1} x^{p-1} + ...... + a_1 x + a_0\)
除数为 \(B = b_q x^q + ...... +b_1 x + b_0\)

\(A<B\) 时,我们有 \(A/B=0\)

此处,我们主要考虑 \(A \geq B\)

这种情形下,必有 \(p>q \space or \space a_p > b_q\)

让我们观察竖式计算的过程。

不难发现,竖式计算原理如下:
要求 C 使得 \(A = C \times B + R\)\(R < B\)
根据乘法分配律,可分解为 \(A = (c_t x^t B + ... + c_1 x B + c_0 B) + R\)
换句话说,我们只需要针对每个 \(x^d B\) 求出对应的 \(c_d\) 就行了
实际上,\(x^d B\) 就相当于 x 进制下的 B 移动 d 位。
而答案 C 可以表示为x进制下的 c1c2...ct

接下来考虑具体实现。

首先,考虑每一次计算时,要移动几位的问题。
这里我采用了一种比较简单的方式:移动到最高位重叠。
对于可能出现的移动后被除数最高位比除数最高位小的问题,我的做法是将此处的 \(c_i\) 记为 0,再将被除数的最高位乘以 x 后并入次高位里。
由于 \(a_i < b_j\) ,我们有 \(b_j \times x > a_i \times x + a_{i-1}\)
也就是说,我们算出的 \(c_k*b_j\)\(c_k\) 必然小于 x
仍然符合 x 进制数的特点。

接下考虑如何估计 \(c_i\)
在笔算时,我们有时不能马上看出答案。我们模拟竖式计算时也同理。
我采用的是利用最高位相除来估算的方法。
例如 \(900 \div 199\) 估算百位时,直接看成 c = 9/1 ,再慢慢减小直到 199*c < 900

上面的这个做法在不压位(BASE较小或为10)的情形下需要减的次数不高,可视为常数。
但在压位下,需要减多少次就显得十分玄学。
对于这两个数 A 和 B,假定 p = q:
\(A = a_p x^p + ...... + a_1 x + a_0\)
\(B = b_q x^q + ...... +b_1 x + b_0\)
那么需要减的次数就是 \(a_p \div b_p - A \div B\)(整除)。
这个数的范围我不会求,有可能与 BASE 的规模相近。
我下文代码库中使用的是这种写法,但是 ZZY大师 给出了一种优化的思路,就是在求 c 的时候不是一个一个地减,而是从预估出的 c 的最高位开始减。这样就可以将规模缩小为与压位的位数相近,而不是与 BASE 相近。

代码库

数据生成器

#include <cstdio>
#include <cstdlib>
#include <ctime>
int main(){
    char str[]="0.in";
    srand(time(NULL));
    for(int i=1;i<=9;i++){
        str[0]=i+48;
        freopen(str,"w",stdout);
        putchar(i+48);
        for(int j=2;j<=1000;j++) putchar(rand()%10+48);
        putchar('\n');
        putchar(10-i+48);
        for(int j=2;j<=233;j++) putchar(rand()%10+48);
        putchar('\n');
        fclose(stdout);
    }
    return 0;
}

数据生成器 python 部分

root = "D:\\Data_Testing\\data\\Bint Div"
for i in range(1,10) :
    f = open(root+"\\%d.in"%(i),"r");
    a = f.readline(); b = f.readline();
    a1 = int(a); b1 = int(b);
    f.close();
    f = open(root+"\\%d.out"%(i),"w+");
    f.write(str(a1//b1));
    f.close;

数据验证器 python

a = int(input()); 
b = int(input());
print(a//b);

压位高精度整除

#include <cstdio>
#include <cstring>
typedef long long ll;
#define REG register
#define rep(i,a,b) for(REG int i=a;i<=b;i++)
#define Rep(i,a,b) for(REG int i=a;i>=b;i--)
const int N=2000,BASE=100000;
inline char getc(){
    static char buf[1<<14],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<14,stdin),p1==p2)?EOF:*p1++;
}
struct Bint{
    ll a[N+2];
    Bint(){ memset(a,0,sizeof(a)); }
    int getSiz() const{
        Rep(i,N,0) if(a[i]) return i;
        return -1;
    }
    void maintain(){
        rep(i,0,N){
            if(a[i]<0) a[i+1]-=(-a[i]/BASE+1),a[i]+=(-a[i]/BASE+1)*BASE;
            else if(a[i]>=BASE) a[i+1]+=a[i]/BASE,a[i]%=BASE;
        }
    }
    void test()const{
        rep(i,0,15) printf("%lld ",a[i]);
        putchar('\n');
    }
    bool operator<=(const Bint&B)const{
        Rep(i,N,0) if(a[i]!=B.a[i]) return a[i]<B.a[i];
        return 1;
    }
    bool operator<(const Bint&B)const{
        Rep(i,N,0) if(a[i]!=B.a[i]) return a[i]<B.a[i];
        return 0;
    }
    Bint operator<<(const int&x)const{
        Bint C;
        rep(i,0,N-x) C.a[i+x]=a[i];
        return C;
    }
    Bint operator*(const int&B)const{
        Bint C;
        rep(i,0,N) C.a[i]=a[i]*B;
        C.maintain();
        return C;
    }
    Bint operator/(const Bint&B)const{
        Bint C=(*this),temp,ans,QQ; REG int t1,t2=B.getSiz();
        while(B<=C){
            t1=C.getSiz(); temp=B<<(t1-t2);
            REG ll div=C.a[t1]/temp.a[t1];
            //printf("p:%d\n",div);
            while(1){
                QQ=temp*div;
                int st=QQ.getSiz();
                if(st==t1+1) QQ.a[t1]+=QQ.a[st]*BASE,QQ.a[st]=0;
                if(!(C<=QQ)) break;
                div--;
            }
            //printf("%d\n",div);
            rep(i,0,N) C.a[i]-=temp.a[i]*div;
            C.maintain();
            C.a[t1-1]+=C.a[t1]*BASE; C.a[t1]=0;
            //C.test(); putchar('\n');
            ans.a[t1-t2]=div;
        }
        return ans;
    }
    ll getll()const{
        ll x=0;
        Rep(i,N,0) x=x*BASE+a[i];
        return x;
    }
};
inline Bint scan(){
    REG int c=0;
    Bint T; static char ch=0,s[N];
    memset(s,0,sizeof(s));
    while(ch<48) ch=getc();
    while(ch>=48) s[++c]=ch-48,ch=getc();
    #define BAS 5
    REG int x,pos=c,cnt=0;
    while(pos-BAS+1>0){
        x=0;
        rep(i,pos-BAS+1,pos) x=x*10+s[i];
        T.a[cnt++]=x; pos-=BAS;
    }
    if(pos>0){
        x=0;
        rep(i,1,pos) x=x*10+s[i];
        T.a[cnt++]=x;
    }
    return T;
}
inline void print(const Bint&x){
    REG bool f=0;
    Rep(i,N,0){
        //别忘了 !f 因为咱压了位
        if(!f&&x.a[i]){
            f=1; printf("%lld",x.a[i]);
        }else if(f) printf("%.5lld",x.a[i]);
    }
    if(!f) putchar('0');
}
int main(){
    //freopen("1.in","r",stdin);
    #define pc putchar('\n')
    Bint A=scan(),B=scan(),C;
    //A.test(); pc;
    //B.test(); pc;
    C=A/B;
    print(C); pc;
    //C.test(); pc;
    //原先t1 t2写反了 出现了奇怪的问题
    //print(A); pc;
    //printf("%lld\n",A.getll()/B.getll());
    return 0;
}
//通过观察发现 答案基本是对的,就是丢了几个0
//然后查出是输出的问题

END

posted @ 2020-10-25 00:40  喵乖乖喵  阅读(483)  评论(0编辑  收藏  举报

膜拜众神

网安院技术部     ZZY大师     Xinyang 大佬     Wjyyy