阶乘之和 & 程序运行时间 & 算法分析
2014-12-09 17:13
309 查看
实例:输入n,计算S = 1! + 2! + 3! + 4! + ... + n!的末六位(不含前导0)。其中 n ≤ 106。
分析:考虑到数据溢出后程序如下:
作者源程序如下:
值得借鉴的是该程序可以计时,使用time.h库中的函数clock()函数返回程序目前为止运行时间,该时间除以常数CLOCKS_PER_SEC得到的值以"秒"为单位。其中常数CLOCKS_PER_SEC和操作系统相关。因此不要直接使用clock()的返回值。据此,我们在程序结束之前调用该函数,就可以得到整个程序的运行时间。
一个问题是我们通过命令行运行程序的时候键盘输入时间也被计算在内。我们利用管道来避免键盘输入时间对测试结果的干扰。Windows系统中,在编译好的程序所在目录下执行命令行命令 echo 20 | FactSum,其中20是要输入的数据,FactSum是程序名。这样操作系统会自动帮我们把20输入程序,因此可以避免键盘输入时间。
关于算法的运行时间分析我在这里做了一些详细得论述,因此我们可以直接得出结论:该算法的运行时间为O(N2),我写的第一个程序是线性时间O(N)。
下面我们来检验一下分析是否正确,一般有两种常用方法可以采用:
一种是编程并比较实际观察到的运行时间与通过分析所描述的运行时间是否相匹配。当N扩大一倍时,线性程序的运行时间乘以因子2,二次程序的运行时间乘以因子4等等。
验证一个程序是否是O(f(N))的另一个常用的技巧是对N的某个范围(通常用2的倍数隔开),计算比值 T(N) / f(N),其中T(N)是实际运行时间。如果f(N)是运行时间的理想近似,那么算出的值收敛于一个正常数。如果f(N)估计过大,则算出的值收敛于零。如果f(N)估计过低,那么算出的值发散。
最后多次测试,根据运行时间表可以得到程序的运行时间,与我们的分析比较即可,这里涉及作图,并且不同配置的机器实际运行时间都有差别,因此运行时间表就在此省略了。另外在本机上多次测试50000得到的平均运行时间是运行时间是17.60,由此我们可以大胆预测,输入为n = 10^6时,程序需要运行17.60*(10^6/ 50000)^2秒,约为1.96个小时程序才能跑完(另:第一个线性时间的程序几乎是瞬间给出了答案)。那么如何解决这个问题呢,一个就是直接用第一个程序,另一个就需要一点观察力了,我们做出一张输出结果表,发现从n=25开始,结果都不变,为940313。我们可以写一个程序求出25!发现其末尾有6个零,所以从第25项开始,后面所有的项都不影响和的末6位数字,因此在该程序的最前面加上一条语句:if(n > 25) n = 25;这样效率和溢出都不成问题了。
分析:考虑到数据溢出后程序如下:
#include <stdio.h> int main(void) { int n, i; int sum = 1; int factorial = 1; scanf("%d", &n); for(i = 2; i <= n; i++) { factorial = (factorial * i) % 1000000; sum = (sum + factorial) % 1000000; } printf("%d\n", sum); return 0; }
作者源程序如下:
#include <stdio.h> #include <time.h> int main(void) { const int MOD = 1000000; int i, j, n, S = 0; scanf("%d", &n); for(i = 1; i<= n; i++) { int factorial = 1; for(j = 1; j <= i; j++) factorial = (factorial * j % MOD); S = (S + factorial) % MOD; } printf("%d\n", S % 1000000); printf("Time used = %.2lf\n", (double)clock() / CLOCKS_PER_SEC); return 0; }
值得借鉴的是该程序可以计时,使用time.h库中的函数clock()函数返回程序目前为止运行时间,该时间除以常数CLOCKS_PER_SEC得到的值以"秒"为单位。其中常数CLOCKS_PER_SEC和操作系统相关。因此不要直接使用clock()的返回值。据此,我们在程序结束之前调用该函数,就可以得到整个程序的运行时间。
一个问题是我们通过命令行运行程序的时候键盘输入时间也被计算在内。我们利用管道来避免键盘输入时间对测试结果的干扰。Windows系统中,在编译好的程序所在目录下执行命令行命令 echo 20 | FactSum,其中20是要输入的数据,FactSum是程序名。这样操作系统会自动帮我们把20输入程序,因此可以避免键盘输入时间。
关于算法的运行时间分析我在这里做了一些详细得论述,因此我们可以直接得出结论:该算法的运行时间为O(N2),我写的第一个程序是线性时间O(N)。
下面我们来检验一下分析是否正确,一般有两种常用方法可以采用:
一种是编程并比较实际观察到的运行时间与通过分析所描述的运行时间是否相匹配。当N扩大一倍时,线性程序的运行时间乘以因子2,二次程序的运行时间乘以因子4等等。
验证一个程序是否是O(f(N))的另一个常用的技巧是对N的某个范围(通常用2的倍数隔开),计算比值 T(N) / f(N),其中T(N)是实际运行时间。如果f(N)是运行时间的理想近似,那么算出的值收敛于一个正常数。如果f(N)估计过大,则算出的值收敛于零。如果f(N)估计过低,那么算出的值发散。
最后多次测试,根据运行时间表可以得到程序的运行时间,与我们的分析比较即可,这里涉及作图,并且不同配置的机器实际运行时间都有差别,因此运行时间表就在此省略了。另外在本机上多次测试50000得到的平均运行时间是运行时间是17.60,由此我们可以大胆预测,输入为n = 10^6时,程序需要运行17.60*(10^6/ 50000)^2秒,约为1.96个小时程序才能跑完(另:第一个线性时间的程序几乎是瞬间给出了答案)。那么如何解决这个问题呢,一个就是直接用第一个程序,另一个就需要一点观察力了,我们做出一张输出结果表,发现从n=25开始,结果都不变,为940313。我们可以写一个程序求出25!发现其末尾有6个零,所以从第25项开始,后面所有的项都不影响和的末6位数字,因此在该程序的最前面加上一条语句:if(n > 25) n = 25;这样效率和溢出都不成问题了。
All Rights Reserved. Author:海峰:) Copyright © xp_jiang. 转载请标明出处:/article/5264975.html
参考资料:
《算法竞赛入门经典》——刘汝佳
《数据结构与算法分析:C语言描述_原书第二版》——Mark Allen Weiss
相关文章推荐
- WV.25-大数阶乘算法5-程序运行时间的测量
- 一段评价程序平均运行时间的shell脚本&shell脚本编写注意事项
- 删除"监视程序运行时间"软件
- 长度为n的整数数组,找出其中任意(n-1)个乘积最大的那一组,只能用乘法,不可 以用除法。要求对算法的时间复杂度和空间复杂度作出分析,可以写思路也可以写程序。
- matlab中,计算,记录,程序运行,起始,结束 时间,间隔 &matlab中 tic,toc函数的用法
- PAT B 1026. 程序运行时间(15)
- 一个c++中计算算法运行时间的程序
- 阶乘之计算从入门到精通-程序运行时间的测量
- <C++>计算程序运行时间
- 《编程之美》之:"程序理解和时间分析"与JOJ 2042
- 数据结构与算法分析 2.17 给出有效的算法(及其运行时间分析)
- 赵老师的强悍 求取程序运行时间算法
- Collection -> .Net集合类型大全,只要灵活运用可以节省很多开发时间和提高程序运行效率
- 一串首尾相连的珠子(m 个),有N 种颜色(N<=10),设计一个算法,取出其中一段,要求包含所有N 中颜色,并使长度最短。并分析时间复杂度与空间复杂度。
- 获取程序运行时间以判别算法优劣
- Collection -&gt; .Net集合类型大全,只要灵活运用可以节省很多开发时间和提高程序运行效率
- 设M 是一个m×n 的矩阵,其中每行的元素从左到右单增有序,每列的元素从上到下单增有序。 给出一个分治算法计算出给定元素x 在M 中的位置或者表明x 不在M 中。分析算法的时间复杂性。
- Collection -> .Net集合类型大全,只要灵活运用可以节省很多开发时间和提高程序运行效率
- 《编程之美》之:"程序理解和时间分析"与JOJ 2042
- 分析算法运行时间的一些通用规则