高精度算法

188 阅读2分钟

是什么?

由于int和long long能够存储的数字范围太小,当我们想要实现至少几千位的数字相乘时,就不能直接设两个变量相乘,而是需要用到高精度算法

高精度算法是通过将数字的每一位数都分开存进一个数组,把一个几千位的数字用数组表示出来。例如,数字2022就可以通过一个数组a = {2, 2, 0, 2}来存(通常倒着存数组,方便模拟)。接着,通过模拟加减乘除的运算过程,如进位,借位等就可以进行高精度计算。

代码实现及原理

加法

因为是倒着存数字,即个位存在a[1],十位存在a[2]等等,所以就可以从数组开头往后模拟。将对应位数字相加,再加上上一位数字的进位,再检查是否需要进位,最后得出在这一位上的结果,全程跟人计算的过程相似

int a[1001], b[1001], ans[1010]//两个相加的数字和它们的和
int l;//记录位数更多的数字的位数
void add(){
    int x = 0;//记录进位
    for(int i = 1;i <= l;i++){
        ans[i] = a[i] + b[i] + x;//数位相加
        x = ans[i]/10;//计算进位
        ans[i] %= 10;//减掉进位
        if(x > 0 && i>= l){//如果需要进位到最高位的下一位,就增加位数
            l++;
            ans[i+1] = x;
        }
    }
}

减法

跟加法相似,唯一的不同就是将进位,替换成借位:

//a[i]被减数,b[i]减数,被减速一定要比减数大!,l代表被减数的长度
void mi(){
    for(int i = 1;i <= l;i++){
        if(a[i] >= b[i]){
            ans[i] = a[i] - b[i];//不需要借位
        }
        else{
            a[i+1]-=1;
            ans[i] = a[i] - b[i] + 10;//需要借位
        }
    }
}

乘法

模拟人做乘法,乘数每一位与另外一个乘数的每一位都要相乘,所以需要用到嵌套for循环,并且和加法一样也要考虑到进位。

//a[] b[]是两个乘数,l1和l2是分别两个乘数的位数
void mul(){
    for(int i = 1;i<=l1;i++){
        x = 0;
        for(int j = 1;j <= l2;j++){
            ans[i+j-1] += a[j]*b[i] + x;//每个位数都相乘
            x = ans[j+i-1] / 10;//计算进位
            ans[j+i-1] = ans[j+i-1] % 10;
            if( x>0 && j == l2 ) ans[j+i] = x;//如果后面没有数了但需要进位,就直接等于x
        }
    }
}

除法

通过使被除数的每一位都减去除数,直到不够减了为止,算出商(被除数一定要比除数打,且能被整除):

int a[], b[], c[], ans[], la, lb, lc;//数组及其位数
int compare(int a[], int b[]){//比较a,b数组的大小
    if(la > lb) return 1;//先比较各自的位数
    if(la < lb) return -1;
    for(int i = la;i>0;i--){//若位数相同,就从最高位逐位比较
        if(a[i] > b[i]) return 1;
        if(a[i] < b[i]) return -1;
    }
    
    return 0;//两个数相同
}

int lp, lq;
void numcpy(int p[], int q[], int det){//复制p数组到q数组从det开始的地方
    for(int i = 1;i<=p[0];i++{
        q[i+det-1] = p[i];
    }
    lq = lp + det - 1;//位数也要更新
}

void jian(int a[], int b[]){//a = a-b
    int flag;
    flag = compare(a, b);
    if(flag == 0){
        la = 0;
        return;
    }
    
    if(flag == 1){
        for(int i = 1;i<=la;i++){
            if(a[i] < b[i]){
                a[i+1]--;
                a[i]+=10;
             }
             a[i] = a[i] - b[i];
         }
     }
     while(la>0 && a[la] == 0) la--;
     return;
}

int tmp[];
void divide(int a[], int b[], int c[]){//除法
    lc = la - lb + 1;//暂时计算位数
    for(int i = lc;i>0;i--){//通过减法来模拟除法
        numcpy(b, tmp, i);
        while(compare(a, tmp)>+0){
            c[i]++;
            jian(a,tmp);
        }
     }
     while(lc>0 && c[lc] == 0) lc--;
     return;
 }