面試題:計算 n!階乘的結果的末尾有幾個0

前言

首先基於一個事實:我們不可能真的把 n! 的結果計算出來,再去數結果的末尾有幾個0;n 很小還好,如果n很大,甚至趨近於無窮大,我們是不可能這樣做的。原因主要有二:

  1. 一般計算機的計算能力和存儲能力也有限,是計算不出那麼大的數的。
  2. 即使計算機能算出來,這樣做也很耗時,可能要算很久。

連計算機都算不出來,那我們怎麼辦呢?別慌,雖然我們不能直接算出結果,但我們可以把問題一步步拆解。

拆解思路

首先,我們想什麼情況下會產生一個0?

誒,一個數乘以 10,在末尾就會多出一個 0。而 10 = 5 * 2。

一組數相乘的結果末尾有幾個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)),那有沒有更快的算法呢?

解法二: log(n) 解法

分析:

  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)),效率會高很多,而且僅需幾行代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章