题目:计算n!中结尾零的个数(C++实现) 。
当时一看到这道题目便有了思路,很快就写出了代码。我写的代码如下:
- #include <stdio.h>
- /*计算n!结尾零的个数,返回零的个数。*/
- int CalZeroNum(int n)
- {
- int result=1;
- int num=0;
- /*计算n的阶乘,结果保存在result中。*/
- int i;
- for(i=n;i>=1;i--)
- result*=i;
- /*计算result结尾零的个数*/
- while(1)
- {
- if(result%10==0)
- {
- result/=10;
- num++;
- }
- else
- break;
- }
- return num;//返回零的个数。
- }
- void main()
- {
- printf("10!结尾零的个数为:%d/n",CalZeroNum(10));
- }
面试官看了我的代码之后有些不满意,因为这种算法存在很大问题——如果n!很大时(大于32767时),int result就无法保存n!的值,程序就无法正确执行。此外,由于需要进行多次累加和累乘运算,程序的效率也是非常低的。回到宿舍后我参考了网上大牛们的算法,最后总结了几个更好的算法解决这个问题。
算法思想:在1-10两个数相乘要产生0,只有 10×1=2×5,2×5。
200!=200×199×198……×2×1=2×5×2×5×2×199…. ×2×1;可以分解为质数相乘的形式,很明显有2的个数比5的多,所以只要求出200的阶乘可分解出多少个5相乘,就可得到200的阶乘结尾的连续的零的个数.
即:num=[200/5]+[200/5/5]+[200/5/5/5].
注: [x]表示对x取整.
所以可以通过这个思路很容易的得到任意阶乘结尾连续的零,其示例C语言代码如下:
- #include <stdio.h>
- /*计算n!结尾零的个数,返回结尾零的个数。*/
- int CalZeroNum(int n)
- {
- int num=0;//n!结尾零的个数
- int b=1;//5的次方
- while(1)
- {
- b*=5;
- num+=n/b;
- if(b>n)
- break;
- }
- return num;//返回结尾零的个数
- }
- void main()
- {
- printf("20!结尾零的个数为:%d/n",CalZeroNum(20));
- }
-----------------------------------------------分割线-----------------------------------------
上述代码怎么理解呢?他的思路是,给定n,那么先算能被5整除的数字有多少个。方法是计算n/5=a1,比如20/5=4,说明有4个被5整除的数字(5,10,15,20),然后计算n/25,统计一下被25整除的数字有多少个。。方法是n/25=a2,由于他是25啊,有两个5,所以要算两次。。。等等,被25整除的数字肯定被5整除,所以之前计算n/5的时候已经算过一次了。计算公式是N=a1 + a2*2-a2+a3*3-a3-a3...=a1+a2+a3+...就这样啦。给定n,需要循环[log5(n)+1]次,所以算法的复杂度是O(log n)的。
本文介绍了如何用C++计算n!阶乘尾部零的个数,通过分析2和5的因子,提出了一种在O(log n)复杂度内的解决方案,避免了大整数乘法导致的问题,适用于大规模数值的处理。
202

被折叠的 条评论
为什么被折叠?



