高精度算法是如何实现的(高精除低精)

引言

昨天我们学习了高精度的加法、减法和乘法,今天咱们来学习有些许不一样的除法是如何实现的。虽然它的实现方法依旧是模拟,但是在存入大数的时候和计算的核心代码是不同的

题目描述

解析

这道题咱们能够看到要进行的是一个除法,并且输入的被除数是一个高精度数,除数是一个非高精度数,故此我们要进行的就是高精度除以低精度的操作。

毋庸置疑的是,我们还是要模拟小学的竖式计算——竖式除法👇

大家能够发现,小学竖式除法的时候究其本质是在逐位试商,就是从被除数的最高位开始,用这位去除以除数整体,得到商放在这个最高位的正上方,然后把余数放在最高位的正下方,第二步是将被除数的第二位放在刚刚得到的余数后面组成一个新数,再由这个新数除以输出……以此类推逐位循环就可以了。

那我们再来看看程序又是如何实现的👇

 for(i=0;i<len_ans;i++){
        ans[i]=(x*10+a2[i])/b;        //要把之前的余数乘10再加进来
        x=(x*10+a2[i])%b;
    }

既然进行的是逐位试商,那就是要从最高位开始尝试,那我们大数就没有必要倒序存储了,直接正序存进来就方便我们的计算。而这个最后得到的余数3就是我们整个式子的余数,而上面的ans数组就是最后的商,当然了,大家看过高精度的加法、减法和乘法以后,就都知道了前导零的存在,所以我们依旧有一步是要去除前导零

去除前导零

除法和我们的减法类似,都会出现有多个前导零的情况,故此,我们依旧要使用while循环进行多次的判断,直到第一次读到非零的数字才开始输出,当然了,有的人可能会问,按是不是要加上输出的地方的数组下标要小于len_ans-1的条件了,那是当然,因为会有0除以某个非零数的情况!

C++代码

#include <bits/stdc++.h>
using namespace std ;
int main()
{
    char a1[5010]={0};
    int a2[5010]={0};
    int ans[5010]={0};
    int i,b,len_a,len_ans,l=0;
    long long x=0;
    cin >> a1 >> b ;
    len_a=strlen(a1);
    for(i=0;i<=len_a-1;i++){
        a2[i]=a1[i]-'0';
    }
    len_ans=len_a;
    for(i=0;i<len_ans;i++){
        ans[i]=(x*10+a2[i])/b;
        x=(x*10+a2[i])%b;
    }
    while(ans[l]==0&&l<len_ans-1)
    l++;
    for(i=l;i<len_ans;i++)
    cout << ans[i] ;
    return 0;
}

细心的朋友可能会发现有两个很奇怪的点:

第一个是len_ans=len_a,那么这个答案数组的长度应该开多大呢,我们回到之前的逐位试商法里面,它每尝试一次就会在ans数组里面产生一个商,故此答案数组的长度是不会超过被除数的长度的,所以我们把循环条件里面的len_ans设置的和len_a一样大就可以了

第二个是对于余数x的变量定义,细心的大家会发现我专门把这个数拿出来用了long long而非int,这是为什么呢?原因其实很简单,我们在进行逐位运算的时候对这个x的操作是x=(x*10+a2[i])%b,那么x*10是可能大于10^9的数量级的,就比如洛谷的最后一个测试点:9*10^9除以1*10^9,这时候我们的x经过10次的循环就已经变成了9*10^9,那它再乘以10就会导致爆栈,这样的话我们的程序就无法正常运行了。

以下就是我调试才发现的这个错误,希望大家引以为戒,要考虑程序运行中所产生的数是否会超出变量范围!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值