您的位置:首页 > 其它

阶乘之和 & 程序运行时间 & 算法分析

2014-12-09 17:13 309 查看
实例:输入n,计算S = 1! + 2! + 3! + 4! + ... + n!的末六位(不含前导0)。其中 n ≤ 106。

分析:考虑到数据溢出后程序如下:

#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