`
周凡杨
  • 浏览: 230308 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

阶乘算法之一N! 末尾有多少个零

阅读更多

                                                                                题:给定一个整数N,求出N!末尾有多少个零,比如N=10N!=362880010!末尾有两个零。

 

首先温固一下阶乘的相关知识!

阶乘(factorial)是基斯顿·卡曼(Christian Kramp, 1760 – 1826)于1808年发明的运算符号。阶乘,也是数学里的一种术语。
任何大于1的自然数n阶乘表示方法:n!=1×2×3×……×n 或n!= n×(n-1)! 
0!=1,注意(0的阶乘是存在的).
双阶乘:
    当n为奇数时表示不大于n的所有奇数的乘积 如:7!!=1×3×5×7
     当n为偶数时表示不大于n的所有偶数的乘积(除0外) 如:8!!=2×4×6×8
     小于0的整数-n的阶乘表示:
   (-n)!= 1 / (n+1)!
   双阶乘是怎么提出来的,是根据阶乘推导出来的吗?这点百思不解?
 
然后理一下本题的解题思路:

      N个自然数相乘,结尾0的个数,依赖有多少个10相乘(有两个10相乘,结尾0的个数就为2),10=2×5,则可以理解为结尾0的个数依赖因子中2的个数和5的个数,而对于连续的自然数来说,2出现的频率比5高的多,所以最终只需要计算出因子中5的个数,即为答案。

 

   把想法整理为JAVA代码,如下所示:

 解题思路一:分析阶乘因子中的每一个数,计算其包含5的个数,最后求总和

   

private int zeroNum(int n){
      int ret = 0;
      for(int i=1;i<=n;i++){
        int j=i;
        while(j%5 == 0){
           ret++;
           j/=5;
        }
      }
      return ret;
}

 

    时间复杂度为:Nlog5N     

 

解题思路二:

f(x)表示正整数x末尾所含有的“0”的个数,则有: 
      
0 < n < 5时,f(n!) = 0; 
      
n >= 5时,f(n!) = k + f(k!), 其中 k = n / 5(取整)

 

     下面对这个结论进行证明: 
    (1)
n < 5结论显然成立。 
    (2)
n >= 5时,令n5k * 5(k-1) * ... * 10 * 5 * a,其中 n = 5k + r (0 <= r <= 4)a是一个不含因子“5”的整数。 
    
对于序列5k, 5(k-1), ..., 10, 5中每一个数5i(1 <= i <= k),都含有因子“5”,并且在区间 [5(i-1), 5i] (1 <= i <= k)内存在偶数,也就是说,a中存在一个因子“2”5i相对应。即,这里的k个因子“5” n!末尾的k“0”一一对应。 

n5k * 5(k-1) * ... * 10 * 5 * a = (5k * k!) *a

 

f(x)表示正整数x末尾所含有的“0”的个数, g(x)表示正整数x的因式分解中因子“5”的个数,则利用上面的的结论有: 

 f(n!) = g(n!) = g(5k * k!* a) =g(5k * k!)= k + g(k!) = k + f(k!) 
所以,最终的计算公式为: 
    
0 < n < 5时,f(n!) = 0; 
    
n >= 5时,f(n!) = k + f(k!), 其中 k = n / 5(取整)。 

 

 

public int method02(int n){
      int ret = 0;
      if(n<5){
        return ret;
      }
      int k = n/5;
      return k + method02(k);
}

  时间复杂度为:log5

 

 解题思路三:

    乘积末尾的0的个数依赖于因子中的2的个数和5的个数。对于阶乘来说,每2个数字就至少有一个2的因子,所以2的因子是足够的。5的因子相对少些,至少连5个数才能保证一定出现一个。注意,这里连续5个书保证出现一个5的因子是指最少的情况。比如12345,这就只会出现一个。但是考虑 212223242525 = 5 * 5,所以如果乘以25那就能得到25的因子。依次会有35的因子

    所以n!5的个数的计算是:[n/5]+[n/(5*5)]+[n/(5*5*5)]+....

 

public int method03(int n){
       int ret = 0;
       int baseNum = 5;
       while (n >= baseNum)
       {
           ret += n/baseNum;
           baseNum *= 5;
       }
       return ret;
}
时间复杂度为:log5

 

       对于解法二和解法三,我这里写的时间复杂度都为log5N  ,但实际验证,解法三比解法二更高效,所以不知道是否时间复杂度写的有问题?高人求解!!!

 

后记(顿悟):

    算法三之所以优于算法二,因为算法二是用到递归算法,递归一系列的函数调用,而函数的调用开销是很大的,系统要为每次函数调用分配存储空间,并将调用点压栈予以记录。而在函数调用结束后,还要释放空间,弹栈恢复断点。所以说,函数调用不仅浪费空间,还浪费时间。所以算法三在时间复杂度和空间复杂度上是优于算法二的。

                                                                    2013.6.20

 

参考资料:

  http://blog.csdn.net/chn_cf/article/details/6541281

  http://www.chinaunix.net/old_jh/23/926848.html

  http://www.pureweber.com/article/recursive-power-4/

 《编程之美》

  

1
4
分享到:
评论
2 楼 cherami 2017-05-15  
解法3有问题,在n很大的时候会导致baseNum溢出使得结果不对
需要把baseNum的类型改成long型
例如n的值是1808548330时,算出来是452137079,但是正确答案是452137077
1 楼 yygymmmmsee 2013-06-09  
第二和第三两个算法本质应该是一致的吧

只不过第二个使用了递归,第三个是一个循环

这也有可能是造成第二个效率比第三个低的原因

相关推荐

    n的阶乘末尾有多少个0_n的阶乘末尾的0_

    题解:求n的阶乘末尾0的数量。因为n的阶乘容易爆整数范围,所以普通算法不合适。用高精度容易超时,这里直接给出数学求解过程

    C++版本计算n阶乘末尾0的个数原理讲解及代码实现

    C++版本计算n阶乘末尾0的个数原理讲解及代码实现

    50个优秀经典PHP算法大集合

    ├──Package │ ├── Sort 排序篇 ...│ ├── Kmp.php 算法导论-KMP算法 │ ├── DijkstraQuery.php 迪克斯特拉算法 ...│ └── Factorial.php 面试题之N的阶乘末尾有多少个0

    刷题遇到的一些题目(Java)——持续更新

    (即阶乘)末尾有多少个0? 比如: n = 10; n! = 3628800,所以答案为2 原题链接:https://www.nowcoder.com/questionTerminal/6ffdd7e4197c403e88c6a8aa3e7a332a 算法思想:最简单的就是分解质因数 n! = n * (n-1) *...

    leetcode中国-Data_Structures-Algorithms:待补充!!

    编写一个程序,将数组循环旋转一个。 找到最大和连续子数组 [V. 输入法] 最小化高度之间的最大差异 [V.IMP] 最低数量到达数组末尾的跳转次数 在 N+1 整数数组中查找重复项 在不使用额外空间的情况下合并 2 个已排序...

    C语言程序设计标准教程

    用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。  可以说C程序的全部工作都是由各式各样的函数完成的, 所以也把C语言称为函数式语言。 由于采用了函数模块式的结构, C语言...

    leetcode中国-450-DSA:450道DSA练习题

    编写一个程序,将数组循环旋转一个。 数组查找最大和连续子数组 [V. 输入法] 数组最小化高度之间的最大差异 [V.IMP] 阵列最小数量到达数组末尾的跳转次数 数组在 N+1 整数数组中查找重复项 Array 合并 2 个已排序的...

    javascript入门笔记

    1、声明一个变量 r ,来表示一个圆的半径,并赋值 2、声明一个常量PI ,来表示圆周率3.14 3、通过 r 和 PI 来计算 该圆的周长,保存在变量l中 周长 = 2 * π * 半径 4、通过 r 和 PI 来计算 该圆的面积,保存在...

    leetcode2sumc-ds-problem:ds问题

    第一个重复字符 3- 删除重复项 4- 找到重复的 5-树深度优先搜索 6- 最大子阵列 力码: 1 - 滑动窗口问题 2 - 二分搜索 3 - 将 +1 添加到数组 4 - 将零移到末尾,同时保持非零的顺序 5 - 递归: 1 - 给定数的阶乘:...

    delphi 开发经验技巧宝典源码

    0237 如何实现一个应用程序只能打开一个进程 158 7.4 其他数据处理技术 159 0238 对计算结果四舍五入 159 0239 获取一个字符的ASCII值 159 0240 判断字符串中是否有文字符 160 0241 如何从字符串中提取...

    delphi 开发经验技巧宝典源码06

    0237 如何实现一个应用程序只能打开一个进程 158 7.4 其他数据处理技术 159 0238 对计算结果四舍五入 159 0239 获取一个字符的ASCII值 159 0240 判断字符串中是否有文字符 160 0241 如何从字符串中提取...

Global site tag (gtag.js) - Google Analytics