算法——阶乘之和(数据溢出以及时间测试)
来源:互联网 发布:自动接听电话软件 编辑:程序博客网 时间:2024/06/18 10:08
阶乘之和
例题:
输入n,计算S = 1!+2!+3!+...+n!的未6位(不含前导0)。n<=10^6,n!表示前n个正整数之积。
样例输入:
10
样例输出:
37913
分析:
这个任务看似不难,实际却有陷阱。先看如下代码:
代码1(有缺陷):
//abc
#include<stdio.h>#include<stdlib.h>int main(){unsigned long int n,S = 0;scanf("%ld",&n);for(int i=1;i<=n;i++){int long factorial = 1;for(int j=1;j <=i;j++)factorial *= j;S += factorial;}printf("%ld\n",S % 1000000);return 0;}#include<stdio.h>#include<time.h>int main(){const int MOD = 1000000;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; }首先是算法改进:对每部阶乘进行求余,随后的结果S再进行求余。先看测试结果:输入20,输出结果:820313; 输入40,输出结果:940313; 输入100,输出:940313;
输入160,输出结果:940313; 输入1600,输出结果:940313..........
可以发现从40开始,答案始终不变。这是因为当25!末尾有6个0,所以从第5项开始,后面的所有项都不会影响和的末6位数字。什么意思呢:1!-10!举例如下:
1~10的阶乘如下:1!=12!=23!=64!=245!=1206!=7207!=50408!=40320 9!=362880 10!=3628800所以有此可见。。。。所以只需要在程序的最前面加一条语句“if(n>25) n=25;”,效率和溢出将都不存在问题。
下面来看程序中其他的东西:这个程序的真正特别之处在于计时函数clock()的使用。该函数返回程序目前为止运行的时间。
这样,在程序结束之前调用此函数,便可获得整个程序的运行时间。这个时间除以常熟CLOCKS_PER_SEC之后得到的值以“秒”为单位。
(
C语言函数
clock
() 功 能:
返回处理器调用某个进程或函数所花费的时间。
用 法:
clock_t
clock
(
void
);
说明:
clock_t
其实就是
long
,即长整形。该函数返回值是硬件滴答数,要换算成秒或者毫秒,需要除以CLK_TCK或者 CLK_TCK
CLOCKS_PER_SEC。比如,在VC++6.0下,这两个量的值都是1000,这表示硬件滴答1000下是1秒,因此要计算一个进程的时间,用
clock
()除以1000即可。
)
输入“20”,按Enter键后,系统瞬间给出了答案820313。但是,输出的Time used居然不是0!其原因在于,键盘输入的时间也被使计算在内——这的确
是程序启动之后才进行的。为了避免输入数据的时间影响测试结果,可以使用一种称为“管道”的小技巧;在windows命令行下执行echo 20|abc,操作系统
会自动把20输入,其中abc是程序名。
测试可知,程序的运行时间和n的平方成正比。(即程序的时间复杂度为O(n^2))).
我们来测试一下这个程序(测试环境为Dev-c++):
当输入10时,输出37913,结果正确。 但当输入100时,输出-961703。
我们知道int最大值为2^31-1,没错,在乘法累加的过程中,整形S的数据溢出。因为我们只求的是阶乘之和
的最后六位,没必要求出阶乘S的结果,所以可以从如下进行改进(过求出S的结果,难度更大)。想要解决这个问题,需要运用这个数学知识:
只要计算包含加法,减法和乘法的整数表达式除以正整数n的余数,可以在每部计算之后对n取余,结果不变。
下面请看改进过后的代码2,还会有新的东西:#include<stdio.h>#include<time.h>int main(){const int MOD = 1000000;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; }首先是算法改进:对每部阶乘进行求余,随后的结果S再进行求余。
先看测试结果:
输入20,输出结果:820313; 输入40,输出结果:940313; 输入100,输出:940313;
输入160,输出结果:940313; 输入1600,输出结果:940313..........
可以发现从40开始,答案始终不变。这是因为当25!末尾有6个0,所以从第5项开始,后面的所有项都不会影响和的末6位数字。
什么意思呢:1!-10!举例如下:
1~10的阶乘如下:
1!=1
2!=2
3!=6
4!=24
5!=120
6!=720
7!=5040
8!=40320
9!=362880
10!=3628800
所以有此可见。。。。所以只需要在程序的最前面加一条语句“if(n>25) n=25;”,效率和溢出将都不存在问题。
下面来看程序中其他的东西:这个程序的真正特别之处在于计时函数clock()的使用。该函数返回程序目前为止运行的时间。
这样,在程序结束之前调用此函数,便可获得整个程序的运行时间。这个时间除以常熟CLOCKS_PER_SEC之后得到的值以“秒”为单位。
(C语言函数clock() 功 能:
返回处理器调用某个进程或函数所花费的时间。
用 法: clock_t clock(void);
说明:clock_t其实就是long,即长整形。该函数返回值是硬件滴答数,要换算成秒或者毫秒,需要除以CLK_TCK或者 CLK_TCK
CLOCKS_PER_SEC。比如,在VC++6.0下,这两个量的值都是1000,这表示硬件滴答1000下是1秒,因此要计算一个进程的时间,用clock()除以1000即可。)
输入“20”,按Enter键后,系统瞬间给出了答案820313。但是,输出的Time used居然不是0!其原因在于,键盘输入的时间也被使计算在内——这的确
是程序启动之后才进行的。为了避免输入数据的时间影响测试结果,可以使用一种称为“管道”的小技巧;在windows命令行下执行echo 20|abc,操作系统
会自动把20输入,其中abc是程序名。
测试可知,程序的运行时间和n的平方成正比。(即程序的时间复杂度为O(n^2))).
0 0
- 算法——阶乘之和(数据溢出以及时间测试)
- 贪心算法——阶乘之和
- 贪心算法之——阶乘之和(nyoj91)
- NYOJ 91 阶乘之和——贪心算法
- 阶乘之和——贪心
- 贪心算法--阶乘之和
- 阶乘之和的正确程序(没有乘法的溢出问题以及效率低下的解决方法)
- 阶乘之和(贪心算法)
- NYOJ91——阶乘之和(贪心)
- 南阳理工91——阶乘之和
- Java基础编程1—阶乘之和
- NYOJ 91 阶乘之和 贪心算法
- NYOJ - 91 - 阶乘之和(贪心算法)
- ACM-算法赛题-NYOJ-阶乘之和
- 贪心算法-nyoj-91-阶乘之和
- NOJ1093阶乘之和——n超过24不变
- 阶乘之和
- 阶乘之和
- openwrt 驱动 hello world
- Java删除ArrayList中的重复元素的2种方法
- Asp.Net Cookie 和 Session 的编写、读取 和 删除
- GiraphV1.2之DiskMessage 运行设置
- Oracle创建数据库
- 算法——阶乘之和(数据溢出以及时间测试)
- 扑克牌洗牌
- iOS应用外搜索之 Core Spotlight 适配
- rman配置及rman常用命令操作
- struts2图片上传报错 findText cleanUpRequest StandardWrapperValve
- Oracle数据库体系结构、启动过程、关闭过程
- Java的synchronized原理
- 计算机构成
- Linked List实现队列的数据存储结构