阶乘的强悍溢出技能

news2024/9/25 7:22:32

【题目描述】

输入n,计算S=1!+2!+3!+…+n!的末6位(不含前导0)。,n!表示

前n个正整数之积。

【样例输入】

10

【样例输出】

37913

一、阶乘溢出性能体验

按正常逻辑,本题可以分三步:

①求1-n的阶乘。

②把每项阶乘加起来。像这样一项一项地把各个式子加到一起,就是累加器。

③取和的末6位。

但是别忘了还有一个条件:

一看到这么大的数,又是阶乘,免不了心里一沉,直觉冥冥中指引恐怕long long型也罩不住。

先拿求n的阶乘探探底。

#include<stdio.h>
int main(){
    for(int n=1; n<=1e6; n++){
        long long factorial = 1;
        for(int i=1; i<=n; i++){
            factorial *= i;
        }
        printf("%d %lld\n", n, factorial);
    }
    return 0;
}

结果输出一堆0,肯定是鸟鸣山一样,完鸟。

把1e6改成100,仍然是一堆0,看来早就超限了。

没错,21!的已经输出负数,阶乘的溢出能力实在是太强悍了。

也就是说,用long long型只能计算到20!。如果用int型呢,只能计算到12!。

这可咋办?

二、大数取余神药

据说有一味神药:要计算只包含加法、减法和乘法的整数表达式除以正整数n的余数,可以在每步计算之后对n取余,结果不变。

也就是说:

t=(a*b+c-d)%n中t的值可以分步取余求出:

t1=a*b/%n;
t2=(t1+c)%n;
t=(t2-c)%n;

是何道理呢?其实很简单,因为任何整数除以n的余数都是这个数减去n的倍数后余下的数。

如果x%n=y,它的意思是x=kn+y(x、y、k为整数,y<x)。

显然,所有含n的项都能被n整除,不会产生余数,所以可将其去除,变为:

它们分别是a、b、c、d除以n的余数,所以有:

三、代码实现

在神药的帮助下,本题可改为如下两步:

①求1-n的阶乘。用for循环实现累乘,每执行一次乘法都对1e6取余。

②把每项阶乘加起来。用for循环实现累加,每加一次都对1e6取余。

代码如下:

#include<stdio.h>
int main(){
    const int MOD = 1e6;
    int n, S = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        int factorial = 1;
        for(int j = 1; j <= i; j++)
            factorial = factorial * j % MOD;
        S = (S + factorial) % MOD;
    }
    printf("%d\n", S);
    return 0;
}

将1e6定义为MOD常量可以改善程序的可读性,也便于修改(代码中有两次用到这个数,使用MOD表示的情况下,如需修改只需改一次MOD的值)。

另一点需要注意的是,两行取余代码千万不要写成下面这种牛皮plus形式:

factorial *= j % MOD;
S += factorial % MOD;

因为*=、+=这种赋值运算符和=一样,优先级是非常低的,而%的优先极和*、/是一样的,所以会先计算取余,再进行乘赋值、加赋值。

比如下面的代码:

#include<stdio.h>
int main(){
    int i=10;
    i *=50+2;
    printf("%d\n", i);
    return 0;
}

它的结果是520,而不502。

三、代码优化

前面给出的求阶乘之和的代码可以进一步优化,将两层循环减少为一层循环。

如果读到这里,请别急着往下读,先自己思考一下方法。

原理很简单:n!可表示成前n个正整数之积,也可以表示成(n-1)!*n,所以只要保留上一次循环求得的阶乘,就可以通过累乘的方式求得n的阶乘。这时候就要把factorial这个变量放到循环体之外定义。

代码如下:

#include<stdio.h>
int main(){
    const int MOD = 1e6;
    int n, factorial=1, S = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        factorial = factorial * i % MOD;
        S = (S + factorial) % MOD;
    }
    printf("%d\n", S);
    return 0;
}

四、耗时测试

阶乘,最大的阶乘,阶乘之和。这样的豪华的阵容吞噬时间的能力必然很是强悍吧!

如果想了解代码的运行时间,可以用计时函数clock()。它定义在< time.h>头文件中,该函数返回从程序启动到调用 clock() 时的 CPU 时间。从这个描述咱们要明白:

①统计时间从启动程序开始。这意味着,输入数据的时间也是计算在内的。

②返回时间与clock()代码放置位置有关。

③返回的不是秒数。

针对上述特点,如果咱们想获取程序的纯运行时间(不计用户输入时间),可以采用如下对策:

(1) 借助命令行工具刨除数据输入时间。

①同时按下Win+R键,快速打开运行窗口。

②在运行窗口中输入“cmd”并按回车,打开命令行窗口。

③直接在光标处输入编译后的可执行文件所在盘符,如D:。

④输入cd folder1\folder2(表示D:\folder1\folder2,指程序执行文件所在路径,可以通过复制粘贴的方式输入)

