阶乘的强悍溢出技能

【题目描述】

输入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时的结果了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[python]bar_chart_race设置日期格式

1、设置日期标签的时间格式 # 设置日期格式&#xff0c;默认为%Y-%m-%dbcr.bar_chart_race(df, covid19_horiz.gif, period_fmt%b %-d, %Y) 2、更改日期标签为数值 # 设置日期标签为数值bcr.bar_chart_race(df.reset_index(dropTrue), covid19_horiz.gif, interpolate_period…

基于springboot+vue的中山社区医疗综合服务平台

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

第二十八天-ES6标准入门和Flex布局

目录 1.ES6标准入门 2.ES6与JavaScript关系 3.ES6常用新特性 1.变量与常量 1.let三大特性 2.常量三大特征 2.解构赋值 1.数组解构赋值 2.对象解构赋值 3.字符串解构赋值 3.函数与箭头函数 1.函数 2.箭头函数 4.JS的面向对象编程 5.模块化 export使用 import使用…

QT界面制作

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowFlag(Qt::FramelessWindowHint);//接收动图QMovie *mv new QMovie(":/pictrue/th.gif…

二分查找算法(2)

852.山脉数组的峰顶索引 一、题目描述 即下标 i 前的所有元素都升序、后的所有元素都降序&#xff0c; i 是最大值 OJ题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09; 二、思路解析 三、代码 class Solution { public:int peakIndexInMountainArray(vector<in…

算法打卡day11

今日任务&#xff1a; 1&#xff09;239. 滑动窗口最大值 2&#xff09;347.前 K 个高频元素 239. 滑动窗口最大值 题目链接&#xff1a;239. 滑动窗口最大值 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移…

利用Python如何实现数据驱动的接口自动化测试

前言 大家在接口测试的过程中&#xff0c;很多时候会用到对CSV的读取操作&#xff0c;本文主要说明Python3对CSV的写入和读取。下面话不多说了&#xff0c;来一起看看详细的介绍吧。 1、需求 某API&#xff0c;GET方法&#xff0c;token,mobile,email三个参数 token为必填项…

【项目】基于YOLOv8和RotNet实现圆形滑块验证码(拼图)自动识别(通过识别中间圆形的角度实现)

TOC 一、引言 1.1 实现目标 要达到的效果是使用算法预测中间圆形的角度&#xff0c;返回给服务器&#xff0c;实现自动完成验证码的问题。要实现的内容如下图所示。 1.2 实现思路 思路1&#xff08;效果较差&#xff09;&#xff1a;以RotNet要实现的验证码识别为灵感&…

微信小程序(五十九)使用鉴权组件时原页面js自动加载解决方法(24/3/14)

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.使用覆盖函数的方法阻止原页面的自动执行方法 2.使用判断实现只有当未登录时才进行方法覆盖 源码&#xff1a; app.json {"pages": ["pages/index/index","pages/logs/logs"],…

python中如何生成词云

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 今天给大家看看&#xff0c;如何使用python实现根据记录创建生成词云 首先我们看下效果图。 一个是生成了新闻的词云&#xff0c;另一个是生成了聊天记录的词云。下面是代码&#xff1a; …

Java学习笔记(19)

双列集合 键值对 一一对应 键值对对象 entry Map Put第一次给不存在的key&#xff0c;会把键值对添加到map中&#xff0c;返回null Put给同一个key是会覆盖value的&#xff0c;返回被覆盖的值value Remove根据key删除键值对&#xff0c;返回被删除的值value Map遍历 键找值 …

python语法踩坑 | list的append操作如何从in-place改为out-of-place

背景 博主写python遇到一个问题&#xff0c;需要把对list添加元素改为非原地操作&#xff0c;即不修改原list。 但是由于列表中的元素是字典类型&#xff0c;无法直接用运算符。 于是写出了下面这行代码 query_list message_list.copy().append(one_question) 其中message…

Anaconda安装 (windowsLinux)

文章目录 Anaconda简介设置国内源pip || conda 一、Anaconda &#xff08;Windows系统&#xff09;1.1 下载及安装1.2 虚拟环境创建1.3 在Pycharm中配置conda的环境 二、Anaconda&#xff08;Linux系统&#xff09; Anaconda简介 conda是一个开源的包、环境管理器&#xff0c;可…

探索 Atlassian 云平台:组织、站点、产品架构解析

我们通常访问的是 Atlassian 的某个云站点&#xff0c;比如填空题-中国站点为&#xff1a;cloze-cn.atlassian.net。当我们访问该站点内的具体产品时&#xff0c;只需在该站点的 URL 后添加相应产品的缩写&#xff0c;例如&#xff1a; Confluence: cloze-cn.atlassian.net/wi…

12.电子产品拆解分析-LED T8_16W灯管

12.电子产品拆解分析~LED_T8_16W家用灯管 一、功能介绍二、电路分析以及器件作用一、功能介绍 ①无需使用镇流器(启辉器),只需接到220V即可点亮使用;②节能省电 透光性强 无可视频闪;二、电路分析以及器件作用 220V交流电经过MB10F桥式整流出300V脉动直流电,然后经过6.8u…

某J 集成 cas5.3res api登录

在学习一个开源项目是时集成了cas&#xff0c;但文档过于简单&#xff0c;研究了两天这次记录做个补充 1.下载cas项目 GitHub - apereo/cas-overlay-template at 5.3 2.编译 解压zip&#xff0c;命令行进去&#xff0c;执行mvn clean package 结束之后会出现 target 文件夹&…

Android14 - Framework- Configuration的创建和更新

本文描述从启动一个新进程的Activity起&#xff0c;Framwork层Configuration的创建和传导过程。 首先&#xff0c;我们知道所有的Window容器都继承于WindowContainer&#xff0c;而WindowContainer本身是ConfigurationContainer的子类。于此同时&#xff0c;WindowProcessContr…

[NOIP1998 提高组] 拼数

[NOIP1998 提高组] 拼数 题目描述 设有 n n n 个正整数 a 1 … a n a_1 \dots a_n a1​…an​&#xff0c;将它们联接成一排&#xff0c;相邻数字首尾相接&#xff0c;组成一个最大的整数。 输入格式 第一行有一个整数&#xff0c;表示数字个数 n n n。 第二行有 n n …

【Vue3遇见的问题】创建vue3的项目使用vscode打开后项目的app.vue里面存在爆红

出现的问题 直接上上问题:问题的图片如下: 解决方法 解决效果 补充 因为vetur的插件禁用了 所以需要一个新插件来 这里发现的官网推荐的插件 也就是volar 他两是一样的

各位老板,你需要的工厂数字孪生可视化库在这

各位老板是不是很喜欢下面这种有逼格的大屏,下面介绍一下怎么实现的,保证有所收获。 Cesium是一个开源的WebGL JavaScript库&#xff0c;用于创建高性能的三维地球、地图和虚拟环境。它支持在浏览器中实现高质量的地球模拟&#xff0c;同时提供了丰富的功能特点&#xff0c;使得…