利用高精度数组求阶乘之和

题目描述

用高精度计算出 S=1!+2!+3!+⋯+n!(n≤50)。

其中 ! 表示阶乘,定义为 n!=n×(n−1)×(n−2)×⋯×1。例如,5!=5×4×3×2×1=120。

输入格式

一个正整数 n。

输出格式

一个正整数 S,表示计算结果。

输入输出样例

输入 #1

3

输出 #1

9

分析

一看到题目,很多人会觉得这是一道很简单的题目,只需要利用递归的方法写一个阶乘的函数就可以了,就像下面这样:

#include<iostream>
#include<string.h>
#include<cmath>
#include<iomanip>
#include<algorithm>
using namespace std;
int factorial(int t) {
	if (t == 1) {
		return 1;
	}
	else {
		return t * factorial(t - 1);
	}
}
int main() {
	int s = 0;
	int n;
	int i;
	cin >> n;
	for (i = 1; i <= n; i++) {
		s += factorial(i);
	}
	cout << s << endl;
	return 0;
}

然而,我们注意到,题目中n的数值最大可以达到50,而当n达到50时,我们可以通过计算器算出此时n!的值为3.04140932 * 10^64。这时,我们发现,n!的值已经远远超出了long long int能够表示的范围,因此,上面的这段代码在n稍微大一点的时候就会不适用。

既然无法用一个int型来表示所求的值,那么,我们为何不构建一个数组,将所求值的每一位都拆开来,分别用数组中的一个元素来表示呢?

由此,我们很自然地想到了利用高精度数组来代替普通的int型进行计算。关于高精度数组的相关运算,我在CSDN上面查了一下,都有很清楚的教学内容,所以在这里就只大概提一下,不做详述,也不通过代码举例。

首先,我们定义一个数组a[N]来表示一个大数,其中,a[0]表示这个大数的个位,a[1]表示这个大数的十位,······以此类推。注意,由于无法确定大数的位数,为了便于接下来的运算,在表示大数时,数组的顺序与大数的正常顺序是相反的,需要读者格外注意。

其次,由于 s=1!+2!+······+n!,因此,高精度数组的加法是必须的。对于高精度数组的加法,我们可以模拟人工竖式的计算:从低位开始,将两个数组的对应位置相加即可。高精度数组的加法中,最重要的一点就是进位:当某一位的和数大于等于10时,我们需要让下一位+1,而本位仅保留和数的个位部分。

再次,想要求出某个数的阶乘,我们还需要利用到高精度数组的乘法。要计算高精度数组的乘法,我们需要利用到乘法分配律的思想:将大数分解成a[0]+a[1]*10+a[2]*100+······+a[N]*10^N,然后分别与乘数相乘,最后再相加即可。

有了高精度数组的加法和乘法,要想求高精度数组的阶乘之和,只需要综合利用上述二者即可。于是,我们就可以写出代码了:

#include<iostream>
#include<string.h>
#include<cmath>
#include<iomanip>
#include<algorithm>
using namespace std;
int s[100]{};//表示s的高精度数组
int n;
int a[51][100]{};//分别用来表示1~n的阶乘的高精度数组
int i, j;
int flag = 0;
void add(int a[100], int b[100]) {//计算加法的函数,将b加到a上
	int i;
	for (i = 0; i < 100; i++) {
		a[i] += b[i];//对每一位分别进行个位数加法
		if (a[i] >= 10) {//如果得到的值不是个位数,则需要进位
			a[i + 1] += 1;
			a[i] -= 10;
		}
	}
}
void multiply(int a[100], int k) {//计算乘法的函数,其中,a[100]为大数,k为一个很小的数,能够用int来表示
	int i, j;
	int m;
	int temp[100]{};//定义一个temp数组来暂时储存相乘后的结果
	int t;
	for (i = 0; i < 100; i++) {//对大数a,从第一位开始分别与k相乘
		m = a[i] * k;
		for (j = 0; m > 0; j++) {
			temp[i + j] += m % 10;
			if (temp[i + j] >= 10) {
				temp[i + j] -= 10;
				temp[i + j + 1] += 1;
			}
			m = floor(m / 10);
		}//上面的for循环用于进位
	}
	for (i = 0; i < 100; i++) {
		a[i] = temp[i];
	}//最后将temp赋值给a
}
void factorial(int a[100], int t) {//计算阶乘t!的函数,其中a的初始值为1,即仅a[0] = 1,其他均为0
	int i;
	for (i = 1; i <= t; i++) {
		multiply(a, i);
	}//类比int型的阶乘函数
}
int main() {
	cin >> n;
	for (i = 1; i <= n; i++) {
		a[i][0] = 1;
		factorial(a[i], i);
		add(s, a[i]);
	}//计算阶乘之和
	for (i = 99; i >= 0; i--) {
		if (s[i] != 0) {
			flag = 1;
		}
		if (flag == 1) {
			cout << s[i];
		}
	}//在输出时,为了避免输出额外的0,我们可以定义一个flag,当出现第一个非0数时,开始输出
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值