⑤确认进入程序所在路径,然后输入:echo 52|u。操作系统会自动把52输入,其中u是程序名。如果要输入多条数据,可以写成echo 52 92|u。

(2) 计时函数clock( )放在程序结束之前。这样就能返回整个程序的执行时间。

(3) 返回秒数:用clock( )/CLOCKS_PER_SEC可以得到秒数。可以想象有个法号叫CPU的和尚在敲钟,clock( )能告诉你它敲了多少下,CLOCKS_PER_SEC指的是敲钟的速度(每秒敲多少下),二者相除就得到敲了多少秒。

代码如下:

#include<stdio.h>
#include<time.h>
int main(){
    const int MOD = 1e6;
    int n, S = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        int factorial = 1;
        for(int j = 1; j <= i; j++)
        factorial = (factorial * j % MOD);
        S = (S + factorial) % MOD;
    }
    printf("%d\n", S);
    printf("Time used = %.2f\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

将优化前后的代码分别测试结果如下:

n

18

19

20

21

22

23

24

25

26

答案

348313

180313

820313

260313

940313

580313

940313

940313

940313

n

1600

3200

6400

12800

25600

51200

102400

204800

409600

答案

940313

940313

940313

940313

940313

940313

940313

940313

940313

优化前

时间

0.03

0.05

0.12

0.38

1.44

5.74

23.28

93.43

381.62

优化后

时间

0.03

0.03

0.03

0.03

0.03

0.03

0.03

0.03

0.03

从表中数据可得出:

(1) 优化后的一层循环都是瞬间运行完毕,而优化前的两层循环随着n的变大,运行时间越来越长,呈指数上升。

(2) 程序的运行时间大致和n的平方成正比(因为n每翻一番,运行时间近似翻两番)。据此可估计n=1e6时,程序大致需要37分钟才能执行完。

这涉及到一个估算时间复杂度的技巧:很多程序的运行时间与规模n存在着近似的简单关系,这种关系可以通过计时函数来估测。比如上表通过对比每次将n翻倍的运行时间来验证这一关系。

(3) 从24开始,答案始终不变。这是因为25!末尾有6个0,所以从第5项开始,后面的所有项都不会影响和的末6位数字。所以,对于优化前的代码,只需要在程序的最前面加一条语句“if(n>25)n=25;”,你就不用等几十分钟才能看到n=1e6时的结果了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1533927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探讨Java代码混淆加固工具

摘要 本篇博客将介绍几种常用的Java代码混淆工具&#xff0c;如ProGuard、Allatori Java Obfuscator、VirboxProtector、ipaguard和DashO。我们将深入探讨它们的特点、功能以及在保护Java应用程序安全方面的作用。此外&#xff0c;还将强调在使用Java代码混淆工具时需要注意的…

DUSt3R:简化三维重建

3D 重建是从二维 (2D) 图像创建对象或场景的 3D 虚拟表示的任务&#xff0c;可用于模拟、可视化或本地化等多种目的。 它广泛应用于计算机视觉、机器人和虚拟现实&#xff08;VR&#xff09;等多个领域。 在基本设置中&#xff0c;3D 重建方法输入一对图像 I1 和 I2&#xff0c…

安装nginx和PHP

首先规划四台虚拟机&#xff0c;之前的主从数据库已经两台&#xff0c;其余两台&#xff0c;一个设置nginx&#xff0c;一个是php 首先NGINX的概念&#xff0c;请参考https://blog.csdn.net/hyfsbxg/article/details/122322125。正向代理&#xff0c;反向代理&#xff0c;可以…

(C语言) print输出函数系列介绍

(C语言) print输出函数系列介绍 文章目录 (C语言) print输出函数系列介绍前言输出系列函数&#x1f5a8;️printf&#x1f5a8;️sprintf & snprintf&#x1f5a8;️fprintf&#x1f5a8;️vprintf&#x1f5a8;️dprintf&#x1f5a8;️puts&#x1f5a8;️fputs&#x1f…

【C语言】基本语法知识C语言函数操作符详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;C语言_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.基本语法 1.1 代码解释 1.1.1 main()主函数 1.1.2 int 1.1.3 { } 1.1.4 printf()库函数 1.1.5 stdio.h头文件 1.2 C语言的…

js 输出负数的个数,和所有正整数的平均值。

首先输入要输入的整数个数n&#xff0c;然后输入n个整数。输出为n个整数中负数的个数&#xff0c;和所有正整数的平均值&#xff0c;结果保留一位小数。 0即不是正整数&#xff0c;也不是负数&#xff0c;不计入计算。如果没有正数&#xff0c;则平均值为0。输入 11 1 2 3 4 5 …

雷池 WAF 社区版:下一代 Web 应用防火墙的革新

黑客的挑战 智能语义分析算法&#xff1a; 黑客们常利用复杂技术进行攻击&#xff0c;但雷池社区版的智能语义分析算法能深入解析攻击本质&#xff0c;即使是最复杂的攻击手法也难以逃脱。 0day攻击防御&#xff1a; 传统防火墙难以防御未知攻击&#xff0c;但雷池社区版能有效…

Delphi7应用教程学习1.3【练习题目】:文本及悬停文字的显示

这个例子主要用到了btn的Hint 属性&#xff0c;Hint是提示的意思。 还有Delphi7还是很好用的&#xff0c;改变了的属性是粗体&#xff0c;默认没有改变的属性为细体。

项目中如何获取Java运行环境中的文件内容

业务场景&#xff1a;获取Java运行环境下的hsminfo.properties文件中&#xff0c;获取key为TESTRSAKEY的值 步骤&#xff1a; 1、获取Java运行环境的根目录&#xff0c;一般是jdk包下的jre文件&#xff1b; 2、通过File.separator拼接运行环境的根路径及目标文件的的名称&am…

SQL查询早于到期时间的数据

遇到个需求是需要查询有效的一些数据&#xff0c;所以要以到期时间作为过滤条件&#xff0c;把到期时间大于到期时间的数据返回&#xff1b; -- 查询早于当前时间 SELECT * FROM jc_tmp t WHERE t.expiration_time > now() 可以直接用 < > 即可 到期时间是now( ) …

实战纪实 | 记一次信息泄露到未授权的挖掘

目标 开局一个登录框 打点 尝试爆破&#xff0c;无望 之后查询了一下供应商的归属&#xff0c;发现是xxxx公司 去了公司官网啾啾&#xff0c;发现了一处wiki&#xff0c;不过现在修了&#xff0c;下面是修了的截图 里面翻到了很多有趣的信息 这时候我们拿到了 a / b (分别代…

漫谈5种注册中心

01 注册中心基本概念 1.1 什么是注册中心&#xff1f; 注册中心主要有三种角色&#xff1a; 服务提供者&#xff08;RPC Server&#xff09;&#xff1a;在启动时&#xff0c;向 Registry 注册自身服务&#xff0c;并向 Registry 定期发送心跳汇报存活状态。 服务消费者&…

ideaSSM博物馆网站系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea 开发 SSM 博物馆网站系统是一套完善的信息管理系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c…

MNN Session 之 Vulkan 算子(八)

系列文章目录 MNN createFromBuffer&#xff08;一&#xff09; MNN createRuntime&#xff08;二&#xff09; MNN createSession 之 Schedule&#xff08;三&#xff09; MNN createSession 之创建流水线后端&#xff08;四&#xff09; MNN Session 之维度计算&#xff08;五…

小米手机蓝牙耳机无声音AACSBC编码故障解决方案

已知环境中蓝牙耳机无声音原因 手机环境&#xff1a;Root Magisk 26.4 最新 27.0 有很多 bug 刷入其他模块会导致永久丢失Root 权限 起初我以为是将手机根目录文件变动导致&#xff0c;最终确定是由于以下模块 magisk-overlayfs3.2-release.zip 作用是解锁 System 分区可读&a…

年度水下蓝牙耳机十大名牌汇集:热销榜TOP游泳耳机揭秘!

近年来&#xff0c;随着运动健身的普及和科技的发展&#xff0c;水下蓝牙耳机逐渐成为游泳爱好者的必备装备。然而市场上各类品牌、型号繁多&#xff0c;如何选择一款适合自己且性价比高的水下蓝牙耳机呢&#xff1f; 为此&#xff0c;我们专门对市面上热销的十大水下蓝牙耳机进…

Halcon 路标牌识别

文章目录 gray_closing_shape 使用选定的掩码执行灰度值关闭create_planar_uncalib_deformable_model 为未校准的透视匹配创建一个可变形的模型get_deformable_model_params 返回可变形模型的参数find_planar_uncalib_deformable_model 在图像中寻找平面投影不变变形模型的最佳…

气压传感器BMP180的简单应用

文章目录 一、BMP1801.介绍2.主要特点&#xff1a;3. 典型应用&#xff1a;4. 原理图5. 典型应用电路6. 测量流程7. 工作模式 二、软件1.初始化2.获取原始温度3.获取真实温度4.获取原始气压5.获取真实气压6.海拔高度的换算 三、总结 一、BMP180 1.介绍 BMP180是一款高精度、小…

【考研数学】全年复习懒人包+资料分享

题主要真是能把这两样做透了&#xff0c;别说90&#xff0c;120都不是问题呀&#xff01;那么我们就先来说说你如何能把这1800做透吧。这可是人称考研数学路上最厚的一本习题册了。经常有人是做到一半就被劝退的&#xff01;假设你是挑题出来做&#xff0c;那也行&#xff0c;不…

ThreaTrace复现记录

1. 环境配置 服务器环境 需要10.2的cuda版本 conda环境 包的版本&#xff1a; python 3.6.13 pytorch 1.9.1 torch-cluster 1.5.9 torch-scatter 2.0.9 torch-sparse 0.6.12 torch-spline-conv 1.2.1 torch-geometric 1.4.3 环境bug 这里环境搭建好以后&#xff0c;就可以正…