头歌(EduCoder)算法设计与分析:大整数的加减乘除运算

任务描述

本关任务:掌握大整数的基本思想,并运用大整数的基本运算计算出常规整数n的阶乘,然后统计大整数n!中数字0的个数。

相关知识

为了完成本关任务,你需要掌握:1.大整数的思想,2.大整数加法,3.大整数减法,4.大整数与整数的乘法,5.大整数乘法,6.大整数与整数的除法,7.n的阶乘求解思路。
大整数的思想

大整数的思想:用数组存储大整数(超长整数),为处理简单起见约定每个数组元素存放相同位数(T位)的数字片段(假定T=4位)。

设定一个大小为N的整型数组a[0,1,…,N−1],给定一个大整数998877665544332211,每个数组单元使用T=4位存储,为了方便计算,将大整数的低位存入数组的高维索引,如下图所示:
在这里插入图片描述
在代码文件里,char_to_int函数已经实现了大整数的字符串序列s到整型数组c的转化,c初始化全为0,然后从下标(N−1)开始从右至左存储大整数,每个数组单元存储T=4位数字,相对应在进制K=10T=10000的结果。

另外,output函数已经实现了整数数组的输出,基本思想是从左到右,遇到第一个不为0的数组单元为大整数的头,之后每个数组单元都输出T=4位数字(注意补0)。

大整数加法

大整数的加法和一般的整数加法是类似的,从低位开始,同位置的数相加,若大于进制K=10000,则进位,以此类推。特别的,当进制K=10时,每个数组单元存储的数字为T=1位,大整数的加法运算就等价于常规整数的加法运算,只不过是用数组来模拟计算过程。

例如两个大整数分别为998877665544332211和112233445566778899,按照T=4,K=10000的参数设定,分别存储在整型数组a和b中,数组大小都是N,它们的加法运算(c=a+b)过程如图所示:
在这里插入图片描述

大整数减法

同样的,大整数的减法过程与一般的整数减法也是类似的,从低位开始,同位置的数相减,被减数小于减数时,被减数向高位借1,当T=4,K=10000时,借一位相当于加10000。特别的,倘若被减大整数小于减数大整数,则先交换它们,然后再做减法,最后为结果添加负号,为了方便起见,本关卡的测试数据保证被减数大于减数。

对于上面的大整数a=998877665544332211和大整数b=112233445566778899,它们的减法运算(c=a−b)过程如下图所示:

在这里插入图片描述

大整数与整数的乘法

我们知道整数之间的乘法是逐位相乘,然后相加。对于大整数与整数之间的乘法,则是把“位”扩展成了“块”,即大整数的每个数组单元分别与整数相乘,然后加上进位(初始为0),并把结果放在对应位置上,若超过了进制K,则进位,以此类推。

例如大整数123456789和整数12345,按照T=4,K=10000的参数设定,大整数存储在整型数组d中,数组大小为N,整数存储在整型变量p中,它们的乘法运算(c=d×p)过程如图所示:
在这里插入图片描述

大整数乘法

我们已经知道了大整数与整数之间的乘法运算,对于大整数a与大整数b的乘法运算则是大整数与整数乘法运算的拓展,也就是说,将大整数a分别与大整数b的每个数组单元相乘,然后放置在相应位置上,并累加进位。

对于上面的大整数a=998877665544332211和大整数b=112233445566778899,它们的乘法运算(c=a×b)过程如下图所示:
在这里插入图片描述

大整数乘法大整数与整数的除法

大整数d与整数p的除法运算保留商和余数,其中商仍然可能是大整数,而余数则是比除数要小的整数。大整数作为被除数,从高位开始,依次(从左到右i=0→N−1)将每个数组单元d[i]除以除数p,当前整除结果作为商存储在数组c对应的高位c[i]中,余数保留到下一个数组单元的计算中,依次类推,最后的余数则是大整数被除尽后的剩余项。

对于上面的大整数d=123456789和整数p=12345,它们的除法运算(c=d/p,r=d过程如下图所示:
在这里插入图片描述

n的阶乘求解思路

n的阶乘是一个非常大的整数,首先需要运用大整数的基本运算法则求得n!的数值。这一步可以借助大整数与整数的乘法运算,然后循环n−1次乘法即可得到n!。

n!=1×2×3×…×n

其次是统计n!中数字0的个数,因为大整数在数组中是分块存储的,所以有两种统计方式(记n!=M):

借助大整数与整数的除法运算:让M除以10,判断余数r是否为0,若为r=0,则答案累加1,然后将M赋值为商c,即M=c,重复以上步骤,直到M=0,程序结束;

借助大整数在数组中的分块存储方式:循环枚举大整数n!的数组单元,计算每个单元里的整数包含数字0的个数,最后进行累加求和即为答案。注意,每个整数单元里的数值不一定为T=4位,比如M[i]=102,当i不是大整数的头时,M[i]的真实数据为0102,包含两个数字0(一个简单的处理技巧:将M[i]加上K=10000,即M[i]+K=10102,然后判断它所包含的数字0的个数)。

编程要求

本关的编程任务是补全右侧代码片段calc中Begin至End中间的代码,具体要求如下:

在calc中,根据大整数的基本运算原理,计算整数n的阶乘,并统计出n!中数字0的个数,然后将统计结果作为函数返回值。

测试说明

台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确,测试数据保证10001>n>0。

以下是平台的测试样例:

测试输入:
10
预期输出:
2

输入格式:
第1行:整数n
输出格式:
第1行:n!中数字0的个数

Tips:注意整数乘法越界的情况

代码

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>

using namespace std;

// 最大长度N,分块长度K
const int N = 10001;
const int K = 10000;
const int T = (int)log10(K); // K=10000, T=4

void char_to_int(char *s, int *c)
{
   
    
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中可以使用string来存储超长整数,并且可以通过自定义函数实现运算。下面是一个简单的示例代码: ```c++ #include <iostream> #include <string> using namespace std; string add(string num1, string num2) { string result = ""; // 保存结果 int carry = 0; // 进位标志 int i = num1.size() - 1; // num1的最高位 int j = num2.size() - 1; // num2的最高位 while (i >= 0 || j >= 0 || carry > 0) { int n1 = i >= 0 ? num1[i--] - '0' : 0; int n2 = j >= 0 ? num2[j--] - '0' : 0; int sum = n1 + n2 + carry; carry = sum / 10; result = to_string(sum % 10) + result; } return result; } string subtract(string num1, string num2) { string result = ""; // 保存结果 int borrow = 0; // 借位标志 int i = num1.size() - 1; // num1的最高位 int j = num2.size() - 1; // num2的最高位 while (i >= 0 || j >= 0) { int n1 = i >= 0 ? num1[i--] - '0' : 0; int n2 = j >= 0 ? num2[j--] - '0' : 0; int diff = n1 - n2 - borrow; if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } result = to_string(diff) + result; } // 去掉前导0 while (result.size() > 1 && result[0] == '0') { result.erase(0, 1); } return result; } int main() { string num1 = "123456789012345678901234567890"; string num2 = "987654321098765432109876543210"; cout << add(num1, num2) << endl; cout << subtract(num1, num2) << endl; return 0; } ``` 上述代码实现了超长整数运算,其中使用了string类型存储超长整数,利用循环逐位相/相并处理进/借位。需要注意的是,法需要判断被数是否小于数,若小于则需要向高位借位。对于法的结果,需要去掉前导0。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值