P1009 【深基4,例7】阶乘之和

#include <iostream>
using namespace std;
int main(){
	int n;
	cin>>n;
	long long ans=0;
	for(int i=1;i<=n;i++){
		long long factor=1;
		for(int j=1;j<=i;j++)
		   factor=factor*j;
		ans=ans+factor;
	}
	cout<<ans<<endl;
}

这段代码的目的是计算从 1 到 n(包含 n)所有整数的阶乘之和。但是,随着 n 的增大,阶乘的值会迅速增长,很快就会超出 long long 类型的存储范围,导致溢出和错误的结果。比如:

当n的值超过21,就会出现溢出。

为了优化这个问题,我们可以考虑使用高精度计算库(如 GMP 或 C++17 引入的 <bit> 和 <numeric> 中的工具,尽管后者主要用于位操作和数值算法,并不直接支持大数运算),但在这里,我将展示一个更简单的优化方法:使用模运算来避免溢出(尽管这不会给出精确的阶乘和,但可以用于处理大数问题时的近似或特定场景下的计算)。

然而,对于直接计算阶乘和的问题,更实际的做法是使用一种算法来避免直接计算大数的阶乘,因为即使使用模运算,当 n 很大时,计算单个阶乘也会非常耗时。

不过,为了保持问题的简单性,并展示如何避免在 long long 范围内溢出,我们可以简单地限制 n 的大小,或者使用模运算来模拟一个“有限范围”的计算。但请注意,这不会给出原始问题的精确答案。

下面是一个使用模运算来避免溢出的示例代码(但请注意,这不会给出真实的阶乘和,只是演示如何避免溢出):

#include <iostream>
using namespace std;
const int MOD = 1e9 + 7; // 选择一个合适的模数来避免溢出  
int main(){
	int n;
	cin>>n;
	long long ans=0;
	for(int i=1;i<=n;i++){
		long long factor=1;
		for(int j=1;j<=i;j++)
		   factor=factor*j%MOD;
		ans=(ans+factor)%MOD;
	}
	cout<<ans<<endl;
}

当n取21,50,结果没有溢出,如下:

选择 MOD=10 ^9 +7(即 1000000007)作为模数,在编程和算法竞赛中是一个常见的做法,其依据和优点主要包括以下几点:

  • 质数性质:10 ^9 +7 是一个质数。在模运算中,使用质数作为模数可以避免一些不必要的数学性质导致的错误。例如,如果模数不是质数,那么它可能有非平凡的因子,这可能会在某些算法中引入不必要的复杂性或错误。
  • 足够大:10 ^9 +7 是一个相对较大的质数,这意味着在大多数情况下,它可以避免在模运算过程中发生溢出之前,计算结果就已经达到了这个模数的上限。这对于处理大数问题特别有用,因为它允许我们在不牺牲太多精度的情况下,使用标准的整数类型(如 long long)进行计算。
  • 广泛应用:由于 10 ^9 +7 的这些优点,它已经被广泛应用于各种编程竞赛和算法实现中。因此,许多算法和数据结构库都内置了对这个模数的支持,或者至少提供了容易修改以使用这个模数的接口。
  • 避免负数:在某些情况下,模运算的结果可能是负数(特别是当被除数小于模数但为负数时)。然而,由于 10 ^9 +7 是一个大于所有标准整数类型能表示的最大负数的质数,因此在使用这个模数进行模运算时,我们几乎总是可以得到一个非负的结果(除非被除数是负数且绝对值非常大,但在实际应用中这种情况很少见)。
  • 习惯和传统:最后,选择 10 ^9 +7 作为模数也部分地基于习惯和传统。由于它在许多算法和竞赛中都被广泛使用,因此许多程序员和算法设计者都熟悉它,并倾向于在需要模运算时使用它。

但是,请注意,这种方法仍然非常低效,因为它对于每个 i 都重新计算了阶乘。在实际应用中,我们可以使用动态规划或其他数学技巧来更有效地计算阶乘和,特别是当 n 很大时。

#include <iostream>
using namespace std;
const int MOD = 1e9 + 7; // 选择一个合适的模数来避免溢出  
int main(){
	int n;
	cin>>n;
	long long ans=0,temp=1;
	for(int i=1;i<=n;i++){
		temp=temp*i%MOD;
		ans=(ans+temp)%MOD;
	}
	cout<<ans;
}

对于非常大的 n,直接计算阶乘和是不切实际的,你可能需要考虑使用近似方法或查找表等策略。如果问题允许,也可以考虑使用高精度计算库来处理大数。

  • 28
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hl026

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值