面试题:计算 n!阶乘的结果的末尾有几个0

前言

首先基于一个事实:咱们不可能真的把 n! 的结果计算出来,再去数结果的末尾有几个0;n 很小还好,若是n很大,甚至趋近于无穷大,咱们是不可能这样作的。缘由主要有二:java

  1. 通常计算机的计算能力和存储能力也有限,是计算不出那么大的数的。
  2. 即便计算机能算出来,这样作也很耗时,可能要算好久。

连计算机都算不出来,那咱们怎么办呢?别慌,虽然咱们不能直接算出结果,但咱们能够把问题一步步拆解。算法

拆解思路

首先,咱们想什么状况下会产生一个0?code

诶,一个数乘以 10,在末尾就会多出一个 0。而 10 = 5 * 2。class

一组数相乘的结果末尾有几个0,取决于这组数因式分解后有几对 5 和 2 的因子。效率

针对于 n! 这个题目,有这样一个事实:把相乘的数因式分解后,2 的个数确定大于 5 的个数。循环

因此,这个问题能够拆解为:只要求出因式分解后有几个 5 的因子便可,5的个数便是末尾出现的0的个数。计算机

解法一:直接法

这种解法的思路是:直接将 n! 中的每一个数,按照 5 来因式分解,最后把出现的 5 的个数加起来。时间

public int calculateZeroInFactorial(int n) {
  int count = 0;
  // 循环判断全部的乘数
  for (int i = n; i > 0; i++) {
    if (i % 5 == 0) {
      // 若是这个乘数能够对 5 进行因式分解,再看这个乘数能够分解出几个5
      int a = i;
      while(a % 5 == 0) {
        a = a / 5;
        count++;
      }
    }
  }
  return count;
}

可是这种算法的时间复杂度为 O(nlog(n)),那有没有更快的算法呢?while

解法二: log(n) 解法

分析:co

  1. n! 这些乘数中,每隔 5 个数,确定会有一个数至少能拆出一个 5 因子。因此 n / 5 = 至少会出现的 5 的个数。
  2. 上面说至少,由于 n / 5 并不能彻底算出 5 因子的个数,好比若某个数 25 = 5 * 5,分解后获得的 5 也算一个,因此能被 25 因式分解至关于会出现 2 个 5 因子,而第一步中除以 5 算个数的时候已经算了一个了,因此至关于比以前会多一个 5 因子。
  3. 依此类推,能被 25 5 = 125 因式分解的至关于比以前按 25 因式分解的时候又多出一个 5 因子。能被 125 5 = 625 因式分解的至关于比按 125 因式分解时又多出一个 5 因子。还有 625 * 5 …...

因此,n! 的结果能够拆分为多少个 5 因子呢?

n/5 + n/25 + n /125 + n/625 + ….

好比 128!的阶乘的结果末尾有几个0呢?

128/5 +128/25 + 128/125 = 25+5+1 = 31 个

又如:1247! 的阶乘的结果末尾有几个0呢?

1247/5 + 1247/25 + 1247/125 + 1247/625 = 249+49+9+1 = 308 个

public int calculateZeroInLogN(int n) {
  int count = 0;
  while (n > 0) {
    count += n / 5;
    n /= 5;
  }
  return count;
}

这种算法的时间复杂度为 O(log(n)),效率会高不少,并且仅需几行代码。

相关文章
相关标签/搜索