洛谷 P1480 A/B Problem 高精度除法 详细题解 原理解释+代码注释

完整代码请扒拉到最底下↓。
解题要求:掌握基础的判断、循环、数组、字符串,以及一些计算原理即可。

“高精度”是怎么一回事?

简单来说,就是我们计算的数字太大太大了,导致已经超出了我们可以定义的范围,以至于我们不能用正常的定义来定义和计算数字,不然只能计算我们能所容纳的数字部分,无法达
到要求。

比如longlong可以定义的范围在 -(2的63次方)到 2的63次方-1

如果此时数据过大,超过了longlong可以定义的范围,那么我们该如何计算它呢?这就需要我们运用到高精度的知识了。

题外话:Python没有这个限制,所以一行代码就解决了。

高精度除法计算的原理

1.存储数据

你看既然我们数据太大,普通的定义都装不下了,那我们拿什么来装数据呢?没错,就是

数组

有同学可能会问:我们又不知道这个数有多长,我现在又不会用vector,(其实我现在也还是还是不会),那我们定义数组的时候应该定义多长的数组呢?

根据题意0<a<10的5000次幂,则将被除数和商的数组长度设置为5000(基础的数学运算,可以自己推算一下),而除数0<b<10的9次幂,这里可以用int来实现了。

int dividend[5000] = { 0 };//被除数
int quotient[5000] = {0};//商
 //由于int无法容纳足够多的数字,只能用string来存储
string a;
int divisor;//题目中除数范围在int内
cin >> a >> divisor;

2.输入数据

很简单,先获取字符串的长度,然后利用for循环将数字一个一个塞入数组,不过注意由于我们定义的是string,所以存入数组的还是string类型,这里我们就需要进行一个数据类型的转换,把它转换成int,这样才能计算。

 //获取字符串的长度:
    int al = a.length();
   

    for (int i = 0; i < al; i++)
    {
        //字符串a是可以变成a[i]的,每一个元素就是对应的字符
        //-'0'可以将数字字符转换为数字
        dividend[i] = a[i]-'0';
    }

3.计算原理(核心)

接下来就是应该如何计算了,这里我们就需要模拟没有计算器的情况下,我们人是如何手算的,于是就需要用到我们在小学学习的竖式运算。

   02
 --------
 5|10
   0
  -----
   10
   10
 -------
    0

1.先用被除数的一位去和除数相除

2.如果这一位比除数小,那么就需要用它减去商和除数的乘积(即0),然后后移补位,再次相除

3.如果这一位比除数大,那么先相除得出商,然后被除数减去商和除数的乘积,然后后移补位,再次计算

4.最终输出商

这里你可以发现两种情况中其实商的计算过程是一模一样的,所以我们可以采用同一个公式来计算商。

然后就是补位怎么补,其实就是最基本的数学运算,前一个数乘10+后一个数乘1,就得到一个二位数了,那如果减下来的数本来就是二位数呢?其实也是一个道理,不信你试试,因为我们补位永远都是只补一位,所以前边的数无论是多大只要乘10,就能为补位腾出一位的空间。

最后我们需要一个临时容器来存储一下中间相减的数,不过注意由于补位需要乘10,所以int无法容纳,需要long long来定义。

代码如下:

 long long remainder = 0;//记录中间数
    for (int i = 0;i < al;i++)//被除数开始逐个计算
    {   
        quotient[i] = (remainder * 10 + dividend[i]) / divisor;//计算商的每一位
        remainder = remainder * 10 + dividend[i] - (quotient[i] * divisor);//计算相减的中间数
     }

4.输出数据

我们手动计算的时候是把商里的0自动除掉的,但是电脑计算的时候会保留,所以我们需要一个判断机制,在找到第一个非零数之前,把遇到的0都跳过,这样就能保证输出的时候数字前边不会有0

 int zeroflag = 0;//设置一个发现非零数的标记,默认为0
    for (int i = 0; i < al ; i++)
    {
        //若前边几位都是0,且不是最后一位,且标记还是为0,则跳过本次循环
        if (quotient[i] == 0 && zeroflag == 0 && i < al-1)
        {
            continue;
        }
        zeroflag = 1;//找到第一个非零数之后,就将标记设位1,这样就不会影响到后续的0
        cout << quotient[i];
    }

最后就是完整代码了

#include <iostream>
using namespace std;
int dividend[5000] = { 0 };//被除数
int quotient[5000] = {0};//商

int main() {
    //由于int无法容纳足够多的数字,只能用string来存储
    string a;
    int divisor;//题目中除数范围在int内
    cin >> a >> divisor;

    //获取字符串的长度:
    int al = a.length();
   

    for (int i = 0; i < al; i++)
    {
        //字符串a是可以变成a[i]的,每一个元素就是对应的字符
        //-'0'可以将数字字符转换为数字
        dividend[i] = a[i]-'0';
    }
  
    
    long long remainder = 0;//记录中间数
    for (int i = 0;i < al;i++)//被除数开始逐个计算
    {   
        quotient[i] = (remainder * 10 + dividend[i]) / divisor;//计算商的每一位
        remainder = remainder * 10 + dividend[i] - (quotient[i] * divisor);//计算相减的中间数
     }
  

    
    //不判断到最后一位的原因是,如果计算结果为0,能保证输出计算结果

   
    int zeroflag = 0;//设置一个发现非零数的标记,默认为0
    for (int i = 0; i < al ; i++)
    {
        //若前边几位都是0,且不是最后一位,且标记还是为0,则跳过本次循环
        if (quotient[i] == 0 && zeroflag == 0 && i < al-1)
        {
            continue;
        }
        zeroflag = 1;//找到第一个非零数之后,就将标记设位1,这样就不会影响到后续的0
        cout << quotient[i];
    }
    return 0;
}

最后的结语

其实这个并不是真正的高精度除法,因为没有考虑小数和负数,有兴趣的小伙伴可以自己下去尝试一下。

另外这也是高精度加减乘除最后一篇文章了,想了解高精度其他运算方法的可以去看我的博客,里边都写的有哦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值